baodan/docs/PPT与海报功能问题整改计划.md
wsb1224 e3479f0546 上线阻断问题全部修复
#	问题	修复	文件
1	前端构建失败(引号错误)	size="small type=" → size="small" type="	PosterHistoryPage.vue
2	migrate_014 ORM vs 缺失列	全部改为原始 SQL,不再引用 ORM 模型	migrate_014.py
3	cleanup 字段名错误	output_path → ppt_path	cleanup.py
4	文案生成 case 越权	添加 case.user_id != user_id 校验	poster/service.py
5	存储路径未接通持久化卷	全部改用 get_storage_root()(默认 /app/api/storage/insurance)	config.py, ppt/routes.py, poster/service.py, poster/tasks.py
高风险问题修复
#	问题	修复	文件
6	migrate_019 rollback 撤销成功字段	每个 ALTER 后立即 commit,失败只回滚当前语句	migrate_019.py
7	迁移锁 Windows 不兼容 + 句柄未持久化	全局变量保存锁句柄,支持 Windows msvcrt	api/insurance/db/__init__.py
8	PDF 校验异常时放行	异常返回 False(文件损坏)	security.py
9	健康检查始终返回成功	缺少关键资源时返回 503 + missing 列表	poster/routes.py
10	短密钥掩码泄露原值	≤4 字符返回 ****	ppt_admin_service.py
11	设置无键名白名单	添加 _ALLOWED_SETTING_KEYS 白名单	ppt_admin_service.py
12	容器重启任务永久 stuck	添加 recover_stale_tasks() 启动恢复函数	poster/tasks.py, ppt/parse_worker.py
2026-07-27 13:52:09 +08:00

43 KiB
Raw Blame History

PPT 与海报功能问题整改计划

文档版本v1.0
编制日期2026-07-27
适用范围api/insurance/ppt/api/insurance/poster/、PPT/海报管理后台、相关数据库迁移与部署配置
文档状态:待评审
目标:修复 PPT 和海报无法稳定生成的问题,打通保司、产品、模板、模型、文件和历史数据链路,并补齐安全与可运维能力


一、文档目的

当前 PPT 与海报模块已经具备页面、接口、数据库模型、迁移、LLM 调用和本地渲染代码,但各部分没有形成稳定闭环。用户表现为:

  • PPT 上传或解析后无法生成;
  • 海报没有可选产品或模板;
  • 海报生成后无法预览、无法下载;
  • 后台已经配置模型,但实际调用不生效;
  • 保司、产品、模板和知识库数据之间没有完整关联;
  • 不同部署环境、不同启动次数可能得到不同的数据状态。

本文档不是重新设计整个系统,而是基于现有代码做最小必要整改,按以下顺序处理:

  1. 修复能够直接导致生成失败、数据泄露的 P0 问题;
  2. 恢复 PPT、海报基础生成闭环
  3. 将耗时操作接入 BaoDan 现有 Celery、Redis 和数据库;
  4. 打通公共保司、产品、模板和知识库数据;
  5. 补齐安全、持久化、监控、测试和发布回滚能力。

二、审计边界与结论说明

2.1 已检查范围

范围 主要文件
PPT 前端 frontend/src/pages/PptPage.vuefrontend/src/pages/components/ppt/
海报前端 frontend/src/pages/PosterPage.vuefrontend/src/components/poster/
前端 API frontend/src/utils/ppt-api.tsposter-api.tsapi.ts
PPT 后端 api/insurance/ppt/
海报后端 api/insurance/poster/
配置管理 api/insurance/admin/ppt_admin_routes.pyppt_admin_service.py
数据模型 api/insurance/models/ppt_*poster_*system_setting.py
数据迁移 api/insurance/db/migrate_014.pymigrate_018.py
部署 Dockerfile.dify-customdocker-compose.dify.ymldeploy/sql/init.sql

2.2 已执行验证

检查项 结果
Python 代码编译检查 通过
Vue 前端生产构建 通过
当前源码环境 python-pptx 未安装
当前源码环境 PyMuPDF/fitz 未安装
静态保司配置 21 家
静态产品配置 7 个
静态 PPT 模板 7 个
海报模板种子数据 未发现
海报文案模板种子数据 未发现
PPT/海报自动化测试 未发现覆盖生成闭环的测试

2.3 结论口径

  • 本文标记为“已确认”的问题,均可由当前代码结构直接证明。
  • 外部模型账号余额、生产数据库实际内容、生产容器日志尚未在本次静态审计中验证。
  • 实施前仍需采集一次生产数据库结构、迁移历史、配置完整度和失败日志,避免覆盖现有有效数据。

三、现状调用链

3.1 PPT 当前调用链

PptUpload
  → POST /insurance/ppt/upload
  → 保存 PDF + 创建 PptSession
  → POST /insurance/ppt/parse/{sessionId}
  → Web 进程后台线程解析 PDF
  → LLMClient 提取结构化数据
  → PptDataReview 人工核对
  → POST /insurance/ppt/generate/{sessionId}
  → normalizer + validator
  → PptRenderer
  → fast_pptx_renderer.py 子进程
  → 写入 downloads/{userId}/{sessionId}.pptx
  → 鉴权下载

主要断点:

  • 源码环境缺依赖;
  • 数据迁移顺序不稳定;
  • slidesConfig 结构不一致;
  • 前端风格与数据库模板不一致;
  • 上传时选择的保司没有流转到生成阶段;
  • 校验错误没有真正阻止生成;
  • 后台线程不可恢复;
  • 输出文件未持久化。

3.2 海报当前调用链

选择 reviewed 产品
  → 上传计划书 PDF
  → 上传请求内同步调用 LLM 解析
  → 人工确认客户数据
  → 选择海报模板、文案模板
  → 模板填充或 LLM 生成文案
  → 图片模型生成
  → 失败时 Pillow 降级
  → 写入 uploads/posters
  → 普通 URL 预览和下载

