2026-07-31 14:10:24 +08:00
|
|
|
|
# 保险智能客服系统 PPT 与海报优化修复计划书
|
|
|
|
|
|
|
2026-07-31 18:38:53 +08:00
|
|
|
|
> 版本:v1.1
|
2026-07-31 14:10:24 +08:00
|
|
|
|
> 日期:2026-07-31
|
|
|
|
|
|
> 范围:PPT 生成、海报生成、保司管理、产品管理、PPT 模板管理、文案模板管理
|
|
|
|
|
|
> 原则:先恢复可用性,再统一配置与数据口径,最后补齐管理能力;仅修改 `api/insurance/`、`frontend/`、`tests/` 和自定义迁移,不修改 BaoDan 基座业务文件。
|
|
|
|
|
|
|
|
|
|
|
|
## 1. 执行摘要
|
|
|
|
|
|
|
2026-07-31 18:38:53 +08:00
|
|
|
|
本轮需求应拆成四个层次推进:
|
2026-07-31 14:10:24 +08:00
|
|
|
|
|
|
|
|
|
|
1. **P0 恢复 PPT 生成**:当前 PPT 一直生成不出来存在已确认的代码级直接根因。`generate_ppt_task()` 在异常捕获块之前访问未定义变量 `task.workspace_id`,Worker 领取任务后会立即抛出 `NameError`,任务还可能停留在 `running`。
|
2026-07-31 18:38:53 +08:00
|
|
|
|
2. **P0 让所选 PPT 模板真正生效**:当前 Worker 能正确解析 `templateId` 和源模板路径,但渲染器随后删除源 PPTX 的全部幻灯片,再用统一的硬编码构建器重建页面。三个内置模板虽然分别包含 20、17、18 张完整设计页,实际成品却变成 12、10、15 张统一深蓝基础样式。
|
|
|
|
|
|
3. **P1 统一生成策略与字段校验**:取消用户侧脱敏开关,改由保司和产品后台配置;新增 Logo 开关;补齐吸烟状态、币种和三个待确认字段;把利益演示和退保提取从阻断错误降级为非阻断警告。
|
|
|
|
|
|
4. **P1/P2 完善海报与后台管理**:按险种和单图/长图建立字段画像;让 PPT 模板场景可扩展;为保司、产品、PPT 模板和文案模板提供安全删除能力。
|
2026-07-31 14:10:24 +08:00
|
|
|
|
|
|
|
|
|
|
本计划不建议把所有问题一次性塞进一个大版本。建议按 P0 热修、P1 数据与策略、P1 海报、P2 管理能力四个可独立验收的版本交付。
|
|
|
|
|
|
|
|
|
|
|
|
## 2. 审计方法与成功标准
|
|
|
|
|
|
|
|
|
|
|
|
本计划基于当前仓库代码、迁移、测试和部署配置形成,不把推测当成结论。
|
|
|
|
|
|
|
|
|
|
|
|
### 2.1 已执行的检查
|
|
|
|
|
|
|
|
|
|
|
|
- 对 PPT/海报前后端、后台管理、数据模型、迁移和测试进行了关键词与调用链审计。
|
|
|
|
|
|
- 对 PPT 路由、校验、归一化、渲染器、Celery 任务执行了 Python 语法检查,语法检查通过。
|
|
|
|
|
|
- 执行相关测试:
|
|
|
|
|
|
- 结果:`10 passed, 1 failed, 1 skipped`。
|
|
|
|
|
|
- 唯一失败为提取器测试 mock 仍返回旧签名,实际代码现在要求 `_extract_pdf_text()` 返回 `(pdf_text, page_qualities)`,说明测试与新接口不同步。
|
|
|
|
|
|
- 检查本地 SQLite:
|
|
|
|
|
|
- 迁移历史只到 `migrate_016`。
|
|
|
|
|
|
- 当前代码中的统一任务表和工作区字段由 `migrate_022` 创建。
|
|
|
|
|
|
- 这不是线上环境已确认事实,但属于必须上线前核验的高风险环境差异。
|
|
|
|
|
|
- 检查 Docker 配置:
|
|
|
|
|
|
- Worker 已监听 `insurance` 队列。
|
|
|
|
|
|
- API 与 Worker 已配置相同 `INSURANCE_STORAGE_ROOT`。
|
|
|
|
|
|
- 自定义镜像显式安装并验证 `python-pptx`。
|
|
|
|
|
|
|
|
|
|
|
|
### 2.2 总体验收目标
|
|
|
|
|
|
|
|
|
|
|
|
- PPT 正常任务在约定时限内从 `queued → running → done`,失败任务必定进入 `failed`,不存在永久 `running`。
|
|
|
|
|
|
- PPT 和海报页面不再出现用户脱敏开关;最终是否脱敏完全由后台当前配置快照决定。
|
|
|
|
|
|
- Logo 关闭后,PPT、海报预览、导出文件和 AI Prompt 均不出现 Logo。
|
|
|
|
|
|
- 利益演示或退保提取缺失/异常不再阻止进入生成步骤。
|
|
|
|
|
|
- 单图与长图、不同险种使用各自字段规则,不因无关字段缺失阻塞。
|
|
|
|
|
|
- 自定义场景能被创建、选择、保存和用于模板生成,不被固定场景校验拒绝。
|
2026-07-31 18:38:53 +08:00
|
|
|
|
- 选择三个内置 PPT 模板中的任意一个时,输出必须保留该模板的母版、页面结构、背景、图片、图表、表格和排版特征,不得退回统一基础样式。
|
|
|
|
|
|
- 任务结果可追溯实际使用的模板资产版本和 SHA-256;“已应用模板”不能只代表模板 ID 校验通过。
|
2026-07-31 14:10:24 +08:00
|
|
|
|
- 四类后台对象均可删除或安全下架,历史记录和已生成文件仍可追溯。
|
|
|
|
|
|
|
|
|
|
|
|
## 3. 当前问题与结论
|
|
|
|
|
|
|
|
|
|
|
|
| 编号 | 需求 | 当前实现 | 结论 | 优先级 |
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
| 1 | 后台控制脱敏 | PPT、海报前台均有 `useMaskedData` 开关;后台只有脱敏展示名,没有启停开关 | 必须移除前台控制,并由服务端解析后台策略 | P1 |
|
|
|
|
|
|
| 2 | PPT 核验字段与非阻断板块 | 核验页缺吸烟状态和币种编辑项;后端仍把部分利益/退保问题标成 `error` | 新增字段并重构错误分级 | P1 |
|
|
|
|
|
|
| 3 | PPT 一直无法生成 | Worker 使用未定义的 `task`,且位于 `try` 之前 | 已确认 P0 直接根因 | P0 |
|
|
|
|
|
|
| 4 | 后台控制 Logo | 有 Logo 上传/主 Logo,但无是否启用字段 | 增加保司级 `logo_enabled`,服务端强制执行 | P1 |
|
|
|
|
|
|
| 5 | 海报按险种和版式取字段 | 当前主要按字段是否存在展示,单图/长图只改变布局高度 | 需要字段画像和内容预算层 | P1 |
|
|
|
|
|
|
| 6 | PPT 生成场景可添加 | 前端固定 3 个场景;后端探测器固定 6 个代码;自定义值会被匹配校验拒绝 | 不能只做 `allow-create`,需拆分业务场景和计算模式 | P1 |
|
|
|
|
|
|
| 7 | 四类后台删除 | 仅文案模板已有硬删除;保司、产品、PPT 模板没有删除接口 | 增加软删除和引用检查 | P2 |
|
2026-07-31 18:38:53 +08:00
|
|
|
|
| 8 | 三个内置 PPT 模板选中后仍使用基础样式 | 模板 ID 和文件路径已进入 Worker,但渲染器删除源模板全部页面,并由统一 `SLIDE_BUILDERS/THEMES` 重建 | 不是缓存或前端传参主因;必须新增“克隆并原位编辑”渲染模式 | P0 |
|
|
|
|
|
|
| 9 | 后台上传模板可能无法稳定保存或追溯版本 | `source_template_asset_id` 仅 `VARCHAR(50)`,而 `stored://{templateId}/{uuid}.pptx` 可超过 50 字符;任务也未固化资产版本/hash | 扩容字段并建立版本化模板资产与任务快照 | P0/P1 |
|
2026-07-31 14:10:24 +08:00
|
|
|
|
|
|
|
|
|
|
## 4. P0:PPT 无法生成专项修复
|
|
|
|
|
|
|
|
|
|
|
|
### 4.1 已确认直接根因
|
|
|
|
|
|
|
|
|
|
|
|
调用链:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
POST /insurance/ppt/generate/{sessionId}
|
|
|
|
|
|
→ task_service.create_task()
|
|
|
|
|
|
→ generate_ppt_task.apply_async(queue="insurance")
|
|
|
|
|
|
→ Worker _claim_task(taskId)
|
|
|
|
|
|
→ 访问 task.workspace_id
|
|
|
|
|
|
→ NameError: name 'task' is not defined
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
证据:
|
|
|
|
|
|
|
|
|
|
|
|
- `api/insurance/generation/celery_tasks.py:266` 使用 `task.workspace_id`。
|
|
|
|
|
|
- 同一函数此前没有查询或赋值 `task`。
|
|
|
|
|
|
- `_claim_task()` 的返回类型是 `bool`,不会返回任务对象。
|
|
|
|
|
|
- 该代码在 `try` 之前执行,因此现有失败回写逻辑捕获不到异常。
|
|
|
|
|
|
- `git blame` 显示这段状态同步代码由 2026-07-30 的提交 `5f78598b3` 引入,属于明确回归点。
|
|
|
|
|
|
|
|
|
|
|
|
实际影响:
|
|
|
|
|
|
|
|
|
|
|
|
- Worker 任务本身异常退出。
|
|
|
|
|
|
- 任务数据库状态已由 `_claim_task()` 改为 `running`,但不会被改为 `failed`。
|
|
|
|
|
|
- 前端可能持续轮询或仅看到“生成中”。
|
|
|
|
|
|
- 过期任务只能等待启动时的 stale recovery 才可能恢复,用户感知为“一直生成不出来”。
|
|
|
|
|
|
|
|
|
|
|
|
### 4.2 P0 修复方案
|
|
|
|
|
|
|
|
|
|
|
|
1. 在 `generate_ppt_task()` 成功领取任务后,先通过 `GenerationTask.query.get(task_id)` 获取任务。
|
|
|
|
|
|
2. 把任务读取、工作区状态同步和 `_execute_ppt_generate()` 全部放入同一个 `try`。
|
|
|
|
|
|
3. 若任务记录不存在,显式结束,不继续执行。
|
|
|
|
|
|
4. 所有异常统一:
|
|
|
|
|
|
- 任务状态改为 `failed`;
|
|
|
|
|
|
- `error_code=generate_error`;
|
|
|
|
|
|
- 同步 `PptSession.status/error/workflow_step`;
|
|
|
|
|
|
- 记录带 `task_id/session_id/stage` 的结构化日志。
|
|
|
|
|
|
5. 不使用 Celery 声明上的 `max_retries` 伪装重试能力:
|
|
|
|
|
|
- 当前函数没有调用 `self.retry()`;
|
|
|
|
|
|
- 第一阶段直接移除误导性重试参数,或明确仅对瞬时渲染异常调用受控重试;
|
|
|
|
|
|
- 数据校验错误不得重试。
|
|
|
|
|
|
6. 增加一次性运维脚本或管理命令,把该回归期间超过阈值的 `running` PPT 任务改为 `failed/stale_task`,同步工作区状态。
|
|
|
|
|
|
|
|
|
|
|
|
推荐的最小修复结构:
|
|
|
|
|
|
|
|
|
|
|
|
```python
|
|
|
|
|
|
if not _claim_task(task_id):
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
task = GenerationTask.query.get(task_id)
|
|
|
|
|
|
if not task:
|
|
|
|
|
|
return
|
|
|
|
|
|
sync_workspace_to_generating(task.workspace_id)
|
|
|
|
|
|
_execute_ppt_generate(task_id)
|
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
|
mark_task_and_workspace_failed(task_id, exc)
|
|
|
|
|
|
raise
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 4.3 必须同时核验的环境项
|
|
|
|
|
|
|
|
|
|
|
|
直接根因修复后,仍需在部署环境执行以下检查,防止第二层问题被遮住:
|
|
|
|
|
|
|
|
|
|
|
|
1. 数据库迁移:
|
|
|
|
|
|
- `db_migration_history` 至少包含 `migrate_022`~`migrate_026`。
|
|
|
|
|
|
- `insurance_generation_tasks` 表存在。
|
|
|
|
|
|
- `insurance_ppt_sessions` 包含 `workflow_step/latest_task_id/draft_revision` 等字段。
|
|
|
|
|
|
2. Worker:
|
|
|
|
|
|
- `celery inspect registered` 能看到 `insurance.generate_ppt`。
|
|
|
|
|
|
- Worker 命令包含 `-Q ...,insurance`。
|
|
|
|
|
|
3. 共享存储:
|
|
|
|
|
|
- API 和 Worker 的 `INSURANCE_STORAGE_ROOT` 完全一致。
|
|
|
|
|
|
- Worker 可写 `outputs/ppt/{userId}/{taskId}`。
|
|
|
|
|
|
- API 可读 Worker 生成的同一路径。
|
|
|
|
|
|
4. 渲染依赖:
|
|
|
|
|
|
- Worker 容器内 `import pptx` 成功。
|
|
|
|
|
|
- `fast_pptx_renderer.py` 存在。
|
|
|
|
|
|
- 使用 Worker 实际 Python 解释器完成一次最小 DeckContract 冒烟测试。
|
|
|
|
|
|
|
|
|
|
|
|
### 4.4 P0 测试
|
|
|
|
|
|
|
|
|
|
|
|
新增测试:
|
|
|
|
|
|
|
|
|
|
|
|
- `test_generate_ppt_task_loads_task_before_workspace_sync`
|
|
|
|
|
|
- `test_generate_ppt_task_exception_marks_task_failed`
|
|
|
|
|
|
- `test_generate_ppt_task_exception_syncs_workspace_failed`
|
|
|
|
|
|
- `test_generate_ppt_task_missing_task_exits_cleanly`
|
|
|
|
|
|
- `test_generate_ppt_task_success_reaches_done`
|
|
|
|
|
|
- `test_stale_running_generate_task_is_recovered`
|
|
|
|
|
|
- API/Worker/共享存储容器级冒烟测试
|
|
|
|
|
|
|
|
|
|
|
|
修复现有失败测试:
|
|
|
|
|
|
|
|
|
|
|
|
- 更新 `tests/ppt_task_lifecycle_test.py` 中 `_extract_pdf_text` mock,使其返回 `("文本", page_qualities)`。
|
|
|
|
|
|
- 该失败不是当前 PPT 生成直接根因,但必须清零后才允许发布。
|
|
|
|
|
|
|
|
|
|
|
|
### 4.5 P0 验收
|
|
|
|
|
|
|
|
|
|
|
|
- 连续生成 20 次 PPT,无永久 `queued/running`。
|
|
|
|
|
|
- 人工制造渲染异常,30 秒内前端显示可理解的失败原因。
|
|
|
|
|
|
- 重启 Worker 后,旧任务不会覆盖新任务状态。
|
|
|
|
|
|
- 生成文件可由 API 下载,PPTX 可被 PowerPoint/WPS 打开。
|
|
|
|
|
|
|
|
|
|
|
|
## 5. 后台统一脱敏策略
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 当前范围说明
|
|
|
|
|
|
|
|
|
|
|
|
现有 `masking.py` 实际处理的是**保司名称和产品名称替换**,不是完整客户隐私脱敏系统。后台已有:
|
|
|
|
|
|
|
|
|
|
|
|
- `PptCompany.masked_display_name`
|
|
|
|
|
|
- `PptProduct.masked_display_name`
|
|
|
|
|
|
|
|
|
|
|
|
前端 PPT 和海报却让用户通过 `useMaskedData` 决定是否执行,服务端也信任该请求参数。这会导致同一产品由不同用户生成不同口径,且用户可以绕过后台策略。
|
|
|
|
|
|
|
|
|
|
|
|
本轮建议明确:
|
|
|
|
|
|
|
|
|
|
|
|
- 保司开关只控制保司名称是否替换为保司脱敏展示名。
|
|
|
|
|
|
- 产品开关只控制产品名称是否替换为产品脱敏展示名。
|
|
|
|
|
|
- 客户姓名、证件号、手机号等个人信息脱敏属于另一套 PII 规则;若业务也要求,应另立范围,不能误以为当前名称替换已经覆盖。
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 数据模型
|
|
|
|
|
|
|
|
|
|
|
|
在新迁移中增加:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
insurance_ppt_companies.masking_enabled BOOLEAN NOT NULL DEFAULT FALSE
|
|
|
|
|
|
insurance_ppt_companies.logo_enabled BOOLEAN NOT NULL DEFAULT TRUE
|
|
|
|
|
|
insurance_ppt_products.masking_enabled BOOLEAN NOT NULL DEFAULT FALSE
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
不设计保司与产品之间的复杂继承关系:
|
|
|
|
|
|
|
|
|
|
|
|
- 保司名称是否脱敏,看保司自己的开关。
|
|
|
|
|
|
- 产品名称是否脱敏,看产品自己的开关。
|
|
|
|
|
|
- 多产品 PPT 对每个产品独立处理。
|
|
|
|
|
|
|
|
|
|
|
|
这样每个后台开关的含义唯一,也符合“一键设置”的要求。
|
|
|
|
|
|
|
|
|
|
|
|
### 5.3 服务端策略
|
|
|
|
|
|
|
|
|
|
|
|
新增统一解析函数,例如:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
resolve_generation_brand_policy(company, products)
|
|
|
|
|
|
→ companyNameMasked
|
|
|
|
|
|
→ productNameMaskedById
|
|
|
|
|
|
→ logoEnabled
|
|
|
|
|
|
→ policyVersion
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
规则:
|
|
|
|
|
|
|
|
|
|
|
|
1. PPT/海报 API 忽略客户端传入的 `useMaskedData`。
|
|
|
|
|
|
2. 任务创建时从数据库读取当前配置,写入任务快照。
|
|
|
|
|
|
3. Worker 只使用快照,不在执行中途重新读取,避免管理员修改设置导致同一任务前后不一致。
|
|
|
|
|
|
4. 历史记录保存最终策略布尔值和策略版本,不保存无必要的原始敏感文本。
|
|
|
|
|
|
5. 用户自上传资料没有后台产品配置时:
|
|
|
|
|
|
- 默认不自动伪造脱敏产品名;
|
|
|
|
|
|
- 若能匹配到保司,则仍执行保司名称策略;
|
|
|
|
|
|
- 产品名脱敏需先由管理员建立映射,或在用户资料模型中另加管理员审核配置。
|
|
|
|
|
|
|
|
|
|
|
|
### 5.4 前后端改造
|
|
|
|
|
|
|
|
|
|
|
|
后台:
|
|
|
|
|
|
|
|
|
|
|
|
- 保司管理增加“名称脱敏”开关。
|
|
|
|
|
|
- 产品管理增加“名称脱敏”开关。
|
|
|
|
|
|
- 开启但未填写 `maskedDisplayName` 时禁止保存,避免运行时采用不可审计的临时规则。
|
|
|
|
|
|
|
|
|
|
|
|
用户端:
|
|
|
|
|
|
|
|
|
|
|
|
- 删除 `PptGenerate.vue` 的“数据脱敏”开关。
|
|
|
|
|
|
- 删除 `PosterProductPanel.vue` 的“使用脱敏数据”开关。
|
|
|
|
|
|
- 删除草稿、请求类型和自动保存中的 `useMaskedData`。
|
|
|
|
|
|
- 可只读显示“后台策略:保司名已脱敏/产品名未脱敏”,但不能由用户修改。
|
|
|
|
|
|
|
|
|
|
|
|
兼容:
|
|
|
|
|
|
|
|
|
|
|
|
- API 在一个版本内可接收旧 `useMaskedData` 字段但完全忽略,并记录一次弃用日志。
|
|
|
|
|
|
- 下一版本从接口文档和类型中删除。
|
|
|
|
|
|
|
|
|
|
|
|
### 5.5 验收矩阵
|
|
|
|
|
|
|
|
|
|
|
|
| 保司开关 | 产品开关 | 期望结果 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| 关 | 关 | 保司名、产品名均显示真实配置名 |
|
|
|
|
|
|
| 开 | 关 | 只替换保司名 |
|
|
|
|
|
|
| 关 | 开 | 只替换产品名 |
|
|
|
|
|
|
| 开 | 开 | 两者均替换 |
|
|
|
|
|
|
|
|
|
|
|
|
以上四种组合需分别验证 PPT、海报预览、最终导出、历史快照和 AI Prompt。
|
|
|
|
|
|
|
|
|
|
|
|
## 6. Logo 启停策略
|
|
|
|
|
|
|
|
|
|
|
|
### 6.1 当前问题
|
|
|
|
|
|
|
|
|
|
|
|
系统已有 Logo 上传、排序、主 Logo 和 `logoUrl`,但没有启停配置。PPT 的 DeckContract 和海报产品快照会直接携带 `logoUrl`。
|
|
|
|
|
|
|
|
|
|
|
|
仅在前端隐藏 Logo 不足够,因为:
|
|
|
|
|
|
|
|
|
|
|
|
- Worker 仍可能把 Logo 渲染进最终文件。
|
|
|
|
|
|
- 海报 Prompt 或快照仍可能携带 Logo。
|
|
|
|
|
|
- 用户可以直接调用 API 绕过页面。
|
|
|
|
|
|
|
|
|
|
|
|
### 6.2 改造方案
|
|
|
|
|
|
|
|
|
|
|
|
1. 保司模型新增 `logo_enabled`,默认 `TRUE`,保证升级后现有行为不突变。
|
|
|
|
|
|
2. 保司管理增加“生成物展示 Logo”开关。
|
|
|
|
|
|
3. 统一品牌策略解析时:
|
|
|
|
|
|
- `logo_enabled=false` → 输出 `logoUrl=""`、`logoAssetId=null`;
|
|
|
|
|
|
- 不删除已上传 Logo;
|
|
|
|
|
|
- 重新开启后继续使用原主 Logo。
|
|
|
|
|
|
4. PPT:
|
|
|
|
|
|
- DeckContract 中不携带 Logo。
|
|
|
|
|
|
- 模板占位符没有 Logo 时隐藏,而不是显示破图或空白边框。
|
|
|
|
|
|
5. 海报:
|
|
|
|
|
|
- HTML 画布和最终导出均不渲染 Logo 图层。
|
|
|
|
|
|
- AI 图片 Prompt 继续禁止模型生成伪 Logo。
|
|
|
|
|
|
6. 多保司比较:
|
|
|
|
|
|
- 每个保司分别按自身开关处理;
|
|
|
|
|
|
- 不因其中一家关闭而隐藏其他保司 Logo。
|
|
|
|
|
|
|
|
|
|
|
|
### 6.3 验收
|
|
|
|
|
|
|
|
|
|
|
|
- 关闭 Logo 后,新生成 PPT/海报无 Logo。
|
|
|
|
|
|
- 历史文件不被追溯修改。
|
|
|
|
|
|
- 重新开启后恢复主 Logo。
|
|
|
|
|
|
- 无主 Logo、Logo 文件已丢失、外部 URL 失效时均能无损降级。
|
|
|
|
|
|
|
|
|
|
|
|
## 7. PPT 核验字段与校验分级
|
|
|
|
|
|
|
|
|
|
|
|
### 7.1 新增关键字段
|
|
|
|
|
|
|
|
|
|
|
|
核验页“关键字段”新增:
|
|
|
|
|
|
|
|
|
|
|
|
| 中文名 | 原始字段 | 归一化字段 | 类型 | 建议口径 |
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
|
|
|
| 吸烟状态 | `insured.smoker` | `insured.smoker` | 枚举 | `yes/no/unknown` |
|
|
|
|
|
|
| 币种 | `policy.currency` | `policy.currency` | 枚举 | ISO 4217 代码 |
|
|
|
|
|
|
| 基本计划年保费 | `policy.basic_plan_annual_premium` | `policy.basicPlanAnnualPremium` | 金额,可选 | 仅来源明确标注时填写 |
|
|
|
|
|
|
| 基本计划名义金额 | `policy.basic_sum_insured` | `policy.basicSumInsured` | 金额,可选 | 对应 Basic Sum Assured/Notional Amount |
|
|
|
|
|
|
| 首年应缴金额 | `policy.first_year_amount_due` | `policy.firstYearAmountDue` | 金额,可选 | 含折扣、征费或附加费后的首年实际应缴 |
|
|
|
|
|
|
|
|
|
|
|
|
注意:
|
|
|
|
|
|
|
|
|
|
|
|
- 需求中的“USB”应按“USD”处理;`USB` 不是货币代码,不应作为选项。
|
|
|
|
|
|
- 人民币统一保存为 `CNY`,页面可以显示“人民币(CNY)”;输入 `RMB/¥` 时归一化为 `CNY`。
|
|
|
|
|
|
- 建议常用选项:`USD、HKD、CNY、SGD、EUR、GBP`,并保留可搜索的 ISO 代码扩展能力。
|
|
|
|
|
|
- 当前 `normalizer.py` 在部分路径中把缺失币种默认成 `USD`。该行为必须移除,未知币种应为 `null/unknown` 并提示用户确认,不能伪装成已确认数据。
|
|
|
|
|
|
|
|
|
|
|
|
### 7.2 三个待判断字段的结论
|
|
|
|
|
|
|
|
|
|
|
|
仓库目前没有真实 PDF 样本,无法证明三个中文标签在所有保司计划书中含义一致。因此不能把它们直接设为所有险种必填。
|
|
|
|
|
|
|
|
|
|
|
|
推荐结论:
|
|
|
|
|
|
|
|
|
|
|
|
1. **基本计划年保费**
|
|
|
|
|
|
- 与现有 `annual_premium` 很可能重叠。
|
|
|
|
|
|
- 只有计划书同时出现“基本计划年保费”和“总年保费/附加保障保费”时,才应保存为独立字段。
|
|
|
|
|
|
- 普通储蓄险只有一个年缴金额时,继续使用 `annual_premium`。
|
|
|
|
|
|
2. **基本计划名义金额**
|
|
|
|
|
|
- 可映射到已有提取结构中的 `basic_sum_insured`。
|
|
|
|
|
|
- 对 CI/IUL 更常见;储蓄险可能不存在或不适合作为主要销售指标。
|
|
|
|
|
|
- 建议作为条件字段,不设全局必填。
|
|
|
|
|
|
3. **首年应缴金额**
|
|
|
|
|
|
- 不等同于年缴保费,可能包含首年折扣、保费征费、附加费。
|
|
|
|
|
|
- 当前 `total_premium_with_levy` 只能作为候选来源,不能无条件等同。
|
|
|
|
|
|
- 建议新增独立字段,并保留 `source_label/source_page`。
|
|
|
|
|
|
|
|
|
|
|
|
落地前需要业务方提供至少以下脱敏样本:
|
|
|
|
|
|
|
|
|
|
|
|
- 储蓄险 3 份,至少 2 家保司;
|
|
|
|
|
|
- 重疾险 3 份,至少 2 家保司;
|
|
|
|
|
|
- IUL 3 份,至少 2 家保司;
|
|
|
|
|
|
- 至少 1 份含征费/折扣;
|
|
|
|
|
|
- 至少 1 份明确出现 Basic Sum Assured/Notional Amount。
|
|
|
|
|
|
|
|
|
|
|
|
样本确认后建立“原文标签 → 规范字段”映射表和金标测试,未确认前三个字段均按可选字段开发。
|
|
|
|
|
|
|
|
|
|
|
|
### 7.3 必填与非阻断规则
|
|
|
|
|
|
|
|
|
|
|
|
建议将校验分为三级:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
BLOCKER:无法确定产品或生成基本事实,禁止进入生成
|
|
|
|
|
|
WARNING:可能影响部分页面准确性,允许继续
|
|
|
|
|
|
INFO:展示提示,不影响流程
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
全局 BLOCKER:
|
|
|
|
|
|
|
|
|
|
|
|
- 产品名称缺失;
|
|
|
|
|
|
- 险种无法确定;
|
|
|
|
|
|
- 被保人年龄缺失或非法;
|
|
|
|
|
|
- 币种缺失;
|
|
|
|
|
|
- 生成所需的核心金额全部缺失。
|
|
|
|
|
|
|
|
|
|
|
|
按险种 BLOCKER:
|
|
|
|
|
|
|
|
|
|
|
|
- 储蓄险:年缴保费、缴费年期。
|
|
|
|
|
|
- 重疾险:保额、年缴保费、缴费年期。
|
|
|
|
|
|
- IUL:保额;目标/计划保费至少有一个有效值。
|
|
|
|
|
|
|
|
|
|
|
|
吸烟状态:
|
|
|
|
|
|
|
|
|
|
|
|
- 作为关键字段展示。
|
|
|
|
|
|
- `yes/no` 已确认时正常通过。
|
|
|
|
|
|
- `unknown` 时产生 WARNING,允许继续;避免计划书确实未载明时形成死锁。
|
|
|
|
|
|
|
|
|
|
|
|
以下全部改为 WARNING,不再阻断:
|
|
|
|
|
|
|
|
|
|
|
|
- 利益演示行不足;
|
|
|
|
|
|
- 利益演示年份不连续;
|
|
|
|
|
|
- 利益演示金额关系异常;
|
|
|
|
|
|
- 退保提取缺失;
|
|
|
|
|
|
- 退保提取年份不连续;
|
|
|
|
|
|
- 利益/退保来源页缺失。
|
|
|
|
|
|
|
|
|
|
|
|
生成策略:
|
|
|
|
|
|
|
|
|
|
|
|
- 数据缺失的板块不生成对应页面,或明确显示“待正式计划书确认”。
|
|
|
|
|
|
- 不得用 `0`、默认 `USD` 或 AI 推测值填补缺失事实。
|
|
|
|
|
|
- 用户完成生成后可以回到核验步骤修改数据,再创建新版本。
|
|
|
|
|
|
|
|
|
|
|
|
### 7.4 前端交互
|
|
|
|
|
|
|
|
|
|
|
|
- 关键字段显示必填标识和来源页。
|
|
|
|
|
|
- 币种使用可搜索下拉框。
|
|
|
|
|
|
- 吸烟状态使用“吸烟/不吸烟/未确认”单选或下拉。
|
|
|
|
|
|
- WARNING 使用黄色,不禁用“下一步”。
|
|
|
|
|
|
- 点击警告可定位对应字段或板块。
|
|
|
|
|
|
- 进入下一步时如仍有 WARNING,弹出一次汇总确认,不要求逐条解决。
|
|
|
|
|
|
- 结果页“返回修改”保留当前 session、模板、字段和修改日志;重新生成形成新版本,不覆盖旧版本。
|
|
|
|
|
|
|
|
|
|
|
|
### 7.5 后端改造点
|
|
|
|
|
|
|
|
|
|
|
|
- `prompts.py`:补齐三个候选字段及来源要求。
|
|
|
|
|
|
- `regex_extractor.py`:补充标签映射;未知币种不默认 USD。
|
|
|
|
|
|
- `normalizer.py`:全险种统一输出吸烟、币种及三个候选字段。
|
|
|
|
|
|
- `validator.py`:按分区和险种分级;利益/退保只返回 warning。
|
|
|
|
|
|
- `routes.py`:`validated` 只由 BLOCKER 数量决定。
|
|
|
|
|
|
- `celery_tasks.py`:生成阶段不得重新把 WARNING 当成 error 阻断。
|
|
|
|
|
|
- `PptDataReview.vue`:补字段、选项、来源和非阻断交互。
|
|
|
|
|
|
- `fast_pptx_renderer.py`:字段缺失时隐藏相关卡片,不制造默认值。
|
|
|
|
|
|
|
|
|
|
|
|
## 8. 海报按险种和单图/长图的字段画像
|
|
|
|
|
|
|
|
|
|
|
|
### 8.1 当前问题
|
|
|
|
|
|
|
|
|
|
|
|
当前海报前端主要根据 `parsedFields` 中是否存在字段决定展示;`outputMode=single/long` 主要只控制画布固定比例或自动高度。系统还没有“险种 × 版式”的字段选择层,因此会出现:
|
|
|
|
|
|
|
|
|
|
|
|
- 单图塞入过多表格和事实,信息密度过高;
|
|
|
|
|
|
- 长图仍只显示单图级摘要,信息不足;
|
|
|
|
|
|
- 储蓄险、重疾险、IUL 使用同一套卡片;
|
|
|
|
|
|
- 某险种不存在的字段被误判为缺失;
|
|
|
|
|
|
- 生成 Prompt 和 HTML 画布可能使用不同字段。
|
|
|
|
|
|
|
|
|
|
|
|
### 8.2 推荐架构
|
|
|
|
|
|
|
|
|
|
|
|
新增单一字段画像配置,不在多个组件中各写一套判断:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
PosterFieldProfile
|
|
|
|
|
|
planType: savings | ci | iul | other
|
|
|
|
|
|
outputMode: single | long
|
|
|
|
|
|
required: []
|
|
|
|
|
|
recommended: []
|
|
|
|
|
|
optional: []
|
|
|
|
|
|
forbidden: []
|
|
|
|
|
|
maxFeatureCount
|
|
|
|
|
|
milestoneYears
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
流程:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
产品资料/计划书
|
|
|
|
|
|
→ 统一事实模型
|
|
|
|
|
|
→ 按险种选择 FieldProfile
|
|
|
|
|
|
→ 按 single/long 裁剪内容
|
|
|
|
|
|
→ 同一 ViewModel 同时供 HTML 预览、AI Prompt、最终导出使用
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 8.3 字段矩阵
|
|
|
|
|
|
|
|
|
|
|
|
#### 储蓄险
|
|
|
|
|
|
|
|
|
|
|
|
单图:
|
|
|
|
|
|
|
|
|
|
|
|
- 必需:产品名、保司名、币种、年缴保费、缴费年期。
|
|
|
|
|
|
- 推荐:2~3 个已审核产品卖点、一个可核验里程碑数据。
|
|
|
|
|
|
- 可选:基本计划名义金额、保障期、投保年龄。
|
|
|
|
|
|
- 不展示:完整利益演示表、完整退保提取表、过多年度数据。
|
|
|
|
|
|
|
|
|
|
|
|
长图:
|
|
|
|
|
|
|
|
|
|
|
|
- 必需:单图必需字段。
|
|
|
|
|
|
- 推荐:投保条件、总计划保费、10/20/30 年里程碑、保证/非保证拆分、回本提示。
|
|
|
|
|
|
- 可选:首年应缴金额、提取方案、完整产品亮点。
|
|
|
|
|
|
- 利益演示不足时隐藏图表,不阻断长图生成。
|
|
|
|
|
|
|
|
|
|
|
|
#### 重疾险(CI)
|
|
|
|
|
|
|
|
|
|
|
|
单图:
|
|
|
|
|
|
|
|
|
|
|
|
- 必需:产品名、保司名、币种、基本保额。
|
|
|
|
|
|
- 推荐:年缴保费、缴费年期、核心疾病/保障层级、等待期。
|
|
|
|
|
|
- 条件字段:投保年龄、性别、吸烟状态。
|
|
|
|
|
|
- 不展示:储蓄险回本倍数、退保价值营销结论。
|
|
|
|
|
|
|
|
|
|
|
|
长图:
|
|
|
|
|
|
|
|
|
|
|
|
- 必需:单图必需字段。
|
|
|
|
|
|
- 推荐:保障项目列表、早期/严重疾病结构、额外赔付、等待期、保障期限。
|
|
|
|
|
|
- 可选:豁免、身故保障、特定疾病、年龄/吸烟条件。
|
|
|
|
|
|
- 保障列表为空时只允许简版长图,并标记 WARNING。
|
|
|
|
|
|
|
|
|
|
|
|
#### IUL
|
|
|
|
|
|
|
|
|
|
|
|
单图:
|
|
|
|
|
|
|
|
|
|
|
|
- 必需:产品名、保司名、币种、名义保额/身故保障。
|
|
|
|
|
|
- 推荐:目标保费或计划保费、缴费方式、一个指数账户核心机制。
|
|
|
|
|
|
- 条件字段:投保年龄、性别、吸烟状态。
|
|
|
|
|
|
- 不展示:未经确认的收益率承诺。
|
|
|
|
|
|
|
|
|
|
|
|
长图:
|
|
|
|
|
|
|
|
|
|
|
|
- 必需:单图必需字段。
|
|
|
|
|
|
- 推荐:目标/最低保费、指数账户、封顶率/参与率/保底机制、关键年度账户价值和退保价值。
|
|
|
|
|
|
- 可选:贷款/提取说明、费用说明、首年应缴金额。
|
|
|
|
|
|
- 所有非保证数据必须带演示口径和免责声明。
|
|
|
|
|
|
|
|
|
|
|
|
#### 其他险种
|
|
|
|
|
|
|
|
|
|
|
|
- 使用通用保守画像:产品名、保司名、保障对象、核心保障、适用人群、免责声明。
|
|
|
|
|
|
- 未建立专属画像前,不展示收益图表或推导指标。
|
|
|
|
|
|
|
|
|
|
|
|
### 8.4 单图与长图的内容预算
|
|
|
|
|
|
|
|
|
|
|
|
| 项目 | 单图 | 长图 |
|
|
|
|
|
|
|---|---:|---:|
|
|
|
|
|
|
| 主标题 | 1 | 1 |
|
|
|
|
|
|
| 副标题/正文 | 1~2 段 | 2~4 段 |
|
|
|
|
|
|
| 核心数字卡 | 2~4 个 | 4~8 个 |
|
|
|
|
|
|
| 卖点 | 2~3 个 | 3~6 个 |
|
|
|
|
|
|
| 图表 | 0~1 个 | 0~3 个 |
|
|
|
|
|
|
| 年度数据 | 1 个里程碑 | 3~6 个里程碑 |
|
|
|
|
|
|
| 免责声明 | 必须 | 必须、可更完整 |
|
|
|
|
|
|
|
|
|
|
|
|
### 8.5 改造位置
|
|
|
|
|
|
|
|
|
|
|
|
- 后端新增 `poster/field_profiles.py` 或等价配置模块。
|
|
|
|
|
|
- `product_source_resolver.py` 输出统一事实模型,不直接决定视觉字段。
|
|
|
|
|
|
- `content_builder.py` 根据画像构建 Poster ViewModel。
|
|
|
|
|
|
- `copy_generator.py` 和 `image_generator.py` 只消费 ViewModel。
|
|
|
|
|
|
- `PosterHtmlCanvas.vue` 不再自行猜测字段别名。
|
|
|
|
|
|
- `PosterSummaryCards.vue` 接收已经筛选和排序的 cards。
|
|
|
|
|
|
- 长图图表只使用服务端确认允许展示的里程碑数据。
|
|
|
|
|
|
|
|
|
|
|
|
### 8.6 海报验收
|
|
|
|
|
|
|
|
|
|
|
|
每个险种至少使用 2 份样本,分别生成单图和长图,共至少 12 个基准输出:
|
|
|
|
|
|
|
|
|
|
|
|
- 无跨险种错误字段;
|
|
|
|
|
|
- 单图无信息溢出;
|
|
|
|
|
|
- 长图缺失板块能自动隐藏;
|
|
|
|
|
|
- 数字、币种、产品名在预览和导出中一致;
|
|
|
|
|
|
- 后台脱敏和 Logo 策略一致生效;
|
|
|
|
|
|
- 不生成虚构文字、数字或 Logo。
|
|
|
|
|
|
|
|
|
|
|
|
## 9. PPT 模板“生成场景”动态化
|
|
|
|
|
|
|
|
|
|
|
|
### 9.1 为什么不能只把下拉框改成可输入
|
|
|
|
|
|
|
|
|
|
|
|
前端目前固定三个场景:
|
|
|
|
|
|
|
|
|
|
|
|
- `single_savings`
|
|
|
|
|
|
- `multi_savings_comparison`
|
|
|
|
|
|
- `savings_iul_comprehensive`
|
|
|
|
|
|
|
|
|
|
|
|
后端 `detect_generation_scenario()` 仍使用固定代码计算场景;生成接口还要求模板 `scenario_tag` 必须等于检测结果。因此只给 `el-select` 增加 `allow-create` 会导致:
|
|
|
|
|
|
|
|
|
|
|
|
- 后台可以保存自定义值;
|
|
|
|
|
|
- 用户选择后生成接口仍提示“模板不适用于当前计划书组合”;
|
|
|
|
|
|
- 自动选模板永远不会命中新场景。
|
|
|
|
|
|
|
|
|
|
|
|
### 9.2 推荐模型
|
|
|
|
|
|
|
|
|
|
|
|
新增 `insurance_ppt_scenarios`:
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 类型 | 说明 |
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
| `code` | VARCHAR(50) PK | 稳定代码 |
|
|
|
|
|
|
| `name` | VARCHAR(100) | 后台显示名 |
|
|
|
|
|
|
| `base_scenario` | VARCHAR(50) | 对应内置数据组合,可空 |
|
|
|
|
|
|
| `generation_mode` | VARCHAR(20) | `single/compare/portfolio` |
|
|
|
|
|
|
| `description` | TEXT | 场景说明 |
|
|
|
|
|
|
| `status` | SMALLINT | 启停 |
|
|
|
|
|
|
| `sort_order` | INT | 排序 |
|
|
|
|
|
|
| `is_builtin` | BOOLEAN | 是否内置 |
|
|
|
|
|
|
| `deleted_at` | TIMESTAMP | 软删除 |
|
|
|
|
|
|
|
|
|
|
|
|
设计边界:
|
|
|
|
|
|
|
|
|
|
|
|
- **场景**负责模板分类和业务话术。
|
|
|
|
|
|
- **generation_mode**仍由实际上传产品组合确定,负责计算逻辑。
|
|
|
|
|
|
- 自定义场景不能修改底层金融计算模式,避免后台误配置导致错误比较。
|
|
|
|
|
|
- `base_scenario` 用于自动匹配;为空的自定义场景只能由管理员配置模板、用户手动选择。
|
|
|
|
|
|
|
|
|
|
|
|
### 9.3 接口
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
GET /insurance/admin/ppt/scenarios
|
|
|
|
|
|
POST /insurance/admin/ppt/scenarios
|
|
|
|
|
|
PUT /insurance/admin/ppt/scenarios/{code}
|
|
|
|
|
|
DELETE /insurance/admin/ppt/scenarios/{code}
|
|
|
|
|
|
PUT /insurance/admin/ppt/scenarios/{code}/status
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
模板接口继续保存 `scenarioTag=code`,但要校验场景存在且启用。
|
|
|
|
|
|
|
|
|
|
|
|
### 9.4 前端
|
|
|
|
|
|
|
|
|
|
|
|
- PPT 模板管理从场景接口加载选项。
|
|
|
|
|
|
- 提供“新增场景”弹窗,而不是在下拉框里随意输入不可审计文本。
|
|
|
|
|
|
- 内置场景允许改显示名和排序,不允许修改代码与计算模式。
|
|
|
|
|
|
- 已被模板使用的场景删除时返回引用冲突,引导先迁移模板。
|
|
|
|
|
|
|
|
|
|
|
|
### 9.5 生成匹配
|
|
|
|
|
|
|
|
|
|
|
|
1. 根据实际产品计算 `detectedScenario` 和 `generationMode`。
|
|
|
|
|
|
2. 自动推荐:
|
|
|
|
|
|
- 先匹配 `base_scenario=detectedScenario`;
|
|
|
|
|
|
- 再按适用保司、产品和状态过滤。
|
|
|
|
|
|
3. 手动选择自定义场景模板:
|
|
|
|
|
|
- 不再要求 `template.scenario_tag == detectedScenario`;
|
|
|
|
|
|
- 校验模板场景的 `generation_mode` 与实际 mode 一致;
|
|
|
|
|
|
- 继续校验适用保司和适用产品。
|
|
|
|
|
|
4. 任务快照同时保存:
|
|
|
|
|
|
- `detectedScenario`
|
|
|
|
|
|
- `selectedScenario`
|
|
|
|
|
|
- `generationMode`
|
|
|
|
|
|
|
|
|
|
|
|
这样既允许业务新增场景,又不破坏底层计算规则。
|
|
|
|
|
|
|
|
|
|
|
|
## 10. 四类后台删除能力
|
|
|
|
|
|
|
|
|
|
|
|
### 10.1 删除策略
|
|
|
|
|
|
|
|
|
|
|
|
这些对象会被历史记录、生成任务、产品、Logo、模板适用范围或文件引用。直接物理删除会破坏追溯,因此统一采用:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
默认:软删除(deleted_at)
|
|
|
|
|
|
必要时:先停用,再删除
|
|
|
|
|
|
文件:引用计数为 0 且过保留期后异步清理
|
|
|
|
|
|
历史:永不级联删除
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
所有列表默认排除软删除数据,管理员可选择查看“回收站”。
|
|
|
|
|
|
|
|
|
|
|
|
### 10.2 保司删除
|
|
|
|
|
|
|
|
|
|
|
|
删除前检查:
|
|
|
|
|
|
|
|
|
|
|
|
- 是否存在未删除产品;
|
|
|
|
|
|
- 是否有进行中的 PPT/海报任务;
|
|
|
|
|
|
- 是否仍被模板 `applicable_company_ids` 引用;
|
|
|
|
|
|
- 是否有 Logo 文件。
|
|
|
|
|
|
|
|
|
|
|
|
规则:
|
|
|
|
|
|
|
|
|
|
|
|
- 有未删除产品时返回 `409 COMPANY_HAS_PRODUCTS`,不自动级联。
|
|
|
|
|
|
- 管理员先迁移或删除产品。
|
|
|
|
|
|
- 删除保司时同时软删除 Logo 元数据;物理 Logo 文件进入延迟清理。
|
|
|
|
|
|
- 历史记录保留公司 ID 和当时快照。
|
|
|
|
|
|
|
|
|
|
|
|
### 10.3 产品删除
|
|
|
|
|
|
|
|
|
|
|
|
删除前检查:
|
|
|
|
|
|
|
|
|
|
|
|
- 是否有进行中的任务;
|
|
|
|
|
|
- 是否被 PPT 模板适用产品列表引用;
|
|
|
|
|
|
- 是否有关联已审核产品小册子或用户资料。
|
|
|
|
|
|
|
|
|
|
|
|
规则:
|
|
|
|
|
|
|
|
|
|
|
|
- 进行中任务存在时禁止删除。
|
|
|
|
|
|
- 模板引用时返回具体模板列表。
|
|
|
|
|
|
- 已生成历史不阻止软删除。
|
|
|
|
|
|
- 小册子文件不立即删除,按数据保留策略异步处理。
|
|
|
|
|
|
|
|
|
|
|
|
### 10.4 PPT 模板删除
|
|
|
|
|
|
|
|
|
|
|
|
规则:
|
|
|
|
|
|
|
|
|
|
|
|
- 内置模板默认只允许停用/隐藏,不物理删除内置资产。
|
|
|
|
|
|
- 自定义模板允许软删除。
|
|
|
|
|
|
- 进行中任务使用该模板时禁止删除。
|
|
|
|
|
|
- 历史 PPT 继续保留 `template_id` 和内容快照。
|
|
|
|
|
|
- 模板源 PPTX 只有在无其他版本引用且过保留期后才清理。
|
|
|
|
|
|
|
|
|
|
|
|
### 10.5 文案模板删除
|
|
|
|
|
|
|
|
|
|
|
|
当前 `delete_copy_template()` 是硬删除,应改为软删除:
|
|
|
|
|
|
|
|
|
|
|
|
- 新增 `deleted_at`。
|
|
|
|
|
|
- 海报历史必须使用生成时的文案快照,不回查已删除模板内容。
|
|
|
|
|
|
- 已被草稿引用时允许软删除,但草稿打开时提示“模板已下架,保留当前文案,重新选择后才能再次套用”。
|
|
|
|
|
|
|
|
|
|
|
|
### 10.6 API 与交互
|
|
|
|
|
|
|
|
|
|
|
|
新增:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
DELETE /insurance/admin/ppt/companies/{id}
|
|
|
|
|
|
DELETE /insurance/admin/ppt/products/{id}
|
|
|
|
|
|
DELETE /insurance/admin/ppt/templates/{id}
|
|
|
|
|
|
DELETE /insurance/admin/ppt/copy-templates/{id} # 改软删除语义
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
统一响应:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": 0,
|
|
|
|
|
|
"message": "success",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"id": "xxx",
|
|
|
|
|
|
"deleted": true,
|
|
|
|
|
|
"deletedAt": "2026-07-31T12:00:00"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
引用冲突:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": 1409,
|
|
|
|
|
|
"message": "该保司仍有 3 个产品,无法删除",
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"reason": "COMPANY_HAS_PRODUCTS",
|
|
|
|
|
|
"references": []
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
前端要求:
|
|
|
|
|
|
|
|
|
|
|
|
- 仅 `config_manage` 权限可见删除按钮。
|
|
|
|
|
|
- 二次确认显示对象名称和影响。
|
|
|
|
|
|
- 禁止只用无上下文的“确定删除?”。
|
|
|
|
|
|
- 删除成功后刷新当前分页,不跳回第一页。
|
|
|
|
|
|
- 409 时展示具体依赖和处理入口。
|
|
|
|
|
|
- 所有删除写入 `system_operation_logs`。
|
|
|
|
|
|
|
|
|
|
|
|
## 11. 数据库迁移计划
|
|
|
|
|
|
|
|
|
|
|
|
建议分两个迁移,降低回滚复杂度。
|
|
|
|
|
|
|
|
|
|
|
|
### 11.1 `migrate_027`:策略与软删除
|
|
|
|
|
|
|
|
|
|
|
|
新增:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
insurance_ppt_companies.masking_enabled
|
|
|
|
|
|
insurance_ppt_companies.logo_enabled
|
|
|
|
|
|
insurance_ppt_companies.deleted_at
|
|
|
|
|
|
|
|
|
|
|
|
insurance_ppt_products.masking_enabled
|
|
|
|
|
|
insurance_ppt_products.deleted_at
|
|
|
|
|
|
|
|
|
|
|
|
insurance_ppt_templates.deleted_at
|
|
|
|
|
|
poster_copy_templates.deleted_at
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
默认值:
|
|
|
|
|
|
|
|
|
|
|
|
- `masking_enabled = FALSE`,保持升级前默认不脱敏。
|
|
|
|
|
|
- `logo_enabled = TRUE`,保持升级前 Logo 行为。
|
|
|
|
|
|
- `deleted_at = NULL`。
|
|
|
|
|
|
|
|
|
|
|
|
增加必要索引:
|
|
|
|
|
|
|
|
|
|
|
|
- 公司、产品、模板的 `(status, deleted_at)`。
|
|
|
|
|
|
- 产品的 `(company_id, deleted_at)`。
|
|
|
|
|
|
- 文案模板的 `(status, deleted_at)`。
|
|
|
|
|
|
|
|
|
|
|
|
### 11.2 `migrate_028`:动态场景
|
|
|
|
|
|
|
|
|
|
|
|
- 创建 `insurance_ppt_scenarios`。
|
|
|
|
|
|
- 写入现有内置场景。
|
|
|
|
|
|
- 为已有模板的未知 `scenario_tag` 补建禁用的兼容场景记录,避免数据丢失。
|
|
|
|
|
|
- 增加 `base_scenario/generation_mode` 校验。
|
|
|
|
|
|
|
|
|
|
|
|
### 11.3 迁移安全
|
|
|
|
|
|
|
|
|
|
|
|
- 迁移只加列、加表、加索引,不删旧字段。
|
|
|
|
|
|
- 发布前对生产库备份。
|
|
|
|
|
|
- API 启动后健康检查返回当前迁移版本。
|
|
|
|
|
|
- 若迁移未到 028,管理后台禁用新开关和场景编辑并显示明确错误,不静默失败。
|
|
|
|
|
|
- 必须修复或补测 SQLite 兼容性;当前 `migrate_022` 的表/列存在检查主要面向 PostgreSQL/MySQL。
|
|
|
|
|
|
|
|
|
|
|
|
## 12. API 改造清单
|
|
|
|
|
|
|
|
|
|
|
|
### 12.1 保司与产品
|
|
|
|
|
|
|
|
|
|
|
|
- `GET companies/products` 返回 `maskingEnabled/deletedAt`。
|
|
|
|
|
|
- 保司额外返回 `logoEnabled`。
|
|
|
|
|
|
- `POST/PUT` 支持新字段。
|
|
|
|
|
|
- `DELETE` 执行软删除与引用检查。
|
|
|
|
|
|
- 公共生成选项接口只返回 `status=1 AND deleted_at IS NULL`。
|
|
|
|
|
|
|
|
|
|
|
|
### 12.2 PPT 生成
|
|
|
|
|
|
|
|
|
|
|
|
请求中移除:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
useMaskedData
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
任务快照新增:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"brandPolicy": {
|
|
|
|
|
|
"companyMaskingEnabled": true,
|
|
|
|
|
|
"productMaskingById": {
|
|
|
|
|
|
"product-a": true
|
|
|
|
|
|
},
|
|
|
|
|
|
"logoEnabled": false,
|
|
|
|
|
|
"policyVersion": 1
|
|
|
|
|
|
},
|
|
|
|
|
|
"detectedScenario": "single_savings",
|
|
|
|
|
|
"selectedScenario": "retirement_education",
|
2026-07-31 18:38:53 +08:00
|
|
|
|
"generationMode": "single",
|
|
|
|
|
|
"templateAsset": {
|
|
|
|
|
|
"templateId": "scenario_single_savings",
|
|
|
|
|
|
"assetId": "builtin://single_savings.pptx",
|
|
|
|
|
|
"version": 1,
|
|
|
|
|
|
"sha256": "ba7b198e92caba6316ed8db71f0ace4231d9b570aa78f8d2ad96ce6e3c31f184",
|
|
|
|
|
|
"rendererMode": "clone-edit-v2"
|
|
|
|
|
|
}
|
2026-07-31 14:10:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 12.3 校验
|
|
|
|
|
|
|
|
|
|
|
|
校验响应新增:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"canProceed": true,
|
|
|
|
|
|
"blockerCount": 0,
|
|
|
|
|
|
"warningCount": 3,
|
|
|
|
|
|
"infoCount": 1
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
兼容旧字段:
|
|
|
|
|
|
|
|
|
|
|
|
- 一个版本内继续返回 `validated = blockerCount == 0`。
|
|
|
|
|
|
- 前端改用 `canProceed`。
|
|
|
|
|
|
|
|
|
|
|
|
### 12.4 海报
|
|
|
|
|
|
|
|
|
|
|
|
海报创建和文案生成请求不再接收 `useMaskedData`。
|
|
|
|
|
|
|
|
|
|
|
|
响应/任务快照增加:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"planType": "savings",
|
|
|
|
|
|
"outputMode": "single",
|
|
|
|
|
|
"fieldProfileVersion": 1,
|
|
|
|
|
|
"brandPolicy": {}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 13. 代码修改范围
|
|
|
|
|
|
|
|
|
|
|
|
### 13.1 后端
|
|
|
|
|
|
|
|
|
|
|
|
| 文件/模块 | 改动 |
|
|
|
|
|
|
|---|---|
|
2026-07-31 18:38:53 +08:00
|
|
|
|
| `api/insurance/generation/celery_tasks.py` | P0 任务修复、校验分级、策略快照消费;固化模板资产版本/hash/渲染模式 |
|
2026-07-31 14:10:24 +08:00
|
|
|
|
| `api/insurance/generation/task_service.py` | 失败状态同步、stale 任务恢复 |
|
|
|
|
|
|
| `api/insurance/ppt/routes.py` | 移除用户脱敏控制、动态场景、校验响应 |
|
|
|
|
|
|
| `api/insurance/ppt/validator.py` | BLOCKER/WARNING/INFO 分级 |
|
|
|
|
|
|
| `api/insurance/ppt/normalizer.py` | 新字段和未知币种处理 |
|
|
|
|
|
|
| `api/insurance/ppt/prompts.py` | 新字段提取口径 |
|
|
|
|
|
|
| `api/insurance/ppt/regex_extractor.py` | 标签与币种归一化 |
|
2026-07-31 18:38:53 +08:00
|
|
|
|
| `api/insurance/ppt/renderer.py` | Logo/脱敏策略写入 DeckContract;按 `rendererMode` 分流真实模板与通用构建器 |
|
|
|
|
|
|
| `api/insurance/ppt/scripts/fast_pptx_renderer.py` | 禁止在真实模板模式删除源幻灯片;按模板清单原位替换形状、图表、表格和图片 |
|
|
|
|
|
|
| `api/insurance/ppt/template_asset_service.py` | 模板入库校验、规范化、SHA-256、版本化和绑定清单解析 |
|
2026-07-31 14:10:24 +08:00
|
|
|
|
| `api/insurance/ppt/masking.py` | 从请求布尔值改为实体策略 |
|
|
|
|
|
|
| `api/insurance/poster/product_source_resolver.py` | 品牌策略和统一事实模型 |
|
|
|
|
|
|
| `api/insurance/poster/content_builder.py` | 按画像构建 ViewModel |
|
|
|
|
|
|
| `api/insurance/poster/copy_generator.py` | 只消费允许字段 |
|
|
|
|
|
|
| `api/insurance/poster/image_generator.py` | 禁止伪 Logo,执行 Logo 策略 |
|
|
|
|
|
|
| `api/insurance/poster/field_profiles.py` | 新增险种 × 版式画像 |
|
|
|
|
|
|
| `api/insurance/admin/ppt_admin_service.py` | 新开关、动态场景、删除与引用检查 |
|
|
|
|
|
|
| `api/insurance/admin/ppt_admin_routes.py` | 新接口 |
|
2026-07-31 18:38:53 +08:00
|
|
|
|
| `api/insurance/models/ppt_config.py` | 新字段、场景模型;模板资产字段扩容及版本关系 |
|
2026-07-31 14:10:24 +08:00
|
|
|
|
| `api/insurance/models/poster_copy_template.py` | 软删除 |
|
|
|
|
|
|
| `api/insurance/db/migrate_027.py` | 策略与软删除迁移 |
|
|
|
|
|
|
| `api/insurance/db/migrate_028.py` | 场景迁移 |
|
2026-07-31 18:38:53 +08:00
|
|
|
|
| `api/insurance/db/migrate_029.py` | 模板资产版本、字段扩容、三个内置模板清单与 hash 回填 |
|
2026-07-31 14:10:24 +08:00
|
|
|
|
|
|
|
|
|
|
### 13.2 前端
|
|
|
|
|
|
|
|
|
|
|
|
| 文件/模块 | 改动 |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `PptGenerate.vue` | 删除脱敏开关、显示只读后台策略 |
|
|
|
|
|
|
| `PptDataReview.vue` | 新字段、币种、非阻断警告、返回修改 |
|
|
|
|
|
|
| `PptResult.vue` | 返回核验并生成新版本 |
|
|
|
|
|
|
| `PosterProductPanel.vue` | 删除脱敏开关 |
|
|
|
|
|
|
| `PosterConfigRail.vue` | 移除脱敏事件 |
|
|
|
|
|
|
| `PosterCopyPanel.vue` | 请求移除 `useMaskedData` |
|
|
|
|
|
|
| `PosterHtmlCanvas.vue` | 消费服务端 ViewModel |
|
|
|
|
|
|
| `PosterSummaryCards.vue` | 消费排序后的字段卡 |
|
|
|
|
|
|
| `usePosterWorkspace.ts` | 删除草稿脱敏字段,增加画像版本 |
|
|
|
|
|
|
| `PptCompaniesAdmin.vue` | 脱敏、Logo、删除开关 |
|
|
|
|
|
|
| `PptProductsAdmin.vue` | 脱敏、删除开关 |
|
|
|
|
|
|
| `PptTemplatesAdmin.vue` | 动态场景、删除 |
|
|
|
|
|
|
| `CopyTemplatesAdmin.vue` | 软删除交互和依赖提示 |
|
|
|
|
|
|
| `ppt-api.ts/poster-api.ts/ppt-admin-api.ts` | 更新类型与接口 |
|
|
|
|
|
|
|
|
|
|
|
|
## 14. 分阶段交付计划
|
|
|
|
|
|
|
|
|
|
|
|
### 阶段 A:P0 热修(建议 0.5~1 人日)
|
|
|
|
|
|
|
|
|
|
|
|
- 修复未定义 `task`。
|
|
|
|
|
|
- 把状态同步纳入异常捕获。
|
|
|
|
|
|
- 清理 stale 任务。
|
|
|
|
|
|
- 修复失败的提取器测试 mock。
|
|
|
|
|
|
- 增加 Worker 任务回归测试。
|
|
|
|
|
|
- 在部署环境确认 022~026 迁移、队列和共享存储。
|
|
|
|
|
|
|
|
|
|
|
|
验收门槛:PPT 可稳定生成,任务状态闭环。
|
|
|
|
|
|
|
2026-07-31 18:38:53 +08:00
|
|
|
|
### 阶段 A2:P0 模板真实套用(建议 3~5 人日)
|
|
|
|
|
|
|
|
|
|
|
|
- 引入 `clone-edit-v2`,从所选源 PPTX 开始原位编辑,不再先删光源页面。
|
|
|
|
|
|
- 为三个内置模板建立逐页、逐形状的数据绑定清单。
|
|
|
|
|
|
- 修复模板图表 OOXML 兼容性并完成 PowerPoint/WPS 打开测试。
|
|
|
|
|
|
- 扩容模板资产标识,固化任务使用的版本和 SHA-256。
|
|
|
|
|
|
- 将当前“文件存在 + 页数正确”的测试升级为模板结构和视觉保真测试。
|
|
|
|
|
|
|
|
|
|
|
|
验收门槛:三个模板各生成一次,成品设计明显互异,且均保留源模板独有视觉签名、可编辑对象和正确资产 hash。
|
|
|
|
|
|
|
2026-07-31 14:10:24 +08:00
|
|
|
|
### 阶段 B:后台策略与 PPT 核验(建议 3~5 人日)
|
|
|
|
|
|
|
|
|
|
|
|
- `migrate_027`。
|
|
|
|
|
|
- 保司/产品脱敏开关、保司 Logo 开关。
|
|
|
|
|
|
- 移除 PPT/海报用户脱敏开关。
|
|
|
|
|
|
- 新增吸烟、币种和三个条件字段。
|
|
|
|
|
|
- 重构校验分级。
|
|
|
|
|
|
- 支持结果页返回修改和版本化重生成。
|
|
|
|
|
|
|
|
|
|
|
|
验收门槛:四种脱敏组合、Logo 开关、非阻断校验全部通过。
|
|
|
|
|
|
|
|
|
|
|
|
### 阶段 C:海报字段画像(建议 3~5 人日)
|
|
|
|
|
|
|
|
|
|
|
|
- 建立统一事实模型和六套核心画像。
|
|
|
|
|
|
- 单图/长图内容预算。
|
|
|
|
|
|
- 预览、Prompt、导出共用 ViewModel。
|
|
|
|
|
|
- 建立至少 12 个基准输出。
|
|
|
|
|
|
|
|
|
|
|
|
验收门槛:无跨险种字段、无虚构数字、布局不过载。
|
|
|
|
|
|
|
|
|
|
|
|
### 阶段 D:动态场景与删除能力(建议 3~4 人日)
|
|
|
|
|
|
|
|
|
|
|
|
- `migrate_028`。
|
|
|
|
|
|
- 场景管理接口和页面。
|
|
|
|
|
|
- 模板匹配逻辑拆分。
|
|
|
|
|
|
- 四类对象软删除、依赖检查、审计日志和回收站。
|
|
|
|
|
|
|
|
|
|
|
|
验收门槛:自定义场景可完整使用,删除不破坏历史。
|
|
|
|
|
|
|
|
|
|
|
|
### 阶段 E:回归与灰度(建议 2~3 人日)
|
|
|
|
|
|
|
|
|
|
|
|
- 全量后端测试、前端构建、容器冒烟。
|
|
|
|
|
|
- 真实脱敏样本 UAT。
|
|
|
|
|
|
- 灰度开启新策略。
|
|
|
|
|
|
- 观察任务失败率、平均生成时间和 stale 任务数。
|
|
|
|
|
|
|
2026-07-31 18:38:53 +08:00
|
|
|
|
总估算:约 14.5~23 人日,不包含业务方准备样本、设计稿调整和外部 AI 服务不稳定等待时间。
|
2026-07-31 14:10:24 +08:00
|
|
|
|
|
|
|
|
|
|
## 15. 测试计划
|
|
|
|
|
|
|
|
|
|
|
|
### 15.1 单元测试
|
|
|
|
|
|
|
|
|
|
|
|
- 品牌策略四种组合。
|
|
|
|
|
|
- Logo 开关。
|
|
|
|
|
|
- 币种别名归一化和未知币种。
|
|
|
|
|
|
- 三个条件字段映射。
|
|
|
|
|
|
- 各险种 BLOCKER/WARNING。
|
|
|
|
|
|
- 利益/退保异常永不成为 BLOCKER。
|
|
|
|
|
|
- 六套海报字段画像。
|
|
|
|
|
|
- 自定义场景与 generation mode 兼容性。
|
|
|
|
|
|
- 四类删除的引用检查。
|
|
|
|
|
|
|
|
|
|
|
|
### 15.2 API 测试
|
|
|
|
|
|
|
|
|
|
|
|
- 用户传入 `useMaskedData` 不改变结果。
|
|
|
|
|
|
- 无权限用户不能改策略或删除。
|
|
|
|
|
|
- 删除引用冲突返回 409 和依赖详情。
|
|
|
|
|
|
- 被删除对象不出现在生成选项。
|
|
|
|
|
|
- 历史接口仍返回已删除对象的快照。
|
|
|
|
|
|
|
|
|
|
|
|
### 15.3 集成测试
|
|
|
|
|
|
|
|
|
|
|
|
- API 创建任务 → insurance 队列 → Worker → 文件 → 下载。
|
|
|
|
|
|
- API/Worker 共享存储。
|
|
|
|
|
|
- PostgreSQL 迁移 016 → 028。
|
|
|
|
|
|
- 空库迁移 001 → 028。
|
|
|
|
|
|
- 重复执行迁移无副作用。
|
|
|
|
|
|
- Worker 中断和恢复。
|
|
|
|
|
|
|
|
|
|
|
|
### 15.4 前端测试
|
|
|
|
|
|
|
|
|
|
|
|
- PPT/海报用户端无脱敏开关。
|
|
|
|
|
|
- 后台三个新开关保存后立即生效。
|
|
|
|
|
|
- 核验页 WARNING 可继续。
|
|
|
|
|
|
- 返回修改保持数据。
|
|
|
|
|
|
- 自定义场景新增、编辑、停用、删除。
|
|
|
|
|
|
- 删除成功、冲突、无权限三种状态。
|
|
|
|
|
|
|
|
|
|
|
|
### 15.5 视觉和文件测试
|
|
|
|
|
|
|
|
|
|
|
|
- PPTX 可打开、页数正确、无破图。
|
2026-07-31 18:38:53 +08:00
|
|
|
|
- 三个内置模板分别生成后进行逐页截图对比;不得出现三份成品共用同一深蓝基础背景的情况。
|
|
|
|
|
|
- 校验任务记录中的 `templateId/assetVersion/assetSha256/rendererMode` 与 Worker 实际读取文件一致。
|
|
|
|
|
|
- 校验源模板独有对象仍可编辑:单储蓄模板图表与摘要表、多储蓄模板双产品对比图、储蓄险 + IUL 模板图片与组合配置页。
|
|
|
|
|
|
- 模板导入需通过 OOXML 校验及 PowerPoint/WPS 无修复提示打开;不得只依赖 `python-pptx` 可解析。
|
2026-07-31 14:10:24 +08:00
|
|
|
|
- Logo 开/关像素级对比。
|
|
|
|
|
|
- 单图常见比例不溢出。
|
|
|
|
|
|
- 长图高度随内容增长。
|
|
|
|
|
|
- 中英文、长产品名、大金额、字段缺失。
|
|
|
|
|
|
|
|
|
|
|
|
## 16. 监控与可观测性
|
|
|
|
|
|
|
|
|
|
|
|
新增指标:
|
|
|
|
|
|
|
|
|
|
|
|
- `insurance_ppt_tasks_total{status,error_code}`
|
|
|
|
|
|
- `insurance_ppt_generation_duration_seconds`
|
|
|
|
|
|
- `insurance_ppt_stale_tasks`
|
|
|
|
|
|
- `insurance_ppt_render_failures_total`
|
|
|
|
|
|
- `insurance_poster_generation_duration_seconds`
|
|
|
|
|
|
- `insurance_generation_policy_snapshot_total`
|
|
|
|
|
|
- `insurance_admin_delete_conflicts_total{entity}`
|
|
|
|
|
|
|
|
|
|
|
|
日志必须包含:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
task_id
|
|
|
|
|
|
workspace_id
|
|
|
|
|
|
user_id(必要时脱敏)
|
|
|
|
|
|
stage
|
|
|
|
|
|
template_id
|
|
|
|
|
|
detected_scenario
|
|
|
|
|
|
selected_scenario
|
|
|
|
|
|
generation_mode
|
|
|
|
|
|
plan_types
|
|
|
|
|
|
error_code
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
不得记录完整客户 PDF 文本、未脱敏姓名或完整 Prompt。
|
|
|
|
|
|
|
|
|
|
|
|
建议告警:
|
|
|
|
|
|
|
|
|
|
|
|
- 5 分钟内 PPT 失败率 > 10%;
|
|
|
|
|
|
- 任一任务 `running` 超过 10 分钟;
|
|
|
|
|
|
- Worker 未注册 `insurance.generate_ppt`;
|
|
|
|
|
|
- API/Worker 存储探针不一致;
|
|
|
|
|
|
- 最新迁移版本低于应用要求。
|
|
|
|
|
|
|
|
|
|
|
|
## 17. 上线与回滚
|
|
|
|
|
|
|
|
|
|
|
|
### 17.1 上线顺序
|
|
|
|
|
|
|
|
|
|
|
|
1. 备份数据库。
|
|
|
|
|
|
2. 部署 P0 Worker 修复。
|
|
|
|
|
|
3. 执行迁移 027。
|
|
|
|
|
|
4. 部署兼容新旧字段的后端。
|
|
|
|
|
|
5. 部署前端,移除用户脱敏开关。
|
|
|
|
|
|
6. 执行迁移 028。
|
|
|
|
|
|
7. 开启动态场景和删除页面。
|
|
|
|
|
|
8. 完成冒烟后再开放真实用户。
|
|
|
|
|
|
|
|
|
|
|
|
必须先后端后前端,避免旧后端不识别新字段。
|
|
|
|
|
|
|
|
|
|
|
|
### 17.2 回滚
|
|
|
|
|
|
|
|
|
|
|
|
- 新迁移只增加字段和表,应用回滚时保留数据。
|
|
|
|
|
|
- 后端回滚后旧代码会忽略新增字段。
|
|
|
|
|
|
- Logo 默认 TRUE、脱敏默认 FALSE,回滚行为接近升级前。
|
|
|
|
|
|
- 动态场景故障时可关闭场景管理入口,保留三个内置场景。
|
|
|
|
|
|
- 删除全部为软删除,可通过回收站恢复。
|
|
|
|
|
|
- P0 Worker 修复不得回滚到含未定义变量的版本。
|
|
|
|
|
|
|
|
|
|
|
|
## 18. 风险与待业务确认
|
|
|
|
|
|
|
|
|
|
|
|
### 18.1 已确定,不需要再讨论
|
|
|
|
|
|
|
|
|
|
|
|
- PPT Worker 未定义变量必须优先修复。
|
|
|
|
|
|
- 用户侧脱敏开关必须移除,服务端不能信任旧参数。
|
|
|
|
|
|
- Logo 开关必须由服务端执行。
|
|
|
|
|
|
- 利益演示和退保提取不得阻断下一步。
|
|
|
|
|
|
- `USB` 应纠正为 `USD`。
|
|
|
|
|
|
- 删除应保护历史,不能直接级联物理删除。
|
|
|
|
|
|
|
|
|
|
|
|
### 18.2 开发前需要业务确认
|
|
|
|
|
|
|
|
|
|
|
|
1. “脱敏”是否仅指保司/产品名称,还是还包括客户姓名、证件号、手机号和保费组合。
|
|
|
|
|
|
2. 三个候选字段在真实计划书中的原文标签和业务含义。
|
|
|
|
|
|
3. 吸烟状态未知时是否允许继续;本计划建议允许并警告。
|
|
|
|
|
|
4. 自定义生成场景是否只用于模板分类,还是需要新增计算逻辑;本计划只开放分类和话术,不开放任意金融计算。
|
|
|
|
|
|
5. 内置 PPT 模板是否允许彻底删除;本计划建议仅停用。
|
|
|
|
|
|
6. 删除后的回收站保留期和源文件物理清理周期。
|
|
|
|
|
|
|
|
|
|
|
|
这些确认不会阻塞 P0 热修,可以与阶段 A 并行完成。
|
|
|
|
|
|
|
|
|
|
|
|
## 19. Definition of Done
|
|
|
|
|
|
|
|
|
|
|
|
本轮优化完成必须同时满足:
|
|
|
|
|
|
|
|
|
|
|
|
- [ ] P0 根因有自动化回归测试。
|
|
|
|
|
|
- [ ] 相关测试全部通过,无已知失败或跳过的核心链路。
|
|
|
|
|
|
- [ ] 生产迁移版本已核验。
|
|
|
|
|
|
- [ ] 用户端脱敏开关已移除。
|
|
|
|
|
|
- [ ] 保司/产品脱敏策略服务端强制生效。
|
|
|
|
|
|
- [ ] Logo 关闭后所有新生成物均无 Logo。
|
|
|
|
|
|
- [ ] 吸烟状态、币种和三个条件字段完成前后端贯通。
|
|
|
|
|
|
- [ ] 利益/退保问题仅为非阻断警告。
|
|
|
|
|
|
- [ ] 结果页可以返回修改并生成新版本。
|
|
|
|
|
|
- [ ] 三险种 × 两版式海报基准全部通过。
|
|
|
|
|
|
- [ ] 自定义场景可以完整创建、选择、生成和停用。
|
2026-07-31 18:38:53 +08:00
|
|
|
|
- [ ] 三个内置 PPT 模板均由 `clone-edit-v2` 真实套用,源模板结构和视觉特征有自动化保真测试。
|
|
|
|
|
|
- [ ] 模板资产版本、SHA-256 和实际渲染模式进入不可变任务快照与结果元数据。
|
2026-07-31 14:10:24 +08:00
|
|
|
|
- [ ] 四类后台对象支持安全删除和引用提示。
|
|
|
|
|
|
- [ ] 删除、策略修改和生成均有审计记录。
|
|
|
|
|
|
- [ ] API 文档、测试用例、部署文档同步更新。
|
|
|
|
|
|
|
|
|
|
|
|
## 20. 建议立即执行的前三项
|
|
|
|
|
|
|
|
|
|
|
|
1. **立即热修 `generate_ppt_task()` 未定义变量,并清理 stale 任务。**
|
|
|
|
|
|
2. **在实际部署数据库和 Worker 中核验迁移、任务注册、队列与共享存储。**
|
|
|
|
|
|
3. **收集 9 份以上脱敏计划书样本,确认三个候选字段,再冻结字段规范。**
|
|
|
|
|
|
|
|
|
|
|
|
在这三项完成前,不建议先做大规模界面调整;否则可能出现页面已经改完,但生成链路和字段语义仍不可靠的情况。
|
|
|
|
|
|
|
|
|
|
|
|
## 21. 实施记录(2026-07-31)
|
|
|
|
|
|
|
|
|
|
|
|
本次已按计划完成代码级修复:
|
|
|
|
|
|
|
|
|
|
|
|
- 修复 PPT Worker 在异常捕获前访问未定义任务变量的问题;失败任务会进入 `failed` 并同步工作区,stale 任务统一使用 `stale_task`。
|
|
|
|
|
|
- 新增 `migrate_027.py`:品牌策略、Logo 开关、软删除字段、动态场景表及六个内置场景。
|
|
|
|
|
|
- PPT/海报生成页已移除用户脱敏开关;服务端忽略旧参数,按任务创建时的后台策略快照执行。
|
|
|
|
|
|
- PPT 核验新增吸烟状态、ISO 币种及三个条件金额字段;`USB` 归一化为 `USD`,`RMB/¥` 归一化为 `CNY`,未知币种不再默认。
|
|
|
|
|
|
- 利益演示与退保提取问题全部为非阻断警告;结果页可返回核验数据,重新生成保留历史版本。
|
|
|
|
|
|
- 海报新增三险种 × 单图/长图六套字段画像和内容预算,单图不会携带完整利益表。
|
|
|
|
|
|
- PPT 场景支持后台新增、停用、删除与模板选择;计算模式仍由实际材料组合确定。
|
|
|
|
|
|
- 保司、产品、PPT 模板、文案模板均使用软删除;保司活跃产品、内置模板和场景引用具备删除保护;策略变更和删除写入操作审计。
|
|
|
|
|
|
- API 文档和部署文档已同步。
|
|
|
|
|
|
|
|
|
|
|
|
已完成验证:
|
|
|
|
|
|
|
|
|
|
|
|
- Python 语法编译通过。
|
|
|
|
|
|
- 核心新增与现有任务/路由测试:`37 passed, 1 skipped`。
|
|
|
|
|
|
- PPT 提取、校验、对比与状态同步扩展回归:`140 passed`(更新旧测试口径后)。
|
|
|
|
|
|
- 使用工作区幻灯片运行环境补跑 PPT 渲染对比:`6 passed`。
|
|
|
|
|
|
- 全量测试:`190 passed, 1 failed`;唯一失败是既有 `test_chat_logs_query` 未创建 Flask application context,与本次 PPT/海报改动无关。
|
|
|
|
|
|
- Vue/Vite 生产构建通过。
|
|
|
|
|
|
|
|
|
|
|
|
环境限制与上线前待办:
|
|
|
|
|
|
|
|
|
|
|
|
- 尚未在生产数据库实际执行 `migrate_027`,也未连接真实 Celery Worker、共享存储和图片模型做端到端生成。
|
|
|
|
|
|
- 三个条件字段仍需使用脱敏真实计划书样本冻结标签语义;本实现保持可选,不将其作为全险种阻断条件。
|
|
|
|
|
|
- 三险种 × 两版式的 12 份视觉基准需要业务样本和人工验收,不能由无样本单元测试替代。
|
2026-07-31 18:38:53 +08:00
|
|
|
|
|
|
|
|
|
|
## 22. 三个内置 PPT 模板专项诊断与修复方案(2026-07-31 补充)
|
|
|
|
|
|
|
|
|
|
|
|
### 22.1 已核验事实
|
|
|
|
|
|
|
|
|
|
|
|
对源 PPTX、数据库模板记录、生成任务快照、Worker 输出和最终成品进行了逐层比对:
|
|
|
|
|
|
|
|
|
|
|
|
| 模板 | 源文件 | 源页数 | 实际成品页数 | 源模板主要视觉 | 实际成品 |
|
|
|
|
|
|
|---|---|---:|---:|---|---|
|
|
|
|
|
|
| `scenario_single_savings` | `single_savings.pptx` | 20 | 12 | 白底、藏蓝与金色;4 个图表、2 个摘要表 | 统一深蓝底、青绿色卡片 |
|
|
|
|
|
|
| `scenario_multi_savings` | `multi_savings_comparison.pptx` | 17 | 10 | 白底、藏蓝与金色;双产品对比图和双摘要表 | 统一深蓝底、青绿色卡片 |
|
|
|
|
|
|
| `scenario_savings_iul` | `savings_iul_comprehensive.pptx` | 18 | 15 | 米白/绿色;5 个媒体资产、家庭与顾问照片、组合配置页 | 统一深蓝底,源照片全部消失 |
|
|
|
|
|
|
|
|
|
|
|
|
数据库中三条记录的 `source_template_asset_id` 均正确,且都被标记为 `clone_ready=true`;近期任务也记录了正确的 `requestedTemplateId/appliedTemplateId` 和 `templateFallback=false`。因此这不是“用户没有选中模板”,也不是内置文件缺失。
|
|
|
|
|
|
|
|
|
|
|
|
### 22.2 直接根因
|
|
|
|
|
|
|
|
|
|
|
|
真实调用链为:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
PptGenerate.vue 选择 templateId
|
|
|
|
|
|
→ 生成任务快照保存 templateId
|
|
|
|
|
|
→ celery_tasks.py 查询 PptTemplate
|
|
|
|
|
|
→ resolve_template_asset() 得到正确 builtin:// 文件路径
|
|
|
|
|
|
→ renderer.py 同时写入 scenarioSlides 与 templateConfig
|
|
|
|
|
|
→ fast_pptx_renderer.py 打开源 PPTX
|
|
|
|
|
|
→ while len(prs.slides): 删除全部源幻灯片
|
|
|
|
|
|
→ SLIDE_BUILDERS + THEMES 重新生成基础页面
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
具体有四个叠加问题:
|
|
|
|
|
|
|
|
|
|
|
|
1. `fast_pptx_renderer.py` 打开源模板后立即删除全部幻灯片。源背景、图片、形状、图表、表格、字号、间距和页面编排因此全部丢失,只可能残留母版关系。
|
|
|
|
|
|
2. `renderer.py` 总是生成 `scenarioSlides`;`_resolve_page_types()` 又优先使用它,导致模板解析得到的 `slidesConfig` 和 20/17/18 页结构被覆盖。
|
|
|
|
|
|
3. 三个模板数据库记录的 `style_preset` 都是 `broker`;最终颜色由硬编码 `THEMES['broker']` 决定,所以选择不同模板仍会生成同一深蓝风格。
|
|
|
|
|
|
4. 当前结果中的 `appliedTemplateId` 只表示模板记录通过校验并进入 Worker,不表示源模板视觉被保留,属于误导性可观测状态。
|
|
|
|
|
|
|
|
|
|
|
|
前端恢复 `draft_options.templateId` 会让旧工作区继续显示上次选择,这可能造成“仍在用旧模板”的次级现象,但无法解释本次三个内置模板都退回基础样式;本次主因已经定位在渲染器。
|
|
|
|
|
|
|
|
|
|
|
|
### 22.3 模板资产本身还存在的兼容风险
|
|
|
|
|
|
|
|
|
|
|
|
- 三个源文件均没有 PowerPoint 结构化占位符,当前解析器只得到标题、页面类型和 `sourceSlide`,没有任何“业务字段 → shape/chart/table”绑定关系。不能靠换一个主题色实现动态填充。
|
|
|
|
|
|
- `single_savings.pptx` 的图表 XML 含 `barGrouping=none/standard`;artifact-tool 2.8.36 无法导入,PowerPoint 自动化需要 `OpenAndRepair` 才能稳定打开。资产上线前必须规范化并重新保存,不能只以 `python-pptx` 可读取作为合格标准。
|
|
|
|
|
|
- `source_template_asset_id` 当前为 `VARCHAR(50)`,而后台上传返回的 `stored://{templateId}/{32位uuid}.pptx` 在常见模板 ID 下可能超过 50 字符,造成保存失败、截断或资产记录缺失。
|
|
|
|
|
|
- 任务只固化模板 ID,没有固化资产版本和 hash;管理员替换模板后,排队任务无法证明自己实际使用的是新文件还是旧文件。
|
|
|
|
|
|
|
|
|
|
|
|
### 22.4 推荐实现:双渲染模式
|
|
|
|
|
|
|
|
|
|
|
|
保留通用构建器作为兼容回退,但把“真实模板”与“基础主题”明确分成两条链:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
clone-edit-v2
|
|
|
|
|
|
打开指定版本源 PPTX
|
|
|
|
|
|
→ 按 manifest 选择/复制源页面
|
|
|
|
|
|
→ 按 shapeId 原位改文字、表格、图表数据和图片
|
|
|
|
|
|
→ 保留母版、版式、背景、图片裁剪和可编辑对象
|
|
|
|
|
|
→ 结构/视觉 QA
|
|
|
|
|
|
|
|
|
|
|
|
generic-builder-v1
|
|
|
|
|
|
无源资产或旧式主题模板
|
|
|
|
|
|
→ SLIDE_BUILDERS + THEMES
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
对 `clone-edit-v2` 的硬性约束:
|
|
|
|
|
|
|
|
|
|
|
|
- 禁止执行“删除全部源幻灯片再重建”。
|
|
|
|
|
|
- 每个输出页都必须映射到明确的 `sourceSlide`。
|
|
|
|
|
|
- 三个内置模板分别维护版本化 manifest,记录页面用途、保留/可选规则和可编辑元素的 shape ID。
|
|
|
|
|
|
- 文本必须在原形状中替换;图表更新数据源;表格更新单元格;图片替换时保留原裁剪和几何信息。
|
|
|
|
|
|
- 源模板没有可用内容槽位时,必须调整模板或显式报错,不允许静默退回基础模板。
|
|
|
|
|
|
- 删除样例业务数据必须按 manifest 精确执行,禁止遍历并清空所有文本形状,以免误删品牌、页脚和设计文字。
|
|
|
|
|
|
|
|
|
|
|
|
### 22.5 资产版本模型
|
|
|
|
|
|
|
|
|
|
|
|
新增 `insurance_ppt_template_assets`,至少包含:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
id, template_id, version, storage_uri, sha256, file_size,
|
|
|
|
|
|
slide_count, manifest_json, validation_status, created_at
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
同时:
|
|
|
|
|
|
|
|
|
|
|
|
- 将 `source_template_asset_id` 扩容为 `VARCHAR(255)`,或改为资产表外键。
|
|
|
|
|
|
- 上传采用“先保存临时文件 → 校验/规范化 → 计算 hash → 创建新版本 → 原子切换当前版本”。
|
|
|
|
|
|
- 旧版本在仍被 queued/running 任务或历史记录引用时不得物理删除。
|
|
|
|
|
|
- Worker 只消费任务快照中的 `assetId/version/sha256`,读取后再次校验 hash;不在执行时回查“当前模板版本”。
|
|
|
|
|
|
- 新工作区默认使用当前版本;旧工作区若保留旧版本,界面必须明确显示版本和更新时间,并提供“切换到最新版”。
|
|
|
|
|
|
|
|
|
|
|
|
### 22.6 测试与验收
|
|
|
|
|
|
|
|
|
|
|
|
现有 `test_renderer_can_reuse_builtin_template_master` 只检查输出存在且为 2 页,实际上无法发现源模板页面被删光。应替换为以下门禁:
|
|
|
|
|
|
|
|
|
|
|
|
1. 三个模板各做一套真实数据端到端测试,断言请求、任务快照、Worker 文件 hash 和结果元数据一致。
|
|
|
|
|
|
2. 断言输出页面全部来自 manifest 映射,不再固定为通用构建器的 12/10/15 页。
|
|
|
|
|
|
3. 断言单储蓄模板的图表/摘要表、多储蓄模板的双产品图表、储蓄险 + IUL 模板的源图片和组合页面仍存在且可编辑。
|
|
|
|
|
|
4. 对三个成品计算渲染截图感知 hash;三个模板之间必须明显不同,并分别通过与各自基准图的差异阈值。
|
|
|
|
|
|
5. 扫描最终 PPTX,确保示例客户、示例保费和示例公司数据已被替换或删除,没有模板样例数据泄漏。
|
|
|
|
|
|
6. PowerPoint/WPS 无修复提示打开,逐页无溢出、破图、空占位符或丢失字体。
|
|
|
|
|
|
7. 管理员上传新版后创建两个任务:切换前任务仍使用旧 hash,切换后任务使用新 hash,以验证版本隔离而不是缓存猜测。
|
|
|
|
|
|
|
|
|
|
|
|
完成上述门禁后,任务结果才允许写入:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"templateApplied": true,
|
|
|
|
|
|
"templateId": "scenario_savings_iul",
|
|
|
|
|
|
"assetVersion": 2,
|
|
|
|
|
|
"assetSha256": "...",
|
|
|
|
|
|
"rendererMode": "clone-edit-v2",
|
|
|
|
|
|
"templateFallback": false
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
2026-07-31 20:05:34 +08:00
|
|
|
|
|
|
|
|
|
|
## 23. 专项修复实施记录(2026-07-31)
|
|
|
|
|
|
|
|
|
|
|
|
本轮已完成以下修复:
|
|
|
|
|
|
|
|
|
|
|
|
- 三个内置模板改为 `clone-edit-v2`:按模板清单选择原始页面并保留背景、图片和版式,不再清空源幻灯片后生成统一深蓝基础模板。
|
|
|
|
|
|
- 源模板中的样例业务文本、表格和图表会在动态内容写入前清理;模板页数不足或页面映射非法时明确失败,不再静默回退。
|
|
|
|
|
|
- 模板上传后记录 `assetVersion` 与 `assetSha256`;创建任务时固化资产 ID、版本、SHA-256 和渲染模式,Worker 严格消费该快照并复核文件哈希。
|
|
|
|
|
|
- `source_template_asset_id` 已扩容为 `VARCHAR(255)`;旧模板文件在仍可能被排队任务引用时不再立即物理删除。
|
|
|
|
|
|
- 海报工作区改为真实缩放画布并从顶部开始滚动;长图不再因垂直居中而无法查看顶部。
|
|
|
|
|
|
- 海报完成状态增加资源加载阶段:背景、图片和字体全部可用且最终合成保存成功后,任务才显示完成;加载失败会展示可重试错误。
|
|
|
|
|
|
- 前端生成按钮与后端 15 秒活动任务复用共同拦截快速重复提交,避免一次点击创建多条生成记录。
|
|
|
|
|
|
|
|
|
|
|
|
已完成验证:
|
|
|
|
|
|
|
|
|
|
|
|
- 三个内置模板均使用真实历史生成契约渲染,输出模式均为 `clone-edit-v2`,且页面映射与模板分别对应。
|
|
|
|
|
|
- PowerPoint 可直接打开三个生成文件,无修复提示;源模板的样例金额未出现在成品文本中。
|
|
|
|
|
|
- IUL 模板的源图片资产在成品中保留;三套模板的最终视觉不再相同。
|
|
|
|
|
|
- 迁移 `migrate_029`、`migrate_030` 已在当前 BaoDan 数据库实际执行,模板资产字段、版本、SHA-256 和 `clone-edit-v2` 模式标识已回填。
|
|
|
|
|
|
- Python 编译、Vue 生产构建及 PPT/海报相关自动化回归均通过。
|