baodan/docs/PPT多文件上传比对功能_详细修复计划书.md

723 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# PPT 多文件上传比对功能详细修复计划书
> 文档状态:待确认
> 适用范围PPT 生成工作台的“上传材料 → 智能解析 → 核对数据 → 配置生成 → 预览交付”流程
> 目标版本:在不改变现有工作台结构、不新增数据库表的前提下,完整支持 14 份计划书上传、同险种比较和跨险种组合分析
---
## 1. 结论摘要
当前系统并不是从零实现多文件能力:
- 后端 `/insurance/ppt/upload` 已按数组接收多个 `files/types/companies/products/passwords`
- 会话模型已使用 `files_json``extractions_json` 保存文件列表及多个解析结果。
- 解析任务会逐个处理会话中的全部文件。
- 比较契约已支持最多 4 份计划书,并能识别单份、多储蓄险、同险种比较和跨险种组合场景。
- 已存在 `multi_savings_comparison.pptx` 多储蓄险比较模板。
当前主要问题集中在四处:
1. 前端上传页把储蓄险、重疾险、IUL 分别建模成单个 `File`,每个上传框 `limit=1`,同一险种无法添加第二份。
2. 上传接口没有提前限制最多 4 份,也没有严格校验五组并行数组是否一一对应。
3. 同币种、同险种等跨文件兼容性校验发生得太晚,用户可能完成解析后,到生成任务阶段才看到失败。
4. 储蓄险比较渲染较完整;通用重疾险/IUL 比较页主要读取前两个产品,不能完整表达 34 份计划书。
本次修复应采用“前端条目化 + 后端原子校验 + 核对阶段前置兼容性检查 + 渲染器全量消费比较契约”的方案。
---
## 2. 修复范围与边界
### 2.1 本次必须完成
- 支持一次添加、删除并上传 14 份 PDF。
- 允许多份文件选择相同险种。
- 每份文件独立配置险种、保险公司、产品和 PDF 密码。
- 自动识别以下业务场景:
- 1 份:单计划书分析。
- 24 份同险种:横向比较。
- 多份不同险种:组合方案,不直接做产品高低排名。
- 同险种横向比较时,在数据核对阶段检查币种、受保人条件等兼容性。
- 储蓄险、重疾险、IUL 的 24 份同类型计划书都必须在成品中完整出现。
- 保留现有 5 步工作流、工作台导航、主题色、组件库和 API 路径。
- 补充后端自动化测试、前端构建检查和真实 PPTX 渲染验证。
### 2.2 明确不做
- 不修改数据库表结构,不新增迁移脚本。
- 不修改 BaoDan 基座文件。
- 不新增“手动选择比较/组合模式”的开关,场景继续由实际文件自动判断。
- 不加入拖拽排序V1 按添加顺序标记为方案 AD。
- 不支持 PDF 以外的计划书格式。
- 不在本次引入新的前端测试框架或状态管理库。
- 不重做智能解析、归一化和模板管理模块。
---
## 3. 现状分析
### 3.1 当前前端数据结构
`PptUpload.vue` 使用以下固定状态:
```text
savingsFile / savingsCompany / savingsProduct / savingsPassword
ciFile / ciCompany / ciProduct / ciPassword
iulFile / iulCompany / iulProduct / iulPassword
```
这意味着每个险种最多只有一个文件。即使把 `el-upload``limit` 从 1 改大,保司、产品和密码仍然无法与多份文件分别绑定,所以不能只改 `multiple``limit`
### 3.2 当前页面布局条件
- PPT 页面是“左侧步骤导航 + 中间工作区 + 右侧上下文”的三栏布局。
- 上传步骤创建会话前没有右侧上下文面板,因此中间区域可使用较完整宽度。
- 中间内容区桌面端内边距为 20px移动端为 12px。
- 当前上传区域使用自适应三列卡片;小于 768px 时切换为单列。
- 页面已经使用 Element Plus、绿色主色、1012px 圆角和轻边框,不需要建立新的视觉语言。
### 3.3 当前后端链路
```mermaid
flowchart LR
A["PptUpload.vue 固定三个单文件槽位"] --> B["POST /ppt/upload 数组参数"]
B --> C["PptSession.files_json"]
C --> D["解析任务逐文件处理"]
D --> E["PptSession.extractions_json"]
E --> F["逐文件校验"]
F --> G["生成阶段判断场景"]
G --> H["比较契约,最多 4 份"]
H --> I["PPT 渲染"]
```
后端主链路已经能保存和解析多个文件。修复重点不是重写接口,而是补齐入口约束、跨文件校验和渲染一致性。
### 3.4 当前业务限制
- 横向比较至少需要 2 份有效计划书。
- 一次最多比较 4 份。
- 横向比较要求险种一致。
- 横向比较要求币种一致。
- 受保人年龄或性别不一致时可以继续,但必须警告用户谨慎使用结论。
- 不同险种应进入组合模式,不能直接横向排名。
---
## 4. 目标交互设计
### 4.1 设计原则
1. 文件和业务配置必须一一绑定,避免并行状态错位。
2. 同险种第二份文件必须容易添加,不能隐藏在高级操作中。
3. 用户在上传前就能看懂本次会生成“单份分析、同险种比较还是组合方案”。
4. 错误尽量在当前步骤暴露,不能到生成任务阶段才失败。
5. 保持现有工作台的操作型界面,不引入大面积装饰或新的页面层级。
### 4.2 上传区目标结构
上传页面改为“场景摘要 + 计划书条目列表 + 添加入口 + 安全说明 + 主操作按钮”。
```text
┌──────────────────────────────────────────────────────────────┐
│ 本次方案 [同险种对比] 已添加 2 / 4 │
│ 将按相同保单年度比较;币种将在解析后校验 │
├──────────────────────────────────────────────────────────────┤
│ 方案 A [储蓄险 ▼] [删除] │
│ ┌──────────────── PDF 文件区域 ────────────────────────────┐ │
│ │ 计划书_A.pdf · 3.2 MB [重新选择] │ │
│ └─────────────────────────────────────────────────────────┘ │
│ [保险公司 ▼] [产品(可选) ▼] [PDF 密码(如有)] │
├──────────────────────────────────────────────────────────────┤
│ 方案 B [储蓄险 ▼] [删除] │
│ ┌──────────────── PDF 文件区域 ────────────────────────────┐ │
│ │ 拖拽 PDF 到此处,或点击选择 │ │
│ └─────────────────────────────────────────────────────────┘ │
│ [保险公司 ▼] [产品(可选) ▼] [PDF 密码(如有)] │
├──────────────────────────────────────────────────────────────┤
│ [+ 添加计划书] 最多 4 份 │
├──────────────────────────────────────────────────────────────┤
│ 🔒 文件仅用于本次生成;密码仅用于本次解密 │
│ [上传 2 份并开始解析] │
└──────────────────────────────────────────────────────────────┘
```
### 4.3 条目行为
每个条目包含:
- 方案编号:按数组顺序显示方案 A、B、C、D。
- 险种储蓄险、重疾险、IUL。
- PDF 文件:每个条目仅绑定一个 PDF。
- 保险公司:可选。
- 产品:可选,按险种及保司过滤。
- PDF 密码:可选,仅保留在浏览器当前状态和本次请求中。
- 删除操作:至少保留一个空条目;只有一个条目时删除等价于清空。
新增条目规则:
- 初始显示一个空条目。
- 点击“添加计划书”时,新条目默认沿用上一条的险种,方便连续添加同险种比较文件。
- 新条目不继承文件、产品、保司或密码。
- 达到 4 份时禁用新增按钮,并显示“最多支持 4 份计划书”。
字段联动规则:
- 修改险种后,清空当前不兼容的产品。
- 选择产品后,自动回填对应保司,保持现有行为。
- 修改保司后,如果当前产品不属于该保司,则清空产品。
- 文件替换只替换当前条目的文件,不清除险种和产品配置。
- 删除条目后立即重新编号,但不改变其他条目的内容和顺序。
### 4.4 场景摘要
前端根据当前条目即时计算,仅用于解释,不作为后端最终判断依据:
| 条件 | 标签 | 提示 |
|---|---|---|
| 无有效文件 | 待添加 | 添加 14 份 PDF 计划书 |
| 1 份 | 单份分析 | 生成一份计划书的客户讲解 PPT |
| 24 份且险种相同 | 同险种对比 | 将按相同保单年度和统一口径比较 |
| 多份且险种不同 | 组合方案 | 展示产品分工与现金流关系,不直接排名 |
同险种比较提示必须明确:“币种、受保人条件和关键年度将在解析后核对。”
### 4.5 主按钮状态
按钮文案随文件数量变化:
- 0 份:`开始上传并解析`,禁用。
- 1 份:`上传 1 份并开始解析`。
- 24 份:`上传 N 份并开始解析`。
- 上传中:`正在上传 N 份计划书…`,所有条目和新增/删除操作锁定。
`canSubmit` 条件:
- 有 14 个条目绑定了 PDF。
- 所有准备提交的条目险种合法。
- 没有本地文件格式错误。
- 不要求必须选择保司或产品。
- 不要求空条目必须填写;提交时应忽略完全空白条目。
- 如果条目填写了业务配置但没有文件,应在该条目显示“请先选择 PDF”不能静默忽略。
### 4.6 状态与错误展示
必须覆盖以下状态:
- 配置加载中:保留当前骨架屏。
- 配置加载失败:显示页内错误和“重新加载”,不能只弹一次消息。
- 空状态:一个可直接操作的空条目。
- 文件格式错误:条目内显示“仅支持 PDF”。
- 文件安全校验失败:错误绑定到具体方案和文件名。
- 密码错误:显示在对应条目的密码字段下。
- 数量超过限制:新增操作禁用,后端再次拦截。
- 部分文件不合法:整个上传请求失败,不创建残缺会话。
- 上传失败:保留用户已选文件和配置,允许修改后重试。
- 上传成功:清空密码,再进入智能解析步骤。
### 4.7 响应式布局
#### 宽屏工作区
- 条目列表使用两列卡片,最多两行。
- 每张卡片内部保司和产品并排,密码单独一行或与其组成三列,按可用宽度决定。
- 场景摘要和底部操作占整行。
#### 中等宽度
- 条目列表改为单列。
- 文件区域和业务字段仍保持紧凑的两列结构。
- 不依赖右侧上下文面板展示关键信息。
#### 小于 768px
- 条目、字段和按钮全部单列。
- 删除、重新选择和添加按钮触控高度不低于 44px。
- 底部主按钮占满宽度。
- 文件名允许两行省略,不能把删除按钮挤出屏幕。
应优先使用现有容器查询逻辑,不额外建立一套互相冲突的断点。
### 4.8 可访问性要求
- 险种、保司、产品、密码必须有可识别标签,不能只依赖 placeholder。
- 删除按钮包含方案名称,例如 `aria-label="删除方案 B"`
- 场景变化和上传错误使用可被辅助技术感知的状态区域。
- 错误信息与对应输入控件关联。
- 键盘可完成新增、选择文件、修改字段、删除和提交。
- 不能只用颜色区分储蓄险、重疾险和 IUL必须同时显示文字标签。
---
## 5. 前端实施方案
### 5.1 目标数据结构
将固定的 12 个状态字段替换为一个条目数组:
```ts
type PlanType = 'savings' | 'ci' | 'iul'
interface UploadPlanItem {
id: string
file: File | null
planType: PlanType
companyId: string
productId: string
password: string
errors: {
file?: string
password?: string
general?: string
}
}
```
说明:
- `id` 只用于前端稳定渲染,不发送给后端。
- 不把 `File` 放入全局状态或持久化草稿。
- 方案字母通过数组索引计算,不单独保存。
- 保留后端现有五组 FormData 字段,避免扩大 API 改动。
### 5.2 `PptUpload.vue` 改动
删除:
- `savingsFile/ciFile/iulFile`
- 三组 company/product/password 状态。
- 针对三个险种分别判断的 `watch``if/else`
- 固定 `ports` 计算属性。
新增:
- `uploadItems`
- `addItem/removeItem/updatePlanType/handleProductChange`
- `activeItems/fileCount/scenarioSummary/canAdd/canSubmit` 计算属性。
- `buildUploadFormData`,按条目顺序同步追加五组字段。
- 条目级错误映射。
保留:
- `pptApi.getRenderOptions()`
- `productsFor()` 的过滤规则。
- 当前上传接口和成功后的 `uploaded` 事件。
- 安全说明、主按钮和现有主题样式。
### 5.3 `PptGenerate.vue` 改动
当前前端场景识别只显式覆盖:
- `single_savings`
- `multi_savings_comparison`
- `savings_iul_comprehensive`
需要补齐:
- 单份重疾险/IUL`generic_single`
- 多份相同重疾险/IUL`generic_compare`
- 其他混合组合:`generic_portfolio`
模板筛选仍以服务端最终判断为准。前端只负责正确显示场景名称,避免同类型重疾险/IUL 被笼统显示为“通用方案”。
建议场景文案:
- `generic_single`:单份计划分析。
- `generic_compare`:同险种计划对比。
- `generic_portfolio`:跨险种组合方案。
### 5.4 不新增全局抽象
上传条目只在 `PptUpload.vue` 使用,不为单一页面建立新的 store、composable 或通用表单引擎。只有当组件实现后明显超过可维护范围时,才将纯数据转换函数提取到 `frontend/src/utils/ppt-upload.ts`
---
## 6. 后端实施方案
### 6.1 上传入口校验
`upload_pdfs()` 最前面增加:
1. 文件数量必须为 14。
2. `types/companies/products/passwords` 的元素数量必须与 `files` 相同。
3. `type` 必须属于 `savings/ci/iul`
4. 每个文件必须通过现有 `prepare_pdf_upload()`
5. 保司、产品、险种关系继续使用现有校验。
不改变字段名称和响应外壳。
### 6.2 原子上传
当前循环可能先保存有效文件,再发现后续文件无效。多文件比较不能默默丢失其中一份,因此应调整为:
```text
读取并校验全部文件
→ 任一失败则返回完整错误列表,不创建会话
→ 全部通过后统一写入存储
→ 创建 PptSession
```
如果实现中仍需边校验边写入,则异常时必须清理本次请求已写入的文件。优先选择“先校验、后写入”,逻辑更容易验证。
错误响应应包含可展示的结构化明细:
```json
{
"code": 4002,
"message": "部分计划书校验失败",
"data": {
"fileErrors": [
{
"index": 1,
"fileName": "方案B.pdf",
"field": "password",
"message": "PDF 密码错误"
}
]
}
}
```
如果现有统一错误工具暂不支持 `data`V1 可以保留拼接后的 `message`,但前端只能显示全局错误;结构化错误应作为本次修复的推荐实现。
### 6.3 跨文件兼容性校验前置
扩展 `/ppt/validate/<session_id>`
1. 按现有逻辑归一化所有成功或部分成功的提取结果。
2. 计算实际场景和生成模式。
3. 对多文件场景调用现有 `build_comparison_contract()`
4. 捕获 `ValueError` 并追加:
```json
{
"field": "comparison",
"severity": "error",
"message": "不同币种不能直接比较,请上传相同币种的计划书"
}
```
这样现有 `PptDataReview.vue` 会自动阻止用户进入配置生成步骤,无需新增接口。
警告规则:
- 受保人年龄或性别不一致:警告,可继续。
- 缺少共同关键年份:警告,可继续,但比较页显示待确认。
- 产品条件不完全一致:警告,不进行高低排名。
错误规则:
- 同险种比较币种不同。
- 有效计划书不足 2 份却进入比较模式。
- 有效计划书超过 4 份。
- 数据归一化失败导致某份计划书无法参与比较。
### 6.4 生成阶段最终保护
生成任务仍保留 `build_comparison_contract()` 校验,作为并发修改或绕过前端时的最终保护。
需要改善错误信息:
- `ValueError` 应转换为明确的 `validation_error``comparison_validation_error`
- 任务错误消息保留业务提示,不统一变成“生成失败”。
- 不重复实现一套与核对接口不同的比较规则。
---
## 7. 比较与渲染修复
### 7.1 场景识别
保留当前自动识别规则:
| 文件组合 | 场景 | 模式 |
|---|---|---|
| 单份储蓄险 | `single_savings` | `single` |
| 24 份储蓄险 | `multi_savings_comparison` | `compare` |
| 储蓄险 + IUL | `savings_iul_comprehensive` | `portfolio` |
| 单份其他险种 | `generic_single` | `single` |
| 24 份相同其他险种 | `generic_compare` | `compare` |
| 其他混合险种 | `generic_portfolio` | `portfolio` |
前端和后端文案可以不同,但场景判断结果必须一致。
### 7.2 储蓄险
现有能力基本可复用:
- 多产品价值走势图遍历全部产品。
- 关键年份对比表遍历全部产品。
- 每份产品生成独立保单摘要。
- 支持 24 份。
本次主要补自动化测试,避免前端开放多文件后暴露潜在回归。
### 7.3 重疾险
同类型重疾险比较不能只沿用储蓄价值指标。比较页至少应包含:
- 年缴保费。
- 缴费年期。
- 基本保额。
- 保障期。
- 重疾/早期重疾或主要保障项目数量。
- 关键保单年度的身故保障或重疾保障数据(仅在正式计划书提供时展示)。
要求:
- 24 份产品全部出现在总览表。
- 不用“回本年”“IRR”作为重疾险核心结论。
- 保障定义、赔付次数或疾病范围不一致时提示“条款口径不同,不直接排名”。
### 7.4 IUL
同类型 IUL 比较页至少应包含:
- 年缴保费、缴费年期、基本保额。
- 保证现金价值。
- 非保证现金价值。
- 保证/非保证身故保障。
- 关键年度价值。
- 正式计划书中披露的演示假设或指数账户信息。
要求:
- 明确区分保证与非保证数据。
- 不把不同演示利率条件下的非保证价值直接排名。
- 24 份产品全部进入表格或图表。
### 7.5 通用比较页面修复
当前通用 `compare``synergy` 页面主要读取 `products[0]``products[1]`。需要改为:
- 比较模式使用 2×2 指标卡或全量表格,遍历最多 4 份产品。
- 同险种比较不使用“协同关系”页面;“协同”只属于跨险种组合模式。
- 渲染器优先消费 `deck.comparison.products` 中已计算的比较指标,避免在页面构建器里重复计算。
- 缺失数据统一显示“待正式计划书确认”,不得补零或推算。
### 7.6 模板策略
- 多储蓄险继续优先使用 `multi_savings_comparison.pptx`
- 同类型重疾险/IUL 暂时复用对应险种的现有 business 模板,不强制新增 PPTX 模板文件。
- 场景页面清单由 `scenarioSlides` 保证,模板只提供母版和视觉。
- 如果复用模板后出现布局不足,再单独新增 `generic_compare` 场景模板;不作为本次修复的前置条件。
---
## 8. 文件级改动清单
| 文件 | 改动 |
|---|---|
| `frontend/src/pages/components/ppt/PptUpload.vue` | 固定三槽位改为 14 个动态条目;新增场景摘要、条目级错误和响应式布局 |
| `frontend/src/pages/components/ppt/PptGenerate.vue` | 补齐通用单份、同险种比较和跨险种组合的场景识别与文案 |
| `api/insurance/ppt/routes.py` | 上传数量/数组/type 校验;原子上传;核对接口增加跨文件兼容性校验 |
| `api/insurance/ppt/comparison.py` | 必要时补充按险种输出的比较指标;保持单一校验规则来源 |
| `api/insurance/generation/celery_tasks.py` | 将比较校验异常转换为明确任务错误;继续保留最终保护 |
| `api/insurance/ppt/scripts/fast_pptx_renderer.py` | 通用比较遍历全部产品;区分 compare 与 portfolio按险种展示指标 |
| `tests/ppt_multi_upload_route_test.py` | 新增多文件上传、数量、数组对齐和原子失败测试 |
| `tests/ppt_comparison_test.py` | 新增场景、币种、险种、人数条件和 4 份上限测试 |
| `tests/ppt_multi_render_test.py` | 新增 2/4 份储蓄险、重疾险、IUL 的 PPTX 渲染回归测试 |
不需要修改:
- `api/insurance/models/ppt_session.py`
- 数据库迁移
- `frontend/src/utils/ppt-api.ts` 的上传接口路径
- BaoDan `main.py`
---
## 9. 实施阶段
### Phase 1后端保护与测试基线
- [ ] 为当前比较场景识别和上限补单元测试。
- [ ] 上传接口增加 14 份限制。
- [ ] 校验五组数组长度。
- [ ] 校验险种白名单。
- [ ] 实现多文件原子成功/失败。
- [ ] 保证单文件上传兼容现有调用方式。
验收:
- 单文件原有用例通过。
- 两份同类型文件可创建一个会话。
- 第 5 份在读取和解析前被拒绝。
- 任一文件校验失败时不创建会话,不遗留本次上传文件。
### Phase 2前端动态条目
- [ ] 使用 `uploadItems` 替换固定三组状态。
- [ ] 实现新增、删除、文件替换和字段联动。
- [ ] 实现场景摘要和动态按钮文案。
- [ ] 实现条目级错误。
- [ ] 完成宽屏、中屏、移动端布局。
- [ ] 保留失败后的用户输入,成功后清除密码。
验收:
- 可连续添加 4 份储蓄险。
- 可添加储蓄险 + IUL 组合。
- 删除中间条目后剩余内容不串位。
- 修改险种后不保留不兼容产品。
- 手机宽度下无横向溢出。
### Phase 3核对阶段兼容性校验
- [ ] `/validate` 归一化全部有效提取结果。
- [ ] 同险种比较调用现有比较契约校验。
- [ ] 将跨文件错误/警告加入现有 issues。
- [ ] 确认 `PptDataReview` 能阻止错误状态继续。
验收:
- 两份同险种不同币种时,在数据核对步骤阻止继续。
- 年龄或性别不同只显示警告。
- 修改提取数据并保存后会重新执行跨文件校验。
### Phase 4全险种多产品渲染
- [ ] 验证 24 份储蓄险现有页面。
- [ ] 重疾险比较使用保障类指标。
- [ ] IUL 比较区分保证/非保证指标。
- [ ] 通用比较页面遍历全部产品。
- [ ] 同险种比较移除不合适的协同页。
- [ ] 比较页优先读取 `deck.comparison`
验收:
- 4 份产品名称均能在成品中检索到。
- 重疾险页面不出现储蓄险专属 IRR/回本结论。
- IUL 保证与非保证数据有明确标识。
- 组合场景仍保留协同与现金流页面。
### Phase 5回归与交付
- [ ] 后端 PPT 相关测试全部通过。
- [ ] 前端 `lint`、TypeScript 检查和生产构建通过。
- [ ] 使用真实或脱敏样本完成端到端验证。
- [ ] 打开生成的 PPTX 检查页数、产品覆盖、表格溢出和中文字体。
- [ ] 验证单文件流程未受影响。
- [ ] 验证密码 PDF、多文件部分失败和任务重试。
---
## 10. 测试矩阵
### 10.1 上传与接口
| 用例 | 预期 |
|---|---|
| 1 份储蓄险 | 上传成功,进入单份分析 |
| 2 份储蓄险 | 上传成功,进入多储蓄险比较 |
| 4 份同类型文件 | 上传成功 |
| 5 份文件 | 上传前被后端拒绝 |
| 2 份文件但只有 1 个 type | 参数错误,不创建会话 |
| type 为未知值 | 参数错误 |
| 第 2 份 PDF 密码错误 | 整体失败,不创建残缺会话 |
| 第 2 份不是 PDF | 整体失败 |
| 产品与险种不匹配 | 整体失败,指出具体文件 |
### 10.2 场景与兼容性
| 文件组合 | 预期 |
|---|---|
| 储蓄险 × 2 | `multi_savings_comparison / compare` |
| 重疾险 × 2 | `generic_compare / compare` |
| IUL × 4 | `generic_compare / compare` |
| 储蓄险 + IUL | `savings_iul_comprehensive / portfolio` |
| 重疾险 + IUL | `generic_portfolio / portfolio` |
| 同险种不同币种 | 核对步骤错误 |
| 同险种不同年龄 | 核对步骤警告 |
| 同险种共同年份不足 | 警告并显示缺失值,不伪造数据 |
### 10.3 前端交互
| 用例 | 预期 |
|---|---|
| 初始进入 | 显示 1 个空条目 |
| 连续点击添加 | 最多出现 4 个条目 |
| 删除方案 B | C、D 重新编号,数据不串位 |
| 选择产品 | 自动回填保司 |
| 修改保司 | 不兼容产品被清空 |
| 修改险种 | 不兼容产品被清空 |
| 上传失败后重试 | 文件及配置仍保留 |
| 上传进行中 | 所有会改变请求内容的操作被禁用 |
| 手机端 | 单列,无横向滚动,主按钮全宽 |
### 10.4 成品验证
- 每份有效产品至少有一个产品名称或摘要落点。
- 对比表行数/列数与产品数量一致。
- 关键年份缺失时显示待确认,不显示虚假 0。
- 4 产品页面无文本越界、重叠或被裁切。
- 生成结果仍能进入预览、编辑、重新生成和下载流程。
---
## 11. 验收标准
功能验收:
1. 用户可以在现有上传步骤添加 14 份 PDF且同一险种可重复添加。
2. 每份文件的险种、保司、产品、密码不会与其他文件串位。
3. 同险种 24 份计划书在核对阶段完成比较条件校验。
4. 生成的 PPT 包含全部参与比较的产品,不只展示前两个。
5. 储蓄险、重疾险、IUL 使用与险种相符的比较口径。
6. 不同险种进入组合模式,不做直接排名。
7. 任何一份上传校验失败时不创建残缺会话。
8. 原单文件上传、解析、核对、生成和下载流程保持可用。
质量验收:
- 后端新增测试全部通过,现有 PPT 生命周期测试无回归。
- 前端生产构建和 lint 通过。
- 桌面、平板和手机布局均可完成全部操作。
- 错误能定位到具体文件或比较条件。
- 不新增数据库迁移、全局状态库或重复 API。
---
## 12. 风险与控制措施
| 风险 | 影响 | 控制措施 |
|---|---|---|
| 多文件解析时间线性增加 | 用户等待变长 | 保留现有逐文件进度;后续再按既有并发方案优化,不与本次 UI 修复绑定 |
| 一份失败导致比较不完整 | 生成错误结论 | 上传阶段原子失败;解析失败在核对阶段作为错误阻止继续 |
| 前后端场景判断漂移 | 模板筛选与实际生成不一致 | 后端作为最终真相;前端只做说明,并补齐与后端同名场景 |
| 4 产品表格拥挤 | PPT 文本溢出 | 采用全量表格或 2×2 卡片;加入真实 PPTX 渲染测试 |
| 不同险种误做排名 | 业务和合规风险 | `compare` 强制同险种;混合险种只进入 `portfolio` |
| 不同币种金额误比 | 错误销售结论 | 核对步骤直接阻断同险种横比 |
| 密码残留 | 敏感信息风险 | 不持久化密码;成功后立即清空;日志不得记录密码 |
---
## 13. 工作量建议
| 阶段 | 预计工作量 |
|---|---:|
| 后端入口保护与路由测试 | 0.51 人日 |
| 前端动态上传条目与响应式布局 | 11.5 人日 |
| 跨文件核对校验 | 0.5 人日 |
| 重疾险/IUL/4 产品渲染完善 | 1.52 人日 |
| 回归、真实样本和 PPTX 视觉检查 | 1 人日 |
| 合计 | 4.56 人日 |
建议先完成 Phase 13确保“能正确上传且不能错误生成”再完成 Phase 4 的全险种成品表达。不要只上线前端多选控件。
---
## 14. 完成定义
只有同时满足以下条件,才能将该功能标记为完成:
```text
页面能添加同类型多份文件
AND 后端能原子接收 14 份
AND 解析结果全部进入核对
AND 比较条件在生成前验证
AND 成品展示全部参与产品
AND 单文件和组合场景没有回归
```