主要断点:

  • 新环境没有 reviewed 产品、海报模板和文案模板;
  • 海报文案设置没有进入调用链;
  • 多图片供应商只做了页面配置,没有协议适配;
  • 上传解析和图片生成是同步请求,超过前端 60 秒超时;
  • 生成文件预览、下载不携带 JWT
  • 参考图没有从页面传到后端;
  • 图片尺寸映射错误;
  • 上传、结果文件未持久化。

四、问题总表

4.1 P0必须先修

编号 问题 影响 主要根因
PPT-P0-01 slidesConfig 数据结构不一致 匹配到模板时渲染器可能直接 KeyError 迁移写对象,渲染器和前端要求数组
DB-P0-01 迁移顺序存在循环依赖 首次部署可能没有模板或字段 014 使用新版模型017 才创建新版字段
DB-P0-02 迁移异常不回滚 一个字段冲突可导致后续迁移全部失败 PostgreSQL 事务进入 aborted 后仍继续
SEC-P0-01 任意登录用户可读取明文模型密钥 模型 API Key 泄露 settings GET 仅要求 JWT数据库明文保存
SEC-P0-02 任意登录用户可查看、导出全体 PPT 历史 用户数据、文件路径泄露 管理历史接口仅要求 JWT
SEC-P0-03 海报文案可读取其他用户的 case 数据 跨用户客户信息泄露 caseUploadId 未校验所有权
DEP-P0-01 源码与 Docker 依赖不一致 本地解析、PPT 渲染直接失败 requirements 未声明全部生成依赖
POSTER-P0-01 海报预览和下载不携带 JWT 已生成图片仍显示失败 使用 <img src>window.open
POSTER-P0-02 新环境没有完整海报基础数据 用户无法开始或无法进入下一步 无 reviewed 产品、海报/文案模板种子
POSTER-P0-03 海报文案模型配置不生效 后台配置正确但实际调用错误模型 复用了只读取 ppt_llm_* 的客户端
POSTER-P0-04 同步解析和生成超过请求超时 前端报失败,后台可能仍在执行 前端 60 秒LLM 180 秒Gunicorn 120 秒
DATA-P0-01 生成和上传文件未持久化 容器重建后历史文件全部失效 文件写在 /app/api/uploadsdownloads

4.2 P1基础可用后立即处理

编号 问题 影响
PPT-P1-01 前端风格与数据库模板风格不一致 多数模板配置永远查询不到
PPT-P1-02 上传时选择的保司在后续丢失 生成使用错误保司或要求重复选择
PPT-P1-03 公司知识库匹配没有进入生成主流程 公司介绍和证据数据为空
PPT-P1-04 多产品只用第一份数据决定模板和保司 跨险种、跨保司组合错误
PPT-P1-05 validator 错误只记录日志,不阻止生成 可输出错误保费、利益数据
POSTER-P1-01 poster_image_provider 未参与协议选择 豆包、智谱配置名义可选但实际不可用
POSTER-P1-02 图片响应只支持 b64_json 返回 URL 的供应商生成失败
POSTER-P1-03 管理员模板参考图未进入图片生成 模板参考图配置无效
POSTER-P1-04 用户填写参考图未传到下一步 页面输入无效
POSTER-P1-05 横版、方形尺寸会退回竖版 输出尺寸与用户选择不一致
POSTER-P1-06 图片 API 错误被无条件降级掩盖 配置错误被记录成“成功”
SEC-P1-01 上传仅检查扩展名或不检查 恶意文件、超大 PDF、资源耗尽
SEC-P1-02 自定义 Base URL 可访问任意地址 存在服务端请求内网风险
DATA-P1-01 JSON 与数据库形成双数据源 环境间保司、产品、模板数据漂移
DATA-P1-02 保留天数配置没有清理任务 客户数据、PDF、prompt 永久保留
API-P1-01 成功响应解包不统一 前端大量 data?.data ?? data,容易误判
TASK-P1-01 后台 daemon thread 不可恢复 Worker 重启后解析任务永久丢失
TASK-P1-02 生成缺少幂等控制 重复点击导致重复计费、并发覆盖文件
OBS-P1-01 health 只返回 ok 依赖、模型、模板、磁盘异常无法发现
TEST-P1-01 缺少端到端测试 每次改动都可能再次断链

4.3 P2稳定性与体验优化

编号 问题 影响
DATA-P2-01 公司模型部分字段没有通过 to_dict 返回 shortEn 等品牌信息无法进入渲染器
DATA-P2-02 管理后台不能完整维护评级、亮点、知识目录 公共保司数据只能部分编辑
TASK-P2-01 LLM 限流只在单进程内生效 多 Worker 时无法控制总调用量
TASK-P2-02 配置保存后缓存最长 60 秒才生效 管理员误以为设置无效
UX-P2-01 降级图、AI 图无明显区分 用户无法判断实际生成方式
UX-P2-02 刷新页面后无法恢复生成步骤 长任务体验不稳定

五、重点问题根因与修复要求

5.1 PPT-P0-01slidesConfig 结构错误

现状

  • migrate_017.py 写入:
{
  "slides": [
    {"pageType": "cover"}
  ]
}
  • PptTemplatesAdmin.vuePptTemplate.to_dict() 和渲染器期望:
[
  {"pageType": "cover"}
]
  • 渲染器 _slide_meta() 对对象使用整数下标,可能抛出 KeyError: 0

修改方案

  1. migrate_017._build_default_slides_config() 返回值统一为数组。
  2. 管理端、模型层、渲染器只接受数组,不再兼容多种永久格式。
  3. 新增修复迁移,将历史对象结构转换为数组:
