diff --git a/docs/README.md b/docs/README.md index 792bef32..926d4847 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,6 +34,7 @@ | [API_INTEGRATION.md](API_INTEGRATION.md) | 后端 API 对接顺序、数字师傅 Agent、正式试点 CSV、移动端训练/复盘、候选资料和 SOP 接口 | | [superpowers/specs/2026-07-24-digital-master-agent-design.md](superpowers/specs/2026-07-24-digital-master-agent-design.md) | “问”模块最终 Agent 产品/架构边界,以及当前已实现与后续工具范围 | | [superpowers/plans/2026-07-24-digital-master-agent.md](superpowers/plans/2026-07-24-digital-master-agent.md) | Agent 实施、自动化/H5/浏览器验证记录和剩余专项生产/真机验收边界 | +| [superpowers/plans/2026-07-31-mobile-citation-source-viewer.md](superpowers/plans/2026-07-31-mobile-citation-source-viewer.md) | 手机端回答引用点击全文、图片和视频定位的分阶段实施计划;每阶段必须记录自动化、权限、Range、性能、生产和真机实际结果 | | [P0_PILOT_ACCEPTANCE_CHECKLIST.md](P0_PILOT_ACCEPTANCE_CHECKLIST.md) | 正式试点执行模板:按当前批次填写角色、窗口与证据;不在仓库保存个人身份、验证码或旧窗口结论 | | [20260718/问师傅多轮会话与原始资料交付设计.md](20260718/问师傅多轮会话与原始资料交付设计.md) | Agent 底层复用的知识短会话、指代改写、意图路由和受控原文件/视频交付契约 | | [KNOWLEDGE_PLATFORM_RUNBOOK.md](KNOWLEDGE_PLATFORM_RUNBOOK.md) | 银城/美途知识空间初始化、授权、令牌、内容迁移、监控验证和安全回滚手册 | diff --git a/docs/superpowers/plans/2026-07-31-mobile-citation-source-viewer.md b/docs/superpowers/plans/2026-07-31-mobile-citation-source-viewer.md new file mode 100644 index 00000000..5c6ed634 --- /dev/null +++ b/docs/superpowers/plans/2026-07-31-mobile-citation-source-viewer.md @@ -0,0 +1,700 @@ +# 手机端问答引用详情与原始媒体定位 Implementation Plan + +> 制定日期:2026-07-31 +> +> 当前状态:阶段 0 基线冻结已完成;阶段 1—6 待实施 +> +> 适用范围:`mobile-uni` 员工端“问 · 数字师傅”、`/api/aihr/agent/**`、知识检索与受控原始资料接口 + +**Goal:** 让员工在手机端点击回答引用后,按当前身份重新鉴权并打开可信来源详情:文本进入全文页并高亮命中位置,图片打开原图,视频进入受控播放器并定位到引用时间或已保存关键帧;来源撤回、过期或失权时失败关闭。 + +**Architecture:** 保留 `aihr_knowledge_fragment` 为检索正文事实源,新增一对一的片段来源定位表保存附件、媒体类型和页码/段落/时间戳等结构化位置。回答只返回不透明 `detailRef` 和最小展示信息;点击后由服务端重新计算“租户 + 调用应用绑定 + 当前主体授权”,再返回受控文本、图片或视频播放信息。原始 OSS 地址、登录令牌和完整提取正文不进入回答响应。 + +**Tech Stack:** Java 17、Spring Boot、Sa-Token、JdbcTemplate、MySQL 8、MinIO/sys_oss、ffmpeg/ffprobe、Tika/现有文档解析器、uni-app Vue 3 + TypeScript、Node test、JUnit 5/Mockito、Android App-Plus。 + +--- + +## 0. 完成定义与证据纪律 + +### 0.1 阶段状态 + +每个阶段只能使用以下状态: + +- `未开始`:尚未写代码或迁移。 +- `实现中`:允许局部测试通过,但尚未形成该阶段的完整证据。 +- `技术通过`:本地自动化和真实运行链路均通过,但尚未部署生产。 +- `生产通过`:所需发布单元已部署,正式账号或真机完成该阶段规定的验证。 +- `阻断`:失败项、复现步骤和责任边界已记录;不得用后续阶段结果覆盖。 + +“源码存在”“单元测试通过”“H5 能加载”“视频能播放”都不能单独视为阶段完成。 + +### 0.2 每阶段必须保存的实际结果 + +每个阶段结束时必须填写本计划中的“实际结果记录”,至少包含: + +| 字段 | 要求 | +|---|---| +| 构建源 | 完整 Git commit,不使用模糊分支名 | +| 环境 | 本地/测试/生产、后端与 schema 版本、APK 版本 | +| 命令或操作 | 可重复执行的命令或真机点击路径 | +| 预期 | 明确数值、HTTP 状态、页面状态或时间误差 | +| 实际 | 真实通过数、失败数、耗时或错误码,不写“基本正常” | +| 证据 | 脱敏日志、JUnit/Node 输出、HTTP 头、截图或真机录像路径 | +| 遗留项 | 未覆盖的文件类型、机型、角色、数据或性能边界 | + +执行证据统一写入: + +```text +output/citation-source-viewer// +``` + +仓库文档只保存脱敏摘要和证据相对路径,不保存手机号、验证码、令牌、短期下载票据、原始 OSS 地址或受保护正文。 + +### 0.3 固定验收样本 + +本地工程样本使用可公开的合成内容,至少包含: + +1. 一份 TXT/Markdown,带唯一标记和已知行号; +2. 一份三页 PDF,目标引用位于第 2 页; +3. 一份 DOCX,目标引用位于指定段落; +4. 一份 PPTX,目标引用位于指定页; +5. 一张带唯一视觉文字的图片; +6. 一段不超过两分钟的视频: + - 已知语句位于固定时间范围; + - 已知画面位于固定时间点; + - 含音轨和画面两种可命中依据。 + +生产验收只选已批准、当前员工有权访问的正式资料;测试问题和答案正文不进入仓库。 + +--- + +## 1. 范围与非目标 + +### 1.1 本计划交付 + +- 回答中的 `DOCUMENT` 引用卡片可点击; +- 文本全文页展示同一受控附件的解析正文,并定位、高亮命中片段; +- PDF/PPT/DOCX 在解析能力允许时展示页码、幻灯片号或段落位置; +- 图片引用打开当前员工仍有权访问的原图; +- 视频引用打开独立播放器,定位到语句时间范围或关键帧时间; +- 大视频使用支持 HTTP Range 的受控播放链路; +- 点击时重新鉴权,撤权、跨租户、来源失效和过期票据均失败关闭; +- 新资料在解析时产生定位元数据,历史资料有可审计的回填与降级策略; +- H5、App-Plus、Android 真机分别形成实际验证结果。 + +### 1.2 本计划不交付 + +- 不把完整原文直接塞进 Agent 回答或会话审计; +- 不暴露 MinIO/OSS 原始地址或永久公开链接; +- 不允许客户端提交 `attachmentId`、租户、岗位或空间代码来扩大权限; +- 不把 AI 推断的位置当作原文件页码或视频真实时间点; +- 不承诺所有旧资料自动获得精确页码/帧;无法可靠回填时降级为“打开全文/原文件”; +- 不在本计划重做 RAG、向量模型或 Agent 意图规划; +- 不把 DCloud 测试签名 APK 验证写成企业签名或应用商店验收。 + +--- + +## 2. 实施总览 + +| 阶段 | 交付物 | 完成门禁 | 当前状态 | +|---|---|---|---| +| 0. 基线冻结 | 当前引用、资源、视频解析和权限链路事实 | 源码位置与缺口逐项复核 | 已完成 | +| 1. 定位数据契约 | schema、结构化解析结果、写入和替换一致性 | 迁移幂等;六类样本定位记录正确 | 未开始 | +| 2. 受控详情与媒体接口 | `detailRef`、详情解析、全文、图片和 Range 视频 | 正授权成功;跨租户/撤权/伪造全部拒绝 | 未开始 | +| 3. 手机端引用详情 | 可点击引用、全文高亮、图片预览、视频定位 | 390×844 主路径实际点击;H5 与 App 测试通过 | 未开始 | +| 4. 历史资料回填 | 安全回填、失败降级、进度报告 | 不改正式来源审批;成功/失败/降级数量可核对 | 未开始 | +| 5. 集成、安全与性能 | 全链路矩阵、并发/Range/资源释放 | 零越权;定位误差和性能达到冻结阈值 | 未开始 | +| 6. 发布与真机收口 | schema、后端、H5/Android 发布与生产验证 | 独立授权发布;正式账号和签名 APK 真机证据 | 未开始 | + +依赖顺序: + +```text +阶段 0 + → 阶段 1 数据契约 + → 阶段 2 受控接口 + → 阶段 3 手机端交互 + → 阶段 4 历史回填 + → 阶段 5 全链路门禁 + → 阶段 6 发布与真机 +``` + +阶段 3 可以在阶段 2 的固定测试响应上并行开发,但不能在阶段 2 的权限和 Range 接口完成前标记技术通过。 + +--- + +## 3. 阶段 0:当前基线冻结 + +**目标:** 冻结真实缺口,防止实施时把“引用已展示”“资源卡能打开”误认为引用跳转已经存在。 + +### 已核实事实 + +| 核验项 | 实际结果 | 结论 | +|---|---|---| +| 工作区 | `main...origin/main`,核验时无未提交文件 | 可建立独立实施计划 | +| 回答引用 | `snippets` 可展开,显示标题、摘要、空间和 `fragmentId`;引用卡没有点击事件 | 需要新增点击详情 | +| 资源卡 | `resources` 中 FILE 可下载打开、VIDEO 可加载内联播放器 | 可复用受控读取能力,但未与每条引用绑定 | +| 引用契约 | `Citation` 无 `attachmentId/mediaType/locator/detailRef` | 后端需要新增最小详情引用 | +| 视频定位 | ASR 每 5 分钟片段写入一个 `[mm:ss]`;关键帧间隔至少 30 秒且最多抽取 40 帧,并写入 `[画面 mm:ss]` | 只能得到粗定位文本 | +| 关键帧文件 | OCR 完成后临时 JPG 被删除 | 无法直接展示历史关键帧图 | +| 片段 schema | 只有 `idx/doc_id/content/embedding`,没有来源定位字段 | 需新增旁路定位表 | +| 原文件权限 | `/api/knowledge/resources/{attachmentId}/content` 每次重新鉴权 | 继续复用,不改成永久 URL | + +### 阶段 0 实际验证结果 + +- 状态:`通过` +- 日期:`2026-07-31` +- 方法:源码只读审计、`rg` 检索、工作区状态核验 +- 结果:8 项事实全部定位到当前源码或 SQL;没有执行数据库写入、部署或生产操作 +- 未覆盖:当前生产附件的真实 MIME 分布、OSS Range 行为、历史视频定位可回填比例 + +--- + +## 4. 阶段 1:片段来源定位数据契约 + +**目标:** 让每个可点击引用都能确定性关联到受控附件及其真实位置,不依赖前端解析片段文字。 + +### Files + +- Create: `backend/script/sql/update/aihr_20260731_knowledge_fragment_locator_mysql8.sql` +- Modify: `backend/script/sql/aihr_knowledge_mysql8.sql` +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/domain/AihrKnowledgeSourceLocator.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrVideoService.java` +- Modify: relevant document parser implementations +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/knowledge/AihrKnowledgeSourceLocatorSchemaTest.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/knowledge/AihrKnowledgeSourceLocatorIngestionTest.java` + +### Task 1.1:建立旁路定位表 + +新增 `aihr_knowledge_fragment_locator`,最小字段: + +```sql +tenant_id +fragment_id +knowledge_id +doc_id +attachment_id +source_kind +page_number +slide_number +paragraph_start +paragraph_end +sheet_name +row_start +row_end +start_ms +end_ms +frame_ms +locator_version +create_time +update_time +``` + +约束: + +- `UNIQUE(tenant_id, fragment_id)`; +- 查询索引覆盖 `(tenant_id, attachment_id, source_kind)`; +- `source_kind` 只允许 `TEXT/PDF/DOCX/PPTX/XLSX/IMAGE/VIDEO`; +- 时间、页码和段落不能为负数; +- 定位表缺失或无定位行时,查询仍可返回普通引用,不影响现有问答; +- 生产请求不得自动执行 DDL。 + +### Task 1.2:解析结果从纯字符串升级为结构化段 + +内部解析结果统一为: + +```java +record LocatedSegment( + String text, + SourceKind sourceKind, + Integer pageNumber, + Integer slideNumber, + Integer paragraphStart, + Integer paragraphEnd, + String sheetName, + Integer rowStart, + Integer rowEnd, + Long startMs, + Long endMs, + Long frameMs +) {} +``` + +最低定位能力: + +- TXT/Markdown:行或段落范围; +- PDF:页码; +- DOCX:段落范围; +- PPTX:幻灯片号; +- XLSX:工作表与行范围; +- IMAGE:关联原图,首版不要求 OCR 框坐标; +- VIDEO ASR:语句或分段开始/结束毫秒; +- VIDEO 画面:抽帧的真实毫秒位置。 + +### Task 1.3:切片、替换和重试一致性 + +- 片段与定位行在同一受控处理流程写入; +- 同名文档替换时同步清理旧定位行; +- 异步重试不得产生孤立定位行; +- 零片段媒体仍保持失败,不创建虚假定位; +- Qdrant payload 不存原始地址;如需要,可只增加 `fragmentId/sourceKind`。 + +### 阶段 1 验证 + +运行: + +```powershell +mvn -f backend/pom.xml -pl ruoyi-modules/ruoyi-aihr -am ` + -DskipTests=false -Dsurefire.failIfNoSpecifiedTests=false ` + -Dtest=AihrKnowledgeSourceLocatorSchemaTest,AihrKnowledgeSourceLocatorIngestionTest test +``` + +在临时数据库上执行正式迁移两次,并导入六类固定样本。必须实际证明: + +1. 迁移连续执行两次均成功; +2. 六类样本均有 `fragment + locator`; +3. PDF 第 2 页、PPT 指定页、图片附件、视频语句和画面时间均与样本基准一致; +4. 文档替换后旧 locator 为 0; +5. 解析失败后 locator 为 0; +6. 未执行运行时 DDL。 + +### 阶段 1 实际结果记录 + +| 字段 | 实际值 | +|---|---| +| 状态 | 待填写 | +| commit | 待填写 | +| JUnit | 待填写,例如 `18/18` | +| 迁移幂等 | 待填写 | +| 六类样本定位 | 待填写 | +| 替换/失败清理 | 待填写 | +| 证据目录 | 待填写 | +| 遗留项 | 待填写 | + +--- + +## 5. 阶段 2:受控引用详情、全文、图片与视频接口 + +**目标:** 客户端只持有不透明引用,服务端在点击时重新鉴权并返回最小必要内容。 + +### Files + +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/domain/AihrKnowledgeQueryDto.java` +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/service/AihrKnowledgeCitationDetailService.java` +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/controller/AihrKnowledgeCitationController.java` +- Modify: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/service/AihrKnowledgeResourceDownloadService.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/knowledge/AihrKnowledgeCitationDetailServiceTest.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/knowledge/AihrKnowledgeCitationControllerTest.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/knowledge/AihrKnowledgeResourceRangeTest.java` + +### Task 2.1:扩展回答引用 + +兼容新增字段: + +```json +{ + "fragmentId": 12345, + "title": "投诉处理培训", + "snippet": "……", + "mediaType": "VIDEO", + "detailRef": "opaque-short-lived-reference", + "locatorSummary": { + "pageNumber": null, + "startMs": 208000, + "endMs": 223000, + "frameMs": null + } +} +``` + +规则: + +- 旧客户端忽略新增字段仍可工作; +- `detailRef` 不编码明文租户、附件、用户或 OSS 地址; +- 正式政策引用仍必须来自有效的 `FORMAL_POLICY + APPROVED` 来源; +- `DATA_TOOL`、个人记录和项目记录分别走现有领域详情,不伪装成企业文档。 + +### Task 2.2:详情解析接口 + +新增: + +```text +GET /api/knowledge/citations/{detailRef} +``` + +返回三类详情: + +- `TEXT`:标题、位置、当前片段、分页全文读取入口、原文件入口; +- `IMAGE`:标题、短期原图读取入口; +- `VIDEO`:标题、`startMs/endMs/frameMs`、短期流媒体入口。 + +点击时必须重新验证: + +1. 当前 APP 登录身份; +2. 当前租户; +3. 调用应用与知识空间绑定; +4. 当前主体授权; +5. 附件仍为 `READY`、知识空间仍为 `ACTIVE`; +6. 正式来源仍有效且版本、哈希未漂移; +7. `fragmentId/docId/attachmentId` 仍属于同一来源。 + +### Task 2.3:全文和媒体读取 + +- 全文按页或游标读取,单次响应有硬上限; +- HTML 展示只生成受控纯文本结构,不渲染原文脚本、宏或外链; +- 图片继续通过当前授权资源链路读取; +- 视频流接口支持 `Range`,合法请求返回 `206`、`Content-Range` 和正确 MIME; +- 票据短期有效、单附件绑定、不可跨资源复用; +- 撤权后已签发但未使用的票据也必须拒绝或在最短有效期内失效。 + +### 阶段 2 验证 + +必须覆盖: + +| 用例 | 预期 | +|---|---| +| 有权员工打开文本、图片、视频 | `200`;视频 Range 为 `206` | +| 伪造 `detailRef` | `404/403`,不泄露资源是否存在 | +| 跨租户引用 | 拒绝 | +| 无空间授权 | 拒绝 | +| 来源回答后被停用或撤权 | 再次点击拒绝 | +| 票据过期或跨附件复用 | 拒绝 | +| 旧客户端请求 | 原问答响应仍兼容 | +| 日志扫描 | 无 token、票据、OSS 地址、全文 | + +### 阶段 2 实际结果记录 + +| 字段 | 实际值 | +|---|---| +| 状态 | 待填写 | +| commit | 待填写 | +| JUnit | 待填写 | +| 正授权 | 待填写 | +| 越权矩阵 | 待填写,例如 `12/12 rejected` | +| Range | 待填写,例如 `bytes=... -> 206` | +| 日志敏感信息扫描 | 待填写 | +| 证据目录 | 待填写 | +| 遗留项 | 待填写 | + +--- + +## 6. 阶段 3:手机端引用点击与详情页面 + +**目标:** 在 `390×844` 和 Android App-Plus 中完成真实可点击的文本、图片和视频引用路径。 + +### Files + +- Modify: `mobile-uni/src/types/api.ts` +- Modify: `mobile-uni/src/services/knowledge.ts` +- Modify: `mobile-uni/src/pages/user/sop/index.vue` +- Create: `mobile-uni/src/pages/user/sop/citation-detail.vue` +- Modify: `mobile-uni/src/pages.json` +- Test: `mobile-uni/tests/knowledge-citation-detail.test.mjs` +- Test: `mobile-uni/tests/agent.test.mjs` + +### Task 3.1:引用卡交互 + +- 每条可查看的 `DOCUMENT` 引用使用可访问按钮; +- 显示来源类型、页码或视频时间摘要和“查看出处”; +- 没有 `detailRef` 的旧引用保持只读,不制造假入口; +- 点击时不携带租户、角色、空间或原始地址。 + +### Task 3.2:统一详情页 + +页面状态: + +- `loading`:正在重新验证来源; +- `text`:全文分段加载、目标片段高亮并自动滚动; +- `image`:受控图片加载,支持全屏缩放; +- `video`:受控播放器加载,在 `loadedmetadata/canplay` 后 seek; +- `forbidden/expired/not-found`:明确说明来源已失效或无权查看; +- `error/retry`:网络失败可重试,不把权限错误当网络错误。 + +### Task 3.3:视频定位 + +- 语音引用使用 `startMs`; +- 画面引用使用 `frameMs` 并默认暂停展示目标画面; +- 页面展示 `mm:ss`,不显示内部片段或附件编号; +- 切换引用、离开页面或重试时释放 Blob URL 和播放器资源; +- 若平台无法保证逐帧 seek,业务定位门槛为目标时间 `±500ms`;需要证明目标帧时同时展示服务端保存的对应关键帧预览。 + +### Task 3.4:图片与文本 + +- 图片使用当前认证上下文读取,不保存永久公开地址; +- 文本高亮只基于服务端目标片段,不在前端用模糊搜索猜位置; +- 超长全文使用分页/虚拟加载; +- 原文件打开继续使用短期下载票据。 + +### 阶段 3 验证 + +自动化: + +```powershell +node --test mobile-uni/tests/knowledge-citation-detail.test.mjs mobile-uni/tests/agent.test.mjs +npm --prefix mobile-uni run typecheck +npm --prefix mobile-uni run build:h5 +npm --prefix mobile-uni run build:app +``` + +真实页面: + +1. `390×844` 打开“问”,提出固定问题; +2. 展开引用并点击; +3. 文本详情自动滚动、高亮且无需水平滚动; +4. 图片可打开、缩放和返回; +5. 视频进入详情页并定位; +6. 撤权/过期状态有明确提示; +7. 返回问答页后会话和展开状态可预测; +8. 控制台无错误,页面无无限加载。 + +### 阶段 3 实际结果记录 + +| 字段 | 实际值 | +|---|---| +| 状态 | 待填写 | +| commit | 待填写 | +| Node tests | 待填写 | +| typecheck | 待填写 | +| H5/App build | 待填写 | +| 390×844 文本/图片/视频 | 待填写,例如 `3/3` | +| 错误状态 | 待填写 | +| 截图目录 | 待填写 | +| 遗留项 | 待填写 | + +--- + +## 7. 阶段 4:历史资料定位回填与安全降级 + +**目标:** 为现有正式资料补定位能力,但不重写正式来源审批、文档哈希或既有片段事实。 + +### Files + +- Create: `scripts/backfill-knowledge-fragment-locators.mjs` +- Create: `scripts/verify-knowledge-fragment-locators.mjs` +- Create: `backend/ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/knowledge/service/AihrKnowledgeLocatorBackfillService.java` +- Test: `backend/ruoyi-modules/ruoyi-aihr/src/test/java/org/dromara/aihr/knowledge/AihrKnowledgeLocatorBackfillServiceTest.java` +- Modify: `docs/KNOWLEDGE_PLATFORM_RUNBOOK.md` + +### Task 4.1:只读计划 + +回填必须先输出: + +- 候选附件数; +- 已有 locator 数; +- 可按内容哈希确定性匹配数; +- 需要重新解析数; +- 无原文件、解析失败或歧义数; +- 正式来源数量及其审批/哈希状态。 + +默认只读,不接受浏览器传入目录,不自动删除或替换资料。 + +### Task 4.2:确定性回填 + +- 优先按 `tenant + knowledge + docId + fragmentId` 和规范化文本哈希匹配; +- 重解析只生成 locator,不改变原始文件 SHA-256、治理审批或知识空间授权; +- 匹配不唯一时标记 `AMBIGUOUS`,不得猜测页码或时间; +- 旧视频只有粗 `[mm:ss]` 时标记定位精度; +- 无法精确回填的资料仍可打开全文/原文件,页面显示“暂无精确位置”。 + +### Task 4.3:生产执行边界 + +- 本地和测试库先跑; +- 生产先只读 `plan`; +- 正式写入属于数据库变更,必须生成完整 `MIGRATE_DB:` 授权串并单独获得授权; +- 每批限制数量,可中断、可重跑; +- 回填失败不得删除已有 fragment、embedding、Qdrant point 或正式来源记录。 + +### 阶段 4 验证 + +必须报告真实分母: + +```text +attachments_total +locators_before +locators_created +exact +coarse +ambiguous +failed +formal_sources_unchanged +fragments_unchanged +qdrant_points_unchanged +``` + +随机抽检至少: + +- 5 份文本/PDF/PPT; +- 3 张图片; +- 3 段视频; +- 所有 `AMBIGUOUS/FAILED` 小样本或最多 20 项。 + +### 阶段 4 实际结果记录 + +| 字段 | 实际值 | +|---|---| +| 状态 | 待填写 | +| commit | 待填写 | +| 计划分母 | 待填写 | +| exact/coarse/ambiguous/failed | 待填写 | +| 抽检 | 待填写 | +| 正式来源未变化 | 待填写 | +| fragment/Qdrant 未变化 | 待填写 | +| 证据目录 | 待填写 | +| 遗留项 | 待填写 | + +--- + +## 8. 阶段 5:全链路、安全、性能与回归门禁 + +**目标:** 证明新功能不会扩大权限、破坏问答、拖垮大文件播放或泄漏受保护资料。 + +### 自动化矩阵 + +必须覆盖: + +- `TEXT/PDF/DOCX/PPTX/XLSX/IMAGE/VIDEO`; +- 新旧引用响应兼容; +- 当前员工正授权; +- 跨员工、跨项目、跨空间、跨租户、停用应用; +- 来源撤回、版本替换、哈希漂移; +- 票据过期、重放和跨资源复用; +- Range 首段、中段、尾段和非法范围; +- 500MB 契约下不要求客户端先下载整个视频; +- 连续打开/关闭 20 次无 Blob URL 和播放器泄漏; +- Agent 普通问答、无依据、冲突和越权原有行为不变。 + +### 固定技术阈值 + +| 指标 | 门槛 | +|---|---:| +| 跨租户/越权泄漏 | `0` | +| 正式政策无引用详情入口 | `0`,旧无定位资料除外且必须明确降级 | +| 文本目标片段定位 | `100%` 固定样本 | +| 图片原图匹配 | `100%` 固定样本 | +| 视频业务定位误差 | `≤500ms`;精确帧以保存帧图核验 | +| Range | 合法范围 `206`,非法范围正确拒绝 | +| 详情元数据 API p95 | 本地基准 `≤1s` | +| 内网视频首帧 | 固定样本 `≤5s` | +| 内网二次 seek 完成 | 固定样本 `≤3s` | +| 原问答回归 | 既有专项测试全部通过 | +| 敏感信息扫描 | `0` 个 token/票据/OSS 原址/手机号 | + +性能结果必须同时记录机器、网络、样本大小和运行次数,不能只写平均值。 + +### 阶段 5 实际结果记录 + +| 字段 | 实际值 | +|---|---| +| 状态 | 待填写 | +| commit | 待填写 | +| 后端测试 | 待填写 | +| 移动端测试 | 待填写 | +| 越权矩阵 | 待填写 | +| 定位误差 | 待填写 | +| Range | 待填写 | +| p50/p95 | 待填写 | +| 资源泄漏循环 | 待填写 | +| 敏感信息扫描 | 待填写 | +| 证据目录 | 待填写 | +| 遗留项 | 待填写 | + +--- + +## 9. 阶段 6:发布与 Android 真机收口 + +**目标:** 将完成门禁的 schema、后端和移动端作为独立发布单元部署,并用正式账号与新签名 APK 验证。 + +### 9.1 发布单元 + +| 单元 | 触发条件 | 授权与验证 | +|---|---|---| +| schema | 新定位表/索引 | 先备份和只读计划;独立 `MIGRATE_DB:` 授权 | +| 后端 JAR | 详情、鉴权、Range、回填服务 | `release-backend.sh plan`;独立 `DEPLOY_BACKEND:` 授权 | +| 管理端 | 仅当新增回填/状态管理页 | 单独构建、备份和静态发布;不得覆盖 `/h5` | +| H5 | 引用详情页面 | 备份 `/opt/wygj/www/h5` 后定向同步 | +| Android | `mobile-uni` 页面和逻辑变化 | 版本号与 `versionCode` 递增,重新出包、深检和真机安装 | + +`0.1.17 (117)` 不包含本计划功能。Android 验收必须使用后续新包,不得以 H5 发布替代 APK。 + +Windows 上的后端发布计划明确使用 Git Bash: + +```powershell +& "C:\Program Files\Git\bin\bash.exe" scripts/release-backend.sh plan ... +``` + +不得改用 WSL `bash`,不得绕过脚本手工覆盖 JAR。 + +### 9.2 生产发布后验证 + +后端与 schema: + +1. 服务 `active`; +2. 公开健康入口 HTTP 200; +3. schema 预检通过且运行时 DDL 关闭; +4. 正式账号文本、图片、视频详情正例; +5. 无权限账号、撤权和过期票据反例; +6. 视频 Range `206`; +7. 日志无敏感数据。 + +H5: + +1. `390×844` 实际点击三类引用; +2. 刷新、返回、弱网和失权状态; +3. 不破坏“今日/练/问/我”导航和既有 Agent 会话。 + +Android 真机: + +1. 新签名 APK 覆盖安装与冷启动; +2. 正式员工账号真实发问并打开文本引用; +3. 打开图片引用并缩放; +4. 打开视频引用,自动定位语句和画面; +5. 切后台再返回,定位和权限状态正确; +6. 切断网络后有明确错误,恢复网络可重试; +7. 撤权后再次点击失败关闭; +8. 无 Crash/ANR,测试后恢复设备权限和清理临时文件。 + +至少在当前 vivo Android 15 真机完成主证据;扩大试点前再补一个不同 Android 大版本或厂商。手机号、验证码、令牌和受保护正文不得出现在截图、录像文件名或报告中。 + +### 阶段 6 实际结果记录 + +| 字段 | 实际值 | +|---|---| +| 状态 | 待填写 | +| release commit | 待填写 | +| schema 版本/迁移结果 | 待填写 | +| 生产 JAR SHA-256 | 待填写 | +| H5 构建与远端哈希 | 待填写 | +| APK 版本/versionCode | 待填写 | +| APK SHA-256/签名摘要 | 待填写 | +| 生产正反权限 | 待填写 | +| 真机型号/Android | 待填写 | +| 文本/图片/视频 | 待填写,例如 `3/3` | +| 视频定位误差 | 待填写 | +| Crash/ANR | 待填写 | +| 回滚物 | 待填写 | +| 证据目录 | 待填写 | +| 遗留项 | 待填写 | + +--- + +## 10. Go/No-Go + +只有以下全部成立,才能称“手机端引用详情与媒体定位已完成生产验证”: + +- [ ] 阶段 1—5 均有实际结果且状态为 `技术通过`; +- [ ] schema、后端、H5/Android 实际发布范围与本地构建一致; +- [ ] 正式账号文本、图片、视频 `3/3` 通过; +- [ ] 跨租户、撤权、过期和伪造反例全部拒绝; +- [ ] 视频 Range 和定位误差达到门槛; +- [ ] 新 Android APK 完成真机验证; +- [ ] 无 Crash/ANR; +- [ ] 回滚物、哈希和证据目录完整; +- [ ] 未把 DCloud 测试签名描述为企业签名或应用商店包。 + +任一项缺失时,结论只能是: + +- “本地技术通过,待生产发布”,或 +- “生产后端/H5 已通过,待 Android 新包真机”,或 +- “文本/图片可用,视频精确定位仍阻断”。 + +不得用“引用已经能展开”“原文件可以下载”或“视频可以播放”替代本计划的定位、权限和真机证据。