627 lines
25 KiB
Markdown
627 lines
25 KiB
Markdown
# 用户上传产品小册子解析与留存实施计划
|
||
|
||
> 文档版本:v1.0
|
||
> 编制日期:2026-07-30
|
||
> 适用范围:海报制作中的产品资料选择、产品小册子上传、解析、确认、留存和复用
|
||
> 建议优先级:P0
|
||
> 预计工作量:MVP 约 6 人日,建议预留 20% 风险缓冲
|
||
|
||
---
|
||
|
||
## 一、结论
|
||
|
||
本需求不应直接把普通用户上传的小册子写入现有公共 `PptProduct` 产品库。
|
||
|
||
推荐新增“我的产品资料库”:
|
||
|
||
1. 用户可以继续选择后台已审核产品;
|
||
2. 找不到产品时,可以上传产品小册子 PDF;
|
||
3. 系统持久化保存原 PDF,并异步解析产品名称、产品卖点、保障亮点、币种、投保规则和风险提示;
|
||
4. 用户必须核对并确认解析结果,确认后才能生成文案和海报;
|
||
5. 已确认的小册子进入上传者的“我的产品资料”,以后可直接复用;
|
||
6. 用户资料默认私有,不对其他用户可见;
|
||
7. 只有显式提交并经管理员审核后,才允许转入公共产品库。
|
||
|
||
这一路径能复用现有 PDF 安全校验、小册子解析器、Celery、存储根目录和海报生成逻辑,同时避免未经审核的数据污染公共产品库。
|
||
|
||
---
|
||
|
||
## 二、当前实现与问题定位
|
||
|
||
### 2.1 已有能力
|
||
|
||
| 能力 | 当前实现 | 可复用结论 |
|
||
|------|----------|------------|
|
||
| 公共产品和小册子 | `PptProduct.manual_file_url`、`manual_parsed_rules`、`manual_parse_status` | 可作为公共产品库继续使用 |
|
||
| 管理员上传小册子 | `PptAdminService.upload_manual()` | 可复用文件校验和存储思路 |
|
||
| 小册子解析 | `poster/manual_parser.py::parse_manual_pdf()` | 直接复用产品规则提取 Schema |
|
||
| 异步任务 | `parse_product_manual_task` + Celery | 新增用户资料解析任务,不另建队列 |
|
||
| 客户计划书上传 | `/poster/case-upload`、`PosterCaseUpload` | 继续保留,不能与产品小册子混用 |
|
||
| 海报文案和生成 | `PosterService.generate_copy()`、`generate_poster()` | 增加统一产品来源解析后复用 |
|
||
| PDF 安全校验 | `prepare_pdf_upload()` | 直接复用大小、魔数、页数、密码处理 |
|
||
| 持久化目录 | `get_storage_root()` | 用户小册子必须写入共享持久化卷 |
|
||
|
||
### 2.2 当前阻断点
|
||
|
||
1. `/poster/products` 只返回 `manual_parse_status=reviewed` 的公共产品。
|
||
2. 海报页面必须先选公共 `productId`,找不到产品时无法继续。
|
||
3. 当前 `/poster/case-upload` 上传的是客户计划书,并且强制绑定公共 `productId`。
|
||
4. `generate_copy()` 和 `generate_poster()` 都直接查询 `PptProduct`,不认识用户上传资料。
|
||
5. 普通用户没有自己的产品资料实体、列表、详情、删除和复用能力。
|
||
6. 当前小册子解析只支持管理员维护的公共产品,没有用户所有权校验。
|
||
7. 前端 `localStorage` 只保存当前草稿,不等于服务端永久留存。
|
||
|
||
### 2.3 两类 PDF 必须分开
|
||
|
||
| 文件类型 | 主要内容 | 解析结果 | 生命周期 |
|
||
|----------|----------|----------|----------|
|
||
| 产品小册子 | 产品规则、卖点、币种、保障、风险提示 | `product_rules` | 可跨多个海报项目复用 |
|
||
| 客户计划书 | 年龄、性别、保额、保费、缴费年期、利益表 | `customer_data` | 与本次客户和海报项目绑定 |
|
||
|
||
前端文案也应统一使用“产品小册子”和“客户计划书”,避免两者都显示为“计划书”。
|
||
|
||
---
|
||
|
||
## 三、方案选择
|
||
|
||
### 3.1 方案对比
|
||
|
||
| 方案 | 优点 | 风险 | 结论 |
|
||
|------|------|------|------|
|
||
| 直接创建全局 `PptProduct` | 改动表面较少 | 私有数据可能被其他用户看到;未经审核资料混入公共库;现有所有查询都要补权限 | 不采用 |
|
||
| 给 `PptProduct` 增加 owner/visibility | 仍使用一个产品表 | 需要审计全项目中所有 `PptProduct` 查询,漏一处就可能越权 | 不建议作为首期 |
|
||
| 新建用户产品资料表 | 权限边界清晰;不影响公共产品;便于留存、复用和软删除 | 海报生成需要支持两种来源 | 推荐 |
|
||
| 仅保存为当前海报项目素材 | 与未来工作区模型一致 | 无法自然实现跨项目复用的“我的资料库” | 可作为项目快照,不作为主实体 |
|
||
|
||
### 3.2 推荐架构
|
||
|
||
```text
|
||
产品资料来源
|
||
├── 公共产品:PptProduct(管理员审核)
|
||
└── 我的资料:UserProductMaterial(用户上传并确认)
|
||
│
|
||
├── 原始 PDF:持久化 storage
|
||
├── 解析结果:parsed_rules
|
||
├── 用户确认结果:confirmed_rules
|
||
└── 可选提交审核:进入公共产品库
|
||
|
||
统一 ProductSourceResolver
|
||
│
|
||
├── 校验来源存在、状态和所有权
|
||
├── 输出统一 ProductContext
|
||
└── 提供给文案生成、海报生成和项目恢复
|
||
```
|
||
|
||
统一的 `ProductContext` 至少包含:
|
||
|
||
```json
|
||
{
|
||
"sourceType": "library_product",
|
||
"sourceId": "aia_example",
|
||
"productName": "产品名称",
|
||
"companyId": "aia",
|
||
"companyName": "保司名称",
|
||
"planType": "savings",
|
||
"rules": {},
|
||
"confirmedAt": "2026-07-30T10:00:00"
|
||
}
|
||
```
|
||
|
||
`sourceType` 首期只支持:
|
||
|
||
- `library_product`:后台公共产品;
|
||
- `user_material`:当前用户上传的产品小册子。
|
||
|
||
---
|
||
|
||
## 四、数据模型与存储
|
||
|
||
### 4.1 新表 `insurance_user_product_materials`
|
||
|
||
建议新增 `api/insurance/models/user_product_material.py`。
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `id` | BIGINT | 主键 |
|
||
| `owner_user_id` | VARCHAR(64) | 上传者,必须建立索引 |
|
||
| `tenant_id` | VARCHAR(64) | 租户隔离;无法可靠取得时首期使用 `default` |
|
||
| `company_id` | VARCHAR(50) | 可为空,确认时允许选择 |
|
||
| `company_name` | VARCHAR(100) | PDF 解析或用户确认的保司名称 |
|
||
| `product_name` | VARCHAR(150) | 解析或用户确认的产品名称 |
|
||
| `plan_type` | VARCHAR(20) | savings/ci/iul/other |
|
||
| `original_name` | VARCHAR(200) | 原文件名 |
|
||
| `file_key` | VARCHAR(500) | 相对 storage 根目录的对象键 |
|
||
| `file_size` | BIGINT | 文件字节数 |
|
||
| `page_count` | INTEGER | PDF 页数 |
|
||
| `sha256` | VARCHAR(64) | 用户内去重和审计 |
|
||
| `parse_status` | VARCHAR(20) | pending/queued/parsing/parsed/failed |
|
||
| `parse_message` | VARCHAR(500) | 进度提示 |
|
||
| `parse_error` | TEXT | 解析错误,返回前需要脱敏 |
|
||
| `parse_task_id` | VARCHAR(200) | Celery 任务 ID |
|
||
| `parsed_rules` | TEXT | 系统原始解析 JSON |
|
||
| `confirmed_rules` | TEXT | 用户核对后的最终 JSON |
|
||
| `confirmed_by` | VARCHAR(64) | 确认人 |
|
||
| `confirmed_at` | TIMESTAMP | 确认时间 |
|
||
| `review_status` | VARCHAR(20) | private/submitted/approved/rejected |
|
||
| `status` | SMALLINT | 1=可用,0=停用 |
|
||
| `created_at` / `updated_at` | TIMESTAMP | 时间字段 |
|
||
| `deleted_at` | TIMESTAMP | 软删除 |
|
||
|
||
索引和约束:
|
||
|
||
- `INDEX(owner_user_id, deleted_at, updated_at)`;
|
||
- `INDEX(owner_user_id, sha256)`;
|
||
- `INDEX(parse_status)`;
|
||
- 不做跨用户重复文件提示,避免侧信道泄露;
|
||
- `confirmed_rules` 为空的资料不得用于生成。
|
||
|
||
### 4.2 现有表兼容扩展
|
||
|
||
为 `poster_case_uploads` 和 `poster_records` 增加:
|
||
|
||
| 字段 | 用途 |
|
||
|------|------|
|
||
| `product_source_type` | `library_product` / `user_material` |
|
||
| `product_source_id` | 统一使用字符串保存来源 ID |
|
||
| `product_snapshot_json` | 提交生成时固化产品名称、保司、规则和确认时间 |
|
||
|
||
兼容规则:
|
||
|
||
- 旧记录没有 `product_source_type` 时,按 `library_product + product_id` 读取;
|
||
- 新代码继续接受旧请求中的 `productId`;
|
||
- 新页面优先发送 `productSource`;
|
||
- 已生成项目始终使用 `product_snapshot_json`,后台资料后续变化不能静默改变历史海报。
|
||
|
||
### 4.3 文件存储
|
||
|
||
推荐对象键:
|
||
|
||
```text
|
||
uploads/product-manuals/users/{safe_user_id}/{material_id}/{uuid}.pdf
|
||
```
|
||
|
||
要求:
|
||
|
||
- 只在数据库保存相对 `file_key`,读取时由统一存储服务拼接;
|
||
- API 和 Worker 必须挂载同一个 `INSURANCE_STORAGE_ROOT` 持久化卷;
|
||
- 下载接口必须鉴权并校验文件位于产品小册子目录内;
|
||
- PDF 密码仅用于本次解密,不写数据库、不写日志;
|
||
- 替换文件时生成新对象,不覆盖旧文件;新解析成功后再切换引用;
|
||
- 删除采用软删除;被历史海报快照引用的文件不得立即物理删除。
|
||
|
||
建议默认保留策略:
|
||
|
||
- 活跃资料:保留至用户主动删除;
|
||
- 用户删除:进入 30 天回收期;
|
||
- 已被海报使用:至少保留到相关海报项目过期;
|
||
- 最终期限需要由业务、法务和存储成本负责人确认。
|
||
|
||
---
|
||
|
||
## 五、后端接口
|
||
|
||
### 5.1 用户产品资料接口
|
||
|
||
| 方法 | URL | 用途 |
|
||
|------|-----|------|
|
||
| GET | `/insurance/poster/product-materials` | 查询“我的产品资料” |
|
||
| POST | `/insurance/poster/product-materials` | 上传 PDF,创建记录并自动投递解析 |
|
||
| GET | `/insurance/poster/product-materials/{id}` | 查询详情和解析状态 |
|
||
| PUT | `/insurance/poster/product-materials/{id}/confirm` | 保存修正结果并确认 |
|
||
| POST | `/insurance/poster/product-materials/{id}/retry` | 解析失败后重试 |
|
||
| GET | `/insurance/poster/product-materials/{id}/file` | 鉴权预览原 PDF |
|
||
| DELETE | `/insurance/poster/product-materials/{id}` | 软删除本人资料 |
|
||
| POST | `/insurance/poster/product-materials/{id}/submit-review` | 可选:提交进入公共库审核 |
|
||
|
||
上传使用 `multipart/form-data`:
|
||
|
||
| 字段 | 必填 | 说明 |
|
||
|------|:----:|------|
|
||
| `file` | 是 | PDF |
|
||
| `password` | 否 | PDF 打开密码,不保存 |
|
||
| `companyId` | 否 | 用户已知时可传 |
|
||
| `planType` | 否 | 用户已知时可传 |
|
||
|
||
### 5.2 产品来源接口
|
||
|
||
保留现有 `GET /poster/products`,新增:
|
||
|
||
```text
|
||
GET /insurance/poster/product-sources
|
||
```
|
||
|
||
返回:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "success",
|
||
"data": {
|
||
"libraryProducts": [],
|
||
"myMaterials": []
|
||
}
|
||
}
|
||
```
|
||
|
||
“我的资料”只返回:
|
||
|
||
- 当前用户所有;
|
||
- 未删除且启用;
|
||
- `parse_status=parsed` 或已确认;
|
||
- 生成时仍必须再次检查 `confirmed_rules`。
|
||
|
||
### 5.3 生成接口兼容
|
||
|
||
新请求:
|
||
|
||
```json
|
||
{
|
||
"productSource": {
|
||
"type": "user_material",
|
||
"id": "123"
|
||
},
|
||
"caseUploadId": 456,
|
||
"templateId": 1
|
||
}
|
||
```
|
||
|
||
兼容逻辑:
|
||
|
||
1. 有 `productSource` 时使用新逻辑;
|
||
2. 只有 `productId` 时映射为 `library_product`;
|
||
3. `ProductSourceResolver` 完成所有权、状态、确认状态和公司状态检查;
|
||
4. `generate_copy()`、`generate_poster()` 不再直接各自拼装产品规则;
|
||
5. 创建 `PosterRecord` 时保存产品快照。
|
||
|
||
建议错误码:
|
||
|
||
| 错误码 | 含义 |
|
||
|--------|------|
|
||
| 4101 | 小册子文件不合法 |
|
||
| 4102 | 小册子需要密码或密码错误 |
|
||
| 4103 | 小册子解析中 |
|
||
| 4104 | 小册子解析失败 |
|
||
| 4105 | 小册子尚未确认 |
|
||
| 4106 | 产品资料不存在或无权访问 |
|
||
| 4107 | 产品资料已删除或停用 |
|
||
|
||
---
|
||
|
||
## 六、解析与确认流程
|
||
|
||
```text
|
||
上传 PDF
|
||
→ prepare_pdf_upload 安全校验和解密
|
||
→ 计算 sha256
|
||
→ 持久化原文件
|
||
→ 创建 material,状态 queued
|
||
→ Celery 解析
|
||
→ parse_manual_pdf
|
||
→ 保存 parsed_rules,状态 parsed
|
||
→ 用户查看原 PDF + 修改结构化字段
|
||
→ 服务端 Schema 校验
|
||
→ 保存 confirmed_rules、确认人和确认时间
|
||
→ 可用于海报并进入“我的资料”
|
||
```
|
||
|
||
### 6.1 解析复用
|
||
|
||
直接复用:
|
||
|
||
- `insurance.utils.security.prepare_pdf_upload`;
|
||
- `insurance.poster.manual_parser.parse_manual_pdf`;
|
||
- `insurance.generation.celery_tasks` 的 Celery 基础设施;
|
||
- `insurance.config.get_storage_root`;
|
||
- BaoDan 数据库、Redis、日志和认证。
|
||
|
||
新增 `parse_user_product_material_task(material_id)`,状态流转:
|
||
|
||
```text
|
||
pending → queued → parsing → parsed → confirmed
|
||
└→ failed
|
||
```
|
||
|
||
数据库字段仍使用 `parsed + confirmed_at` 表示最终确认,不必为了展示再增加 `confirmed` 状态值。
|
||
|
||
### 6.2 确认 Schema
|
||
|
||
首期至少要求:
|
||
|
||
- `product_name` 非空;
|
||
- `features` 为 1~8 项;
|
||
- 每项卖点包括 `title`、`summary`,尽量保留 `source_page`;
|
||
- `currency_options` 为字符串数组;
|
||
- `coverage_highlights` 为字符串数组;
|
||
- `risk_warnings` 为字符串数组;
|
||
- `investment_rules` 为对象;
|
||
- 所有字符串限制长度,禁止把整份 PDF 原文写入结构化字段。
|
||
|
||
用户替换原 PDF 或重新解析后,必须清空旧 `confirmed_rules` 和 `confirmed_at`,强制重新确认。
|
||
|
||
### 6.3 Prompt 安全
|
||
|
||
- PDF 内容一律视为待抽取数据,不视为系统指令;
|
||
- 系统 Prompt 明确忽略 PDF 内的指令、链接和要求;
|
||
- 结构化输出必须做服务端 Schema 校验;
|
||
- 不把 PDF 全文、密码、客户敏感数据或模型密钥写入日志;
|
||
- 对超长 PDF 使用页级筛选和最大上下文限制;
|
||
- 解析失败应返回结构化错误,不向前端暴露内部堆栈。
|
||
|
||
---
|
||
|
||
## 七、前端交互
|
||
|
||
### 7.1 产品资料面板
|
||
|
||
将当前“选择产品”抽屉改为两个标签:
|
||
|
||
1. `系统产品`:现有后台已审核产品;
|
||
2. `我的资料`:当前用户已上传并确认的小册子。
|
||
|
||
固定提供:
|
||
|
||
- “上传产品小册子”按钮;
|
||
- 搜索无结果时显示“没有找到?上传小册子”;
|
||
- 我的资料卡片展示产品名、保司、解析状态、更新时间;
|
||
- 支持查看原 PDF、编辑确认结果、替换、删除。
|
||
|
||
### 7.2 上传对话框
|
||
|
||
步骤:
|
||
|
||
1. 选择 PDF,可选输入密码;
|
||
2. 上传并显示真实解析状态;
|
||
3. 左侧预览 PDF,右侧核对解析字段;
|
||
4. 点击“确认并使用”;
|
||
5. 自动加入“我的资料”并选中。
|
||
|
||
解析可能超过页面停留时间,用户关闭弹窗后任务仍应继续;再次进入“我的资料”可恢复状态。
|
||
|
||
### 7.3 草稿状态调整
|
||
|
||
`PosterDraft` 增加:
|
||
|
||
```ts
|
||
productSourceType: 'library_product' | 'user_material' | ''
|
||
productSourceId: string
|
||
productSourceConfirmedAt: string
|
||
```
|
||
|
||
兼容保留 `productId`,但新流程不再仅以 `productId` 判断是否完成产品选择。
|
||
|
||
当用户更换产品来源时:
|
||
|
||
- 清空已生成文案;
|
||
- 将合规确认置为未确认;
|
||
- 如果客户计划书与旧产品绑定,提示并要求重新确认;
|
||
- 不直接删除已上传客户计划书。
|
||
|
||
### 7.4 文案调整
|
||
|
||
当前“计划书与数据”建议改为“客户计划书与数据”,上传提示明确写“客户计划书 PDF”。
|
||
|
||
产品资料区域明确写“产品小册子”,避免用户把同一个文件传错入口。
|
||
|
||
---
|
||
|
||
## 八、代码改动清单
|
||
|
||
### 8.1 后端
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| `api/insurance/db/migrate_026.py`(以开发分支启动时的下一个可用编号为准) | 新表、兼容字段、索引和幂等迁移 |
|
||
| `api/insurance/models/user_product_material.py` | 用户产品资料模型 |
|
||
| `api/insurance/models/__init__.py` | 注册模型 |
|
||
| `api/insurance/poster/product_material_service.py` | 上传、查询、确认、删除和所有权校验 |
|
||
| `api/insurance/poster/product_source_resolver.py` | 统一公共产品与用户资料 |
|
||
| `api/insurance/poster/routes.py` | 新增产品资料和产品来源接口 |
|
||
| `api/insurance/poster/service.py` | 文案/海报生成改用统一 resolver |
|
||
| `api/insurance/generation/celery_tasks.py` | 新增用户小册子解析任务 |
|
||
| `api/insurance/models/poster_case_upload.py` | 保存产品来源引用 |
|
||
| `api/insurance/models/poster_record.py` | 保存产品来源和产品快照 |
|
||
| `api/insurance/utils/security.py` | 仅在现有能力不足时补充限制,不重写上传校验 |
|
||
|
||
当前仓库的下一个迁移编号是 026,但《海报生成工作台完整重构修复计划》也预留了该编号。若两项工作进入同一发布版本,应合并为一个迁移;若分支独立交付,后合并的分支必须顺延编号。海报工作台 V2 的 `poster_project_assets` 已落地时,在生成项目时把用户资料注册为:
|
||
|
||
```text
|
||
asset_type=product_manual
|
||
source_type=library
|
||
source_ref_id={user_material_id}
|
||
```
|
||
|
||
### 8.2 前端
|
||
|
||
| 文件 | 改动 |
|
||
|------|------|
|
||
| `frontend/src/utils/poster-api.ts` | 产品资料上传、查询、确认、删除、重试接口 |
|
||
| `frontend/src/composables/usePosterWorkspace.ts` | 增加产品来源状态和兼容恢复 |
|
||
| `frontend/src/components/poster/workspace/PosterProductPanel.vue` | 系统产品/我的资料双标签 |
|
||
| `frontend/src/components/poster/workspace/UserProductMaterialDialog.vue` | 新增上传、轮询、核对弹窗 |
|
||
| `frontend/src/components/poster/workspace/PosterSourcePanel.vue` | 文案改为客户计划书,解除“必须先选公共 productId”假设 |
|
||
| `frontend/src/pages/PosterPage.vue` | 生成请求发送 `productSource` |
|
||
|
||
### 8.3 测试
|
||
|
||
建议新增:
|
||
|
||
```text
|
||
tests/poster_product_material_model_test.py
|
||
tests/poster_product_material_api_test.py
|
||
tests/poster_product_material_parse_test.py
|
||
tests/poster_product_source_resolver_test.py
|
||
tests/poster_product_material_permission_test.py
|
||
tests/poster_product_material_storage_test.py
|
||
tests/poster_user_manual_e2e_test.py
|
||
```
|
||
|
||
---
|
||
|
||
## 九、可执行排期
|
||
|
||
### Phase 0:需求冻结和样例准备(0.5 人日)
|
||
|
||
- [x] MANUAL-0001 确认“用户资料默认私有”;
|
||
- [x] MANUAL-0002 确认是否允许用户确认后立即生成;
|
||
- [ ] MANUAL-0003 准备 3 份脱敏样例:文本 PDF、扫描 PDF、加密 PDF;
|
||
- [x] MANUAL-0004 固化解析字段 Schema 和验收期望;
|
||
- [ ] MANUAL-0005 确认保留期、单文件大小和页数上限。
|
||
|
||
完成标准:三份样例和预期 JSON 入库到测试夹具,关键产品决策有书面结论。
|
||
|
||
### Phase 1:数据、存储和基础 API(1.5 人日)
|
||
|
||
- [x] MANUAL-0101 编写幂等迁移;
|
||
- [x] MANUAL-0102 新增模型和 `to_dict()`;
|
||
- [x] MANUAL-0103 实现用户内 SHA-256 去重;
|
||
- [x] MANUAL-0104 实现上传、列表、详情、预览和软删除;
|
||
- [x] MANUAL-0105 实现文件路径和所有权校验;
|
||
- [x] MANUAL-0106 增加迁移、存储和权限单元测试。
|
||
|
||
完成标准:重启 API/Worker 后文件仍存在,用户 A 无法查看、下载或删除用户 B 的资料。
|
||
|
||
### Phase 2:异步解析和人工确认(1.5 人日)
|
||
|
||
- [x] MANUAL-0201 新增用户资料 Celery 任务;
|
||
- [x] MANUAL-0202 复用 `parse_manual_pdf()`;
|
||
- [x] MANUAL-0203 增加 Prompt 注入防护和输出 Schema 校验;
|
||
- [x] MANUAL-0204 实现轮询、失败原因和重试;
|
||
- [x] MANUAL-0205 实现确认接口和确认失效规则;
|
||
- [ ] MANUAL-0206 覆盖加密、损坏、空文本、超限和扫描件场景。
|
||
|
||
完成标准:上传后异步完成解析;失败可重试;未确认资料不能进入生成。
|
||
|
||
### Phase 3:海报链路接入(1.0 人日)
|
||
|
||
- [x] MANUAL-0301 实现 `ProductSourceResolver`;
|
||
- [x] MANUAL-0302 改造 `generate_copy()`;
|
||
- [x] MANUAL-0303 改造 `generate_poster()`;
|
||
- [x] MANUAL-0304 为计划书和海报记录保存产品来源与快照;
|
||
- [x] MANUAL-0305 验证旧 `productId` 请求保持兼容;
|
||
- [x] MANUAL-0306 增加跨用户引用和历史快照测试。
|
||
|
||
完成标准:公共产品和本人资料均可生成;用户资料更新不改变已生成海报的产品快照。
|
||
|
||
### Phase 4:前端交互(1.0 人日)
|
||
|
||
- [x] MANUAL-0401 产品选择抽屉增加双标签;
|
||
- [x] MANUAL-0402 新增上传、解析进度、预览和核对弹窗;
|
||
- [x] MANUAL-0403 支持从“我的资料”复用、替换和删除;
|
||
- [x] MANUAL-0404 修正产品小册子/客户计划书文案;
|
||
- [x] MANUAL-0405 产品来源变化时清理失效草稿状态;
|
||
- [ ] MANUAL-0406 PC 和移动端基本回归。
|
||
|
||
完成标准:用户无需管理员帮助即可完成“上传小册子 → 解析 → 确认 → 生成海报 → 下次复用”。
|
||
|
||
### Phase 5:测试、灰度和上线(0.5 人日)
|
||
|
||
- [x] MANUAL-0501 执行后端权限、存储和兼容回归;
|
||
- [ ] MANUAL-0502 执行前端端到端流程;
|
||
- [x] MANUAL-0503 增加 `POSTER_USER_MANUAL_UPLOAD_ENABLED` 功能开关;
|
||
- [ ] MANUAL-0504 管理员和 5% 用户灰度;
|
||
- [ ] MANUAL-0505 观察解析成功率、耗时、重试率和存储增长;
|
||
- [x] MANUAL-0506 更新 API、测试、部署和任务清单文档。
|
||
|
||
完成标准:P0/P1 用例通过;关闭功能开关可立即回到旧产品选择流程;旧海报可继续查看和下载。
|
||
|
||
### 可选 Phase 6:提交公共库审核(1.5~2 人日)
|
||
|
||
- [ ] MANUAL-0601 用户提交审核;
|
||
- [ ] MANUAL-0602 管理端待审列表和 PDF/解析结果对照;
|
||
- [ ] MANUAL-0603 管理员修改、批准和驳回;
|
||
- [ ] MANUAL-0604 批准后创建或合并 `PptProduct`;
|
||
- [ ] MANUAL-0605 记录来源资料、审核人和审核时间;
|
||
- [ ] MANUAL-0606 同产品重复提交检测。
|
||
|
||
首期不应把该阶段作为“用户能生成海报”的阻断项,除非合规制度明确要求所有产品必须管理员预审。
|
||
|
||
---
|
||
|
||
## 十、测试与验收
|
||
|
||
### 10.1 P0 验收
|
||
|
||
- [ ] 不选择公共产品也能上传产品小册子;
|
||
- [ ] 原 PDF 保存在持久化 storage,服务重启后仍可预览;
|
||
- [ ] 加密 PDF 密码只用于本次请求;
|
||
- [ ] 解析任务页面关闭后继续执行;
|
||
- [ ] 用户可以修改并确认解析结果;
|
||
- [ ] 未确认资料不能生成文案或海报;
|
||
- [ ] 确认后的资料出现在“我的资料”并可跨项目复用;
|
||
- [ ] 用户 A 无法枚举、预览、引用、修改或删除用户 B 的资料;
|
||
- [ ] 公共产品原流程保持可用;
|
||
- [ ] 旧 `productId` API 调用保持兼容;
|
||
- [ ] 历史海报使用产品快照,不受资料后续修改影响。
|
||
|
||
### 10.2 P1 验收
|
||
|
||
- [ ] 同一用户重复上传相同 PDF 不重复调用模型;
|
||
- [ ] 替换 PDF 后旧确认自动失效;
|
||
- [ ] 解析失败可查看可理解原因并重试;
|
||
- [ ] 危险 PDF、伪造扩展名、超大小和超页数被拒绝;
|
||
- [ ] PDF 中的 Prompt 注入文本不会改变解析任务;
|
||
- [ ] 日志中没有 PDF 密码、完整正文和模型密钥;
|
||
- [ ] 删除资料不会破坏历史海报;
|
||
- [ ] 解析成功率、P95 耗时和存储增长可监控。
|
||
|
||
### 10.3 建议性能指标
|
||
|
||
| 指标 | 建议目标 |
|
||
|------|----------|
|
||
| 上传接口 P95 | 小于 3 秒,不含客户端网络上传时间 |
|
||
| 解析任务成功率 | 大于 95%,扫描件单独统计 |
|
||
| 文本 PDF 解析 P95 | 小于 120 秒 |
|
||
| 重复上传模型调用 | 0 次 |
|
||
| 跨用户访问成功数 | 0 |
|
||
| 服务重启后文件丢失 | 0 |
|
||
|
||
---
|
||
|
||
## 十一、风险与处理
|
||
|
||
| 风险 | 影响 | 应对 |
|
||
|------|------|------|
|
||
| 用户资料被误当公共产品 | 数据泄露、合规风险 | 独立表、默认私有、所有权校验 |
|
||
| PDF 解析结果不准确 | 错误产品卖点进入文案 | 人工确认、来源页、未确认阻断 |
|
||
| 扫描 PDF 无文本 | 解析失败率高 | 首期明确提示;P1 接入 OCR |
|
||
| 重复上传导致模型成本增加 | 成本浪费 | 用户内 SHA-256 去重 |
|
||
| 文件只存容器临时盘 | 重启后丢失 | 强制共享持久化卷和 readiness 检查 |
|
||
| 资料修改影响历史海报 | 审计不可追溯 | 生成时保存产品快照 |
|
||
| 用户删除资料导致历史断链 | 旧项目无法恢复 | 软删除、引用保护、延迟物理清理 |
|
||
| 在现有 `PptProduct` 上补权限遗漏 | 跨用户暴露 | 不复用全局产品表保存私有资料 |
|
||
| 与海报工作台 V2 迁移冲突 | 重复表和迁移编号 | 用户资料作为长期资料库,项目素材只保存引用和快照 |
|
||
|
||
---
|
||
|
||
## 十二、上线决策项
|
||
|
||
开发开始前必须确认:
|
||
|
||
| 编号 | 决策 | 推荐默认值 |
|
||
|------|------|------------|
|
||
| D-01 | 用户资料是否默认私有 | 是 |
|
||
| D-02 | 用户确认后能否立即生成 | 能,仅限本人使用 |
|
||
| D-03 | 是否自动进入公共产品库 | 否,必须显式提交并管理员审核 |
|
||
| D-04 | 扫描 PDF 是否首期支持 OCR | 否,先提示用户上传可检索 PDF |
|
||
| D-05 | 文件默认保留期 | 活跃资料保留至用户删除,删除后 30 天回收 |
|
||
| D-06 | 单文件大小和页数 | 沿用 `prepare_pdf_upload()` 当前配置 |
|
||
| D-07 | 是否允许无保司资料 | 允许上传,确认前必须补齐或选择“其他” |
|
||
| D-08 | 删除后历史海报是否保留文件 | 是,保留至项目生命周期结束 |
|
||
|
||
---
|
||
|
||
## 十三、完成定义
|
||
|
||
只有以下条件全部满足,本需求才可标记完成:
|
||
|
||
- 用户能在海报产品选择区上传系统中不存在的小册子;
|
||
- 小册子原文件、解析结果、确认结果和确认信息均保存在服务端;
|
||
- 上传资料默认只对本人可见;
|
||
- 用户确认后可以立即用于文案和海报生成;
|
||
- 下次新建海报时可以从“我的资料”直接选择;
|
||
- 公共产品和旧接口不受影响;
|
||
- 生成记录保存产品快照;
|
||
- 文件、权限、解析、兼容和端到端自动化测试通过;
|
||
- 生产环境 API 与 Worker 使用同一持久化存储;
|
||
- 功能支持灰度开关和无损回退。
|