如果值是 {"slides": [...]} → 提取 slides
如果值已经是数组 → 原样保留
如果为空 → 根据 required_page_types_json 生成
如果无法解析 → 记录异常,不静默覆盖
  1. PptTemplate.to_dict() 中增加安全解析,非法 JSON 返回明确配置错误。
  2. 渲染前验证 slidesConfig
    • 必须为数组;
    • 每项必须有 pageType
    • pageType 必须在允许集合内;
    • 至少包含一页。

涉及文件

  • api/insurance/db/migrate_017.py
  • 新增 api/insurance/db/migrate_019.py
  • api/insurance/models/ppt_config.py
  • api/insurance/ppt/scripts/fast_pptx_renderer.py
  • api/insurance/admin/ppt_admin_service.py
  • frontend/src/pages/admin/PptTemplatesAdmin.vue

验收

  • 历史对象结构能够自动转为数组;
  • 所有启用模板均能生成至少一页 PPT
  • 非法结构在管理后台明确显示“模板配置无效”;
  • 不再出现 KeyError: 0

5.2 DB-P0-01/P0-02迁移顺序与事务

现状

  1. deploy/sql/init.sql 的模板表缺少 slides_config_json
  2. migrate_014 使用包含该字段的新版 ORM 模型。
  3. migrate_017 才添加字段。
  4. migrate_015._add_column() 捕获错误后不回滚。
  5. run_migrations() 捕获迁移异常后不统一回滚。
  6. 每个 Web Worker 启动时都可能并发执行迁移。

修改原则

  • 已经在生产执行过的迁移文件不作为唯一修复手段;
  • 修正旧迁移以保证新环境可部署;
  • 另加一个幂等修复迁移处理已有环境;
  • 单个迁移必须原子提交;
  • 失败必须回滚并停止,不允许“带病继续”;
  • 迁移只能由一个进程执行。

修改方案

  1. 更新 deploy/sql/init.sql,使新建表结构与当前模型一致。
  2. 调整 migrate_014
    • 使用 SQLAlchemy Inspector 检查表和字段;
    • 不在字段不存在时通过完整 ORM 模型查询;
    • 种子导入改为显式 SQL 或确保最新列已存在。
  3. 修复 migrate_015
    • 添加字段前先检查,不通过异常判断“已存在”;
    • 捕获异常时 rollback
    • 任一必要字段失败时抛出异常。
  4. 修复迁移调度器:
    • 每个迁移开始前 rollback 残留事务;
    • 迁移失败后 rollback
    • 不记录失败迁移;
    • 必要迁移失败时停止应用启动。
  5. PostgreSQL 使用 advisory lock避免多个 Gunicorn Worker 同时迁移。
  6. 新增 migrate_019.py
    • 补齐缺失字段;
    • 修复 slides_config_json
    • 校验 PPT、海报表
    • 补必要索引;
    • 输出修复数量。
  7. 提供只读迁移预检命令,显示:
    • 当前 schema
    • 已执行迁移;
    • 缺失字段;
    • 异常配置数量。

涉及文件

  • deploy/sql/init.sql
  • api/insurance/db/__init__.py
  • api/insurance/db/migrate_014.py
  • api/insurance/db/migrate_015.py
  • api/insurance/db/migrate_017.py
  • api/insurance/db/migrate_018.py
  • 新增 api/insurance/db/migrate_019.py
  • scripts/setup/patch_app.py

验收

  • 空数据库首次启动一次即可得到完整表、保司、产品和模板;
  • 同一迁移重复执行三次结果一致;
  • 两个进程并发启动时只有一个执行迁移;
  • 故意制造字段冲突时能够 rollback并阻止应用误报启动成功
  • SQLite 若继续声明支持017/018 必须通过 SQLite 测试;否则文档明确只支持 PostgreSQL。

5.3 SEC-P0-01模型密钥保护

现状

  • GET /insurance/admin/ppt/settings 只要求登录;
  • 返回所有 system_settings 原始值;
  • API Key 以明文保存;
  • 前端加载后会显示完整值,并在保存时再次回传。

修改方案

  1. settings GET、available-models 和历史管理接口增加明确权限。
  2. 设置项建立允许列表,禁止任意 key 写入。
  3. 密钥类字段加密存储:
    • 复用项目已有密钥/加密能力;
    • 数据库只保存密文;
    • 日志不得输出明文。
  4. GET 只返回:
{
  "poster_image_api_key_configured": true,
  "poster_image_api_key_masked": "sk-****abcd"
}
  1. PUT 语义:
    • 字段缺失:保持不变;
    • 空字符串:不覆盖旧值;
    • 明确 clear=true:删除密钥;
    • 新密钥:加密后保存。
  2. 增加密钥修改审计日志。
  3. 对现有明文数据执行一次加密迁移。

涉及文件

  • api/insurance/admin/ppt_admin_routes.py
  • api/insurance/admin/ppt_admin_service.py
  • api/insurance/models/system_setting.py
  • frontend/src/pages/admin/PptSettingsAdmin.vue
  • frontend/src/utils/ppt-admin-api.ts
  • 新增密钥加解密工具或复用现有工具

验收

  • 普通用户请求 settings 返回 403
  • 管理员看不到完整旧密钥;
  • 数据库中不存在可直接使用的明文 API Key
  • 日志、错误响应和前端状态中不包含完整密钥。

5.4 SEC-P0-02/P0-03历史权限与跨用户数据

修改方案

  1. 管理历史列表、导出、删除统一要求 audit_viewconfig_manage
  2. 应用 get_data_scope()
    • 超级管理员:全部;
    • 部门主管:本部门;
    • 普通用户:仅本人。
  3. 海报 generate_copy() 必须校验:
    • case 存在;
    • case.user_id == current_user
    • case 已确认;
    • case.product_id 与请求 productId 一致。
  4. 海报 generate_poster() 同样校验 case、产品和模板。
  5. 所有按 ID 查询的用户数据接口建立统一所有权检查。
  6. 越权访问统一返回 404避免暴露 ID 是否存在。

涉及文件

  • api/insurance/admin/ppt_admin_routes.py
  • api/insurance/admin/ppt_admin_service.py
  • api/insurance/poster/service.py
  • api/insurance/poster/routes.py
  • api/insurance/middleware/auth_middleware.py

验收

  • 用户 A 无法读取、生成或下载用户 B 的 case、文案和海报
  • 普通用户无法导出全体历史;
  • 权限测试覆盖本人、同部门、跨部门和管理员四种身份。

5.5 DEP-P0-01依赖统一

必须声明

依赖 用途
python-pptx PPTX 生成
PyMuPDF PDF 文本提取
PyPDF2/pypdf/pdfplumber PDF 提取回退
Pillow 海报降级图
openai OpenAI 兼容图片接口
httpx LLM HTTP 调用

修改方案

  1. api/insurance/requirements.txt 声明全部直接依赖。
  2. Dockerfile 安装该 requirements不再维护另一份手写依赖列表。
  3. 增加启动依赖自检。
  4. 快速启动、完整部署文档使用同一安装命令。
  5. 固定兼容版本区间,避免基础镜像升级导致 SDK 行为变化。

涉及文件

  • api/insurance/requirements.txt
  • Dockerfile.dify-custom
  • docs/快速启动指南.md
  • docs/部署文档_完整版.md

验收

  • 新 Python 环境安装 requirements 后可导入全部生成依赖;
  • Docker 构建阶段执行导入检查;
  • 使用样例 DeckContract 能生成有效 PPTX
  • 无图片 API Key 时能生成明确标记的 fallback PNG。

5.6 POSTER-P0-01鉴权预览和下载

修改方案

  1. 海报 API 增加 Blob 下载方法,与 PPT 下载方式一致。
  2. 前端通过 Axios 请求 Blob创建临时 Object URL 供 <el-image> 预览。
  3. 下载按钮复用同一 Blob不再 window.open 受保护 URL。
  4. 组件卸载或重新生成时释放 Object URL。
  5. PPT 的 window.open 降级路径一并移除。
  6. 历史页使用相同认证图片加载组件。

涉及文件

  • frontend/src/utils/poster-api.ts
  • frontend/src/components/poster/PosterStepPreview.vue
  • frontend/src/pages/PosterHistoryPage.vue
  • frontend/src/pages/components/ppt/PptResult.vue

验收

  • 登录模式和访客模式均能预览、下载本人海报;
  • 浏览器网络请求携带 Authorization
  • 用户之间不能互相下载;
  • 多次预览不持续占用 Blob 内存。

5.7 POSTER-P0-02基础数据初始化

修改方案

  1. 至少准备:
    • 1 个 reviewed 测试产品;
    • 2 个海报模板;
    • 2 个文案模板。
  2. 正式产品仍必须走“小册子解析 → 人工审核 → reviewed”。
  3. 开发/验收种子与生产业务数据区分:
    • 开发环境自动导入;
    • 生产环境提供显式初始化命令;
    • 不自动覆盖管理员修改。
  4. 页面空状态明确区分:
    • 没有产品;
    • 产品未审核;
    • 没有模板;
    • 没有文案模板。
  5. 管理后台增加配置完整度提示。

涉及文件

  • 新增 api/insurance/poster/config/ 或数据库种子文件
  • 新增修复迁移/初始化脚本
  • frontend/src/components/poster/PosterStepProduct.vue
  • frontend/src/components/poster/PosterStepTemplate.vue
  • 管理后台相关页面

验收

  • 空数据库初始化后可以完整走通一条海报流程;
  • 生产环境不会把未审核产品自动标为 reviewed
  • 缺配置时页面给出管理员可执行的处理提示。

5.8 POSTER-P0-03/P1-01模型配置和供应商适配

目标

保留一套通用调用接口,但按用途读取不同配置:

ppt_extract   → ppt_llm_*
poster_copy   → poster_llm_*
poster_image  → poster_image_*

修改方案

  1. LLMClient 支持传入用途或配置命名空间,不让海报文案继续读取 PPT 配置。
  2. 不配置模型时不再返回 mock {};正式接口应返回“模型未配置”错误。
  3. 结构化输出执行真实 Schema/Pydantic 校验。
  4. 图片供应商按协议拆分最小适配器:
    • OpenAI
    • 豆包;
    • 智谱;
    • 自定义 OpenAI-compatible。
  5. 同时支持:
    • b64_json
    • 图片 URL 下载;
    • 各供应商允许的尺寸;
    • 参考图是否受支持。
  6. 保存配置前提供测试连接。
  7. 图片生成记录增加:
    • provider
    • model
    • generation_mode
    • error_code
    • latency
    • fallback_reason。
  8. AI 失败时是否允许 fallback 由明确配置决定。

涉及文件

  • api/insurance/ppt/llm_client.py
  • api/insurance/poster/copy_generator.py
  • api/insurance/poster/manual_parser.py
  • api/insurance/poster/image_generator.py
  • api/insurance/poster/service.py
  • api/insurance/models/poster_record.py
  • frontend/src/pages/admin/PptSettingsAdmin.vue

验收

  • 海报文案实际使用后台配置的 poster_llm_*
  • 每个声明支持的图片供应商至少有一个契约测试;
  • 错误 Key、错误模型、欠费、超时返回不同错误码
  • 空对象或缺少标题、正文、行动号召时不能进入下一步。

5.9 POSTER-P0-04/TASK-P1-01异步任务

目标状态机

created
  → queued
  → parsing / generating
  → review_required / done
  → failed

修改方案

  1. 复用 BaoDan Celery不新增独立任务系统。
  2. 上传接口只完成:
    • 文件校验;
    • 保存;
    • 创建记录;
    • 投递任务;
    • 返回任务 ID。
  3. Celery 负责:
    • PDF 解析;
    • 产品小册子解析;
    • 海报图片生成;
    • 必要时 PPT 渲染。
  4. Redis 只用于任务锁和进度,不作为最终状态唯一来源。
  5. 数据库保存最终状态、错误和结果路径。
  6. 增加幂等键:
    • 同一 session 同一版本只生成一次;
    • 重复点击返回现有任务;
    • 明确“重新生成”时创建新版本。
  7. Redis 锁使用随机 owner token释放时比较 owner。
  8. 前端按 2 秒轮询,连续错误采用退避。
  9. 页面刷新后根据 session/record ID 恢复进度。
  10. Celery 重试仅覆盖网络、限流等可重试错误;数据错误不重试。

接口调整

接口 调整
POST /poster/case-upload 立即返回 case ID 和 queued
GET /poster/case-upload/{id} 返回解析进度、状态、错误
POST /poster/generate 返回 generation record/task ID
GET /poster/records/{id} 返回生成进度、模式、文件状态
POST /ppt/generate/{sessionId} 建议返回任务状态,保留兼容期

验收

  • 创建任务接口 3 秒内返回;
  • 5 分钟解析任务不会导致浏览器请求超时;
  • Worker 重启后任务可重试或明确失败;
  • 重复点击不会重复扣费;
  • 用户可以刷新页面并恢复进度。

5.10 DATA-P0-01文件持久化

统一目录

/app/api/storage/insurance/
├── uploads/
│   ├── ppt/
│   ├── poster-cases/
│   └── manuals/
├── outputs/
│   ├── ppt/
│   └── posters/
└── work/

修改方案

  1. 新增 INSURANCE_STORAGE_ROOT 配置,默认指向 BaoDan 持久化 storage。
  2. 数据库保存相对对象键,不保存依赖当前工作目录的绝对路径。
  3. 下载时通过统一 StorageService 解析路径。
  4. 使用 os.path.commonpath 校验安全目录,不使用字符串 startswith
  5. Docker API、Worker 共享同一 storage volume。
  6. 工作目录可清理,结果目录按保留策略清理。
  7. 对旧路径提供一次迁移/兼容读取。

涉及文件

  • api/insurance/config.py
  • api/insurance/ppt/routes.py
  • api/insurance/ppt/renderer.py
  • api/insurance/poster/service.py
  • api/insurance/poster/routes.py
  • docker-compose.dify.yml
  • Worker volume 配置

验收

  • API 容器重建后历史文件仍能下载;
  • Worker 生成的文件 API 能读取;
  • 非 storage 路径无法通过下载接口访问;
  • 清理任务不会误删仍在保留期的文件。

5.11 PPT-P1-01P1-04模板、保司、产品和知识库

统一运行时数据源

运行时以数据库为唯一权威数据源JSON 仅作为版本化种子,不在请求中直接读取。

生成上下文

{
  "customer": {},
  "plans": [
    {
      "planType": "savings",
      "company": {},
      "product": {},
      "extractedData": {},
      "evidence": []
    }
  ],
  "template": {},
  "generation": {}
}

修改方案

  1. 上传时的 companyId 保留到每个 extraction。
  2. 解析后用产品名称、别名匹配产品和保司。
  3. 匹配优先级:
    • 用户强制选择;
    • 产品目录精确匹配;
    • 产品别名;
    • 保司别名;
    • 无法确定时人工确认。
  4. 低置信度匹配不得自动进入正式生成。
  5. company-kb/match 接入解析完成流程。
  6. 调用 BaoDan 知识库获取公司介绍证据,结果保存:
    • 文档 ID
    • 文件名;
    • 片段;
    • 版本/更新时间。
  7. 多产品按每份 plan 保存独立保司,不再使用一个全局 companyId。
  8. 前端不硬编码风格列表,按险种和产品读取可用模板。
  9. 模板选择校验:
    • status
    • planType
    • applicableCompanyIds
    • applicableProductIds。
  10. 公司 to_dict() 补齐品牌字段,管理后台允许维护知识目录、评级和亮点。
  11. 增加显式种子 upsert 命令,默认只新增,不覆盖人工修改。

涉及文件

  • api/insurance/ppt/routes.py
  • api/insurance/ppt/parse_worker.py 或对应 Celery task
  • api/insurance/ppt/knowledge.py
  • api/insurance/ppt/renderer.py
  • api/insurance/models/ppt_config.py
  • api/insurance/admin/ppt_admin_service.py
  • frontend/src/pages/components/ppt/PptUpload.vue
  • frontend/src/pages/components/ppt/PptDataReview.vue
  • frontend/src/pages/components/ppt/PptGenerate.vue

验收

  • 同一产品在 PPT 和海报中使用相同保司名称、Logo 和卖点;
  • 上传阶段选择的保司自动带入生成阶段;
  • 两家保司的组合方案分别显示各自公司信息;
  • 公司介绍可以追溯到知识库证据;
  • 前端只展示当前方案真正可用的模板。

5.12 PPT-P1-05正式数据校验

修改方案

  1. 统一校验级别:
    • error:禁止生成;
    • warn:用户确认后允许;
    • info:展示但不阻塞。
  2. 生成接口必须再次服务端校验,不能依赖前端。
  3. 用户确认 warning 时保存:
    • 确认人;
    • 时间;
    • 校验快照;
    • 数据版本。
  4. PPT 历史记录保存正式输入快照和校验摘要。
  5. 关键保险数字禁止由 LLM 补造。

验收

  • 构造缺少保费、缴费年期或利益表的数据时生成返回 4xx
  • warning 未确认不能生成;
  • 已生成文件可追溯到输入数据和校验版本。

5.13 SEC-P1-01/P1-02上传与外部请求安全

文件上传规则

项目 建议规则
文件类型 仅 PDF
文件头 必须为合法 PDF
单文件大小 配置化,默认不超过 30MB
页数 配置化,默认不超过 200 页
单会话文件数 默认不超过 3
加密 PDF 明确拒绝并提示
文件名 不使用原始文件名作为物理路径
解析超时 任务级限制

自定义 Base URL 规则

  • 默认只允许 HTTPS
  • 拒绝 localhost、环回、私网、链路本地和云元数据地址
  • DNS 解析后校验最终 IP
  • 禁止跟随到内网的重定向;
  • 可配置供应商域名白名单;
  • 连接测试设置短超时和响应大小限制。

验收

  • 伪造扩展名、超大文件、加密 PDF、超页数 PDF 被拒绝;
  • 127.0.0.1169.254.169.254、私网地址不能作为自定义 Base URL
  • 文件拒绝不创建脏记录。

5.14 DATA-P1-02隐私、保留和清理

敏感数据

  • 上传的计划书 PDF
  • 客户姓名、年龄、性别;
  • 保额、保费、利益演示;
  • 解析结果和人工确认数据;
  • AI 原始文案;
  • 生成 prompt
  • 模型调用日志。

修改方案

  1. 明确保留策略的适用对象,而不只配置历史记录天数。
  2. 增加每日 Celery 清理任务。
  3. 删除顺序:
    • 标记待删除;
    • 删除物理文件;
    • 清理或匿名化数据库数据;
    • 写审计记录。
  4. prompt 日志默认不保存完整客户数据,保存脱敏摘要或 hash。
  5. 发送外部模型前记录供应商、用途和数据范围。
  6. 管理后台提供按用户/会话删除能力。
  7. 明确失败任务和临时工作目录的短保留期。

验收

  • 将保留期设置为测试值后,过期文件和记录能自动清理;
  • 清理任务可重复执行;
  • 日志中搜索不到完整保额、保费和 API Key
  • 删除后历史接口不再返回失效下载链接。

5.15 API-P1-01接口响应统一

统一格式

{
  "code": 0,
  "message": "success",
  "data": {}
}

修改方案

  1. PPT、海报路由统一使用 success()error()
  2. Service 返回业务数据或抛出业务异常,不返回第二层完整响应包装。
  3. 前端 Axios 拦截器只解包一次。
  4. 删除:
res?.data?.data ?? res?.data
  1. 为任务类接口统一字段:
    • taskId
    • status
    • progress
    • message
    • errorCode
    • retryable
  2. 在 API 文档补齐请求、响应和错误码。

验收

  • 所有 PPT/海报成功响应只有一层 data
  • 4xx/5xx 错误码与页面提示一致;
  • TypeScript 为接口响应提供明确类型,不再使用主要流程 any

六、文件级修改清单

文件/目录 修改内容 优先级
api/insurance/db/__init__.py 迁移锁、失败回滚、失败停止、原子记录 P0
api/insurance/db/migrate_014.py 修复模板种子导入顺序 P0
api/insurance/db/migrate_015.py Inspector 检查字段,移除异常驱动流程 P0
api/insurance/db/migrate_017.py slidesConfig 改为数组 P0
api/insurance/db/migrate_018.py 数据库方言兼容 P1
api/insurance/db/migrate_019.py 修复已有 schema 和模板数据 P0
deploy/sql/init.sql 同步最新表结构 P0
api/insurance/requirements.txt 补齐 PPT、PDF、海报依赖 P0
Dockerfile.dify-custom 安装统一 requirements 并自检 P0
api/insurance/ppt/routes.py 模板校验、正式数据校验、异步生成、持久化路径 P0/P1
api/insurance/ppt/parse_worker.py 迁移到 Celery保留兼容入口后删除线程 P1
api/insurance/ppt/renderer.py 配置预检、输出校验、持久化路径 P0
api/insurance/ppt/scripts/fast_pptx_renderer.py 严格验证 slidesConfig失败不误报成功 P0
api/insurance/ppt/llm_client.py 配置命名空间、真实 Schema 校验、禁用 mock 成功 P0
api/insurance/ppt/knowledge.py 接入真实知识库证据 P1
api/insurance/poster/service.py 所有权、状态校验、异步任务、统一响应 P0
api/insurance/poster/image_generator.py 供应商适配、尺寸、响应格式、fallback 标记 P0/P1
api/insurance/poster/copy_generator.py 使用 poster LLM 配置并校验输出 P0
api/insurance/poster/routes.py 统一响应、鉴权下载、安全路径 P0
api/insurance/admin/ppt_admin_routes.py 设置、历史权限收紧 P0
api/insurance/admin/ppt_admin_service.py 密钥掩码、允许列表、配置验证 P0
api/insurance/models/system_setting.py 密钥字段处理或关联加密服务 P0
api/insurance/models/poster_record.py 任务、供应商、模式、错误字段 P1
api/insurance/models/ppt_config.py 完整公司字段、安全 JSON 解析 P1
frontend/src/utils/api.ts 任务请求策略、统一响应 P1
frontend/src/utils/poster-api.ts 鉴权 Blob、状态接口类型 P0
frontend/src/components/poster/* 恢复任务、参考图、错误和 fallback 状态 P0/P1
frontend/src/pages/components/ppt/* 动态模板、保司流转、任务恢复 P1
frontend/src/pages/admin/PptSettingsAdmin.vue 密钥掩码、连接测试、配置完整度 P0
docker-compose.dify.yml API/Worker 共享持久化 storage P0

七、分阶段实施计划

Phase 0生产现状快照与回归基线

预计工作量0.5 人日

任务

  • 备份数据库;
  • 导出 db_migration_history
  • 导出相关表字段;
  • 统计保司、产品、PPT 模板、海报模板、文案模板数量;
  • 检查 slides_config_json 的实际结构;
  • 收集最近 PPT、海报失败日志
  • 准备脱敏 PDF 样例和预期结果。

产出

  • 生产数据快照;
  • 迁移前检查报告;
  • 可重复的失败样例;
  • 回归测试基线。

完成标准

  • 能明确当前失败发生在上传、解析、模板选择、渲染还是下载阶段;
  • 所有修复均可用同一组样例复测。

Phase 1紧急 P0 修复

预计工作量2 人日

任务

  • 修复 slidesConfig 结构;
  • 修复迁移顺序、事务回滚和迁移锁;
  • 增加修复迁移 019
  • 收紧 settings、历史权限
  • 修复海报 case 所有权;
  • 补齐依赖;
  • 修复海报鉴权预览和下载;
  • 将文件根目录改到持久化 storage。

完成标准

  • 不配置外部图片模型时fallback 海报可生成、预览和下载;
  • 储蓄险样例能够生成可打开的 PPTX
  • 普通用户不能读取模型密钥和全体历史;
  • 容器重启后已生成文件仍存在。

Phase 2模型和海报基础闭环

预计工作量2 人日

任务

  • 海报文案读取 poster_llm_*
  • 图片供应商按协议适配;
  • 真实结构化输出校验;
  • 初始化海报模板和文案模板;
  • 修复参考图、尺寸;
  • 区分 AI 成功、fallback 成功和失败;
  • 增加模型连接测试。

完成标准

  • 模板文案和 AI 文案均可用;
  • 至少一个正式图片供应商通过真实测试;
  • 错误模型配置不会被误报为成功;
  • 生成记录包含真实 provider、model 和 generation_mode。

Phase 3异步任务和幂等

预计工作量2 人日

任务

  • 将海报 case 解析接入 Celery
  • 将海报图片生成接入 Celery
  • 将 PPT 解析从后台线程迁移到 Celery
  • 评估 PPT 渲染是否同步保留或异步化;
  • 增加状态、进度、错误和重试字段;
  • 增加幂等键;
  • 前端支持轮询和刷新恢复。

完成标准

  • 任务创建请求 3 秒内返回;
  • Worker 重启后状态可解释;
  • 重复点击不重复生成、计费;
  • 任务失败可区分可重试和不可重试。

Phase 4公共保司、产品、模板与知识库

预计工作量2 人日

任务

  • 建立统一 GenerationContext
  • 上传保司信息贯穿解析、审核和生成;
  • 自动匹配产品和保司;
  • 低置信度人工确认;
  • 接入 BaoDan 知识库证据;
  • 多产品独立携带公司;
  • 前端动态读取可用模板;
  • 统一 JSON 种子和数据库策略。

完成标准

  • PPT、海报共享同一保司、产品数据
  • 跨保司方案公司信息不串用;
  • 公司介绍有证据来源;
  • 模板适用范围生效。

Phase 5安全、隐私与运维

预计工作量1.5 人日

任务

  • 文件上传限制;
  • 自定义 Base URL 安全校验;
  • API Key 加密和掩码;
  • 数据保留和清理任务;
  • prompt 和日志脱敏;
  • 增强 readiness
  • 结构化日志和关键指标。

完成标准

  • 安全测试通过;
  • 过期文件可自动清理;
  • health 能识别依赖、配置和存储异常;
  • 日志不包含完整密钥和客户敏感数字。

Phase 6自动化测试、灰度和上线

预计工作量2 人日

任务

  • 单元测试;
  • API 集成测试;
  • Celery 任务测试;
  • 前端端到端测试;
  • 数据迁移演练;
  • 容器重建测试;
  • 灰度开关;
  • 上线和回滚演练;
  • 更新 API、部署、测试和变更日志文档。

完成标准

  • P0/P1 验收用例全部通过;
  • 旧数据修复结果有统计;
  • 灰度期间无跨用户访问、密钥泄露和文件丢失;
  • 回滚方案演练成功。

八、测试计划

8.1 单元测试

编号 测试内容
UT-PPT-01 对象形式 slidesConfig 转换为数组
UT-PPT-02 非法 slidesConfig 被拒绝
UT-PPT-03 风格、险种、保司、产品模板匹配
UT-PPT-04 validator error 阻止生成
UT-PPT-05 多产品分别携带公司
UT-POSTER-01 文案模板变量替换
UT-POSTER-02 海报文案缺字段被拒绝
UT-POSTER-03 各图片供应商响应解析
UT-POSTER-04 横版、方形、竖版尺寸映射
UT-SEC-01 安全文件路径判断
UT-SEC-02 Base URL 私网拦截
UT-DB-01 迁移重复执行幂等

8.2 API 集成测试

编号 场景 预期
API-PPT-01 上传合法 PDF 返回 sessionId
API-PPT-02 上传伪 PDF 4xx
API-PPT-03 解析任务提交 3 秒内返回 queued
API-PPT-04 无效数据生成 4xx包含校验问题
API-PPT-05 合法数据生成 返回任务,最终 done
API-PPT-06 用户 B 下载用户 A 文件 404
API-POSTER-01 无 reviewed 产品 返回明确配置状态
API-POSTER-02 访问他人 caseUploadId 404
API-POSTER-03 模型未配置 明确错误,不返回空文案
API-POSTER-04 图片 API 失败且允许 fallback fallback_success
API-POSTER-05 图片 API 失败且禁止 fallback failed
API-SEC-01 普通用户读取 settings 403
API-SEC-02 普通用户导出全体历史 403

8.3 端到端测试

  1. 储蓄险 PDF → 解析 → 人工核对 → PPT → 下载;
  2. 重疾险 PDF → business 模板 → PPT → 下载;
  3. IUL PDF → 数据校验 → PPT → 下载;
  4. Reviewed 产品 → 客户计划书 → 模板文案 → 海报 → 预览 → 下载;
  5. Reviewed 产品 → AI 文案 → AI 海报;
  6. 图片供应商失败 → fallback 海报;
  7. 两家保司组合 → 自动匹配 → 对比 PPT
  8. 页面刷新 → 恢复任务;
  9. API/Worker 容器重建 → 历史文件仍可下载;
  10. 普通用户越权访问 → 全部拒绝。

8.4 数据库迁移测试

至少准备四种数据库状态:

  1. 完全空库;
  2. 只执行到 014
  3. 015018 部分字段存在;
  4. slides_config_json 同时包含对象、数组、空值和非法 JSON。

每种状态验证:

  • 迁移一次成功;
  • 再执行两次无副作用;
  • 迁移失败可回滚;
  • 数据数量和模板配置符合预期。

九、监控与健康检查

9.1 健康检查分层

检查 内容
/health/live 进程存活
/health/ready DB、Redis、storage 可写、必要表字段
/ppt/health PPT 依赖、模板数量、渲染器可执行
/poster/health reviewed 产品、模板、文案模板、图片模型配置

9.2 关键指标

  • PPT 上传成功率;
  • PDF 解析成功率和平均耗时;
  • PPT 渲染成功率和页数;
  • 海报 AI 成功率;
  • 海报 fallback 比例;
  • 模型超时、限流、鉴权错误;
  • 任务排队长度;
  • 存储剩余空间;
  • 历史文件缺失数量;
  • 模板配置错误数量。

9.3 日志字段

requestId
taskId
sessionId / recordId
userId必要时脱敏
provider
model
status
durationMs
errorCode
retryable
fallbackReason

十、发布与回滚

10.1 上线前

  • 数据库备份;
  • storage 目录备份;
  • 导出迁移历史;
  • 运行迁移预检;
  • 在生产副本演练迁移 019
  • 验证旧 PPT、海报历史读取
  • 准备功能开关:
    • PPT_GENERATION_ENABLED
    • POSTER_GENERATION_ENABLED
    • POSTER_AI_IMAGE_ENABLED
    • POSTER_FALLBACK_ENABLED

10.2 灰度顺序

  1. 只开放管理员和测试账号;
  2. 开放 PPT 基础生成;
  3. 开放海报模板/fallback 模式;
  4. 开放 AI 文案;
  5. 开放 AI 图片;
  6. 开放全部用户。

10.3 回滚原则

  • 代码回滚不自动回滚已成功的数据修复;
  • 新字段保持向后兼容;
  • migration 019 不删除旧字段和旧文件;
  • 功能异常优先关闭开关,不立即执行破坏性数据库回滚;
  • 文件路径迁移期间保留旧路径兼容读取;
  • 回滚后仍需保证历史文件可下载。

10.4 回滚触发条件

  • PPT/海报生成失败率持续超过基线;
  • 出现跨用户数据访问;
  • 模型密钥暴露;
  • 迁移导致表结构异常;
  • 生成任务大量重复计费;
  • storage 文件无法读取;
  • 数据校验失效并输出错误保险数字。

十一、文档同步清单

实施代码时必须同步更新:

文档 更新内容
保险智能客服系统_API接口文档.md 异步任务、状态、错误码、鉴权
保险智能客服系统_需求文档.md PPT/海报业务规则和异常场景
保险智能客服系统_测试用例.md 本文测试用例
部署文档_完整版.md 依赖、storage、Celery、配置
快速启动指南.md 统一依赖安装和验证
PPT与海报功能开发任务清单.md 修复任务状态
CHANGELOG.md 安全、迁移、生成链路修复

十二、实施约束

  1. 自研代码仍全部位于 api/insurance/
  2. 不修改 BaoDan 核心模块来承载业务逻辑。
  3. 复用 BaoDan 数据库、Redis、Celery、日志和认证。
  4. 不在一次提交中同时重构无关模块。
  5. 每项代码修改必须关联本文问题编号。
  6. 先写失败测试,再修复对应问题。
  7. 不以 fallback 掩盖配置错误。
  8. 不以“前端已经校验”为理由跳过服务端校验。
  9. 不提交 .env、API Key、客户 PDF 或未脱敏样例。
  10. 迁移必须在生产数据副本上演练后上线。

十三、完成定义

只有同时满足以下条件PPT 与海报模块才可标记为完成:

  • 空数据库首次部署一次成功;
  • 迁移重复执行幂等;
  • PPT 三个险种样例均可生成和下载;
  • 海报模板、AI、fallback 三种路径状态明确;
  • 生成文件在容器重建后仍存在;
  • PPT、海报使用同一公共保司和产品数据
  • 模板配置实际进入渲染器;
  • 普通用户无法读取密钥、全体历史和他人 case
  • 所有耗时任务可恢复、可追踪、可幂等;
  • 关键数据错误会阻止正式生成;
  • health 能发现依赖、配置、数据库和存储问题;
  • P0/P1 自动化测试全部通过;
  • 发布和回滚演练完成;
  • API、部署、测试、变更日志文档同步完成。

十四、预计工作量

阶段 预计人日
Phase 0现状快照 0.5
Phase 1紧急 P0 2
Phase 2模型和海报闭环 2
Phase 3异步任务 2
Phase 4公共数据打通 2
Phase 5安全与运维 1.5
Phase 6测试与上线 2
合计 约 12 人日

说明:

  • 以上为单人连续开发的技术工作量估算;
  • 不包含外部模型账号申请、知识库资料整理、业务方核对产品规则的等待时间;
  • Phase 1 完成后可恢复基础可用;
  • Phase 4 完成后,公共保司、产品、模板和知识库才算真正连通;
  • Phase 6 完成后才建议面向全部生产用户开放。