# 问题 修复 文件 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
43 KiB
43 KiB
PPT 与海报功能问题整改计划
文档版本:v1.0
编制日期:2026-07-27
适用范围:api/insurance/ppt/、api/insurance/poster/、PPT/海报管理后台、相关数据库迁移与部署配置
文档状态:待评审
目标:修复 PPT 和海报无法稳定生成的问题,打通保司、产品、模板、模型、文件和历史数据链路,并补齐安全与可运维能力
一、文档目的
当前 PPT 与海报模块已经具备页面、接口、数据库模型、迁移、LLM 调用和本地渲染代码,但各部分没有形成稳定闭环。用户表现为:
- PPT 上传或解析后无法生成;
- 海报没有可选产品或模板;
- 海报生成后无法预览、无法下载;
- 后台已经配置模型,但实际调用不生效;
- 保司、产品、模板和知识库数据之间没有完整关联;
- 不同部署环境、不同启动次数可能得到不同的数据状态。
本文档不是重新设计整个系统,而是基于现有代码做最小必要整改,按以下顺序处理:
- 修复能够直接导致生成失败、数据泄露的 P0 问题;
- 恢复 PPT、海报基础生成闭环;
- 将耗时操作接入 BaoDan 现有 Celery、Redis 和数据库;
- 打通公共保司、产品、模板和知识库数据;
- 补齐安全、持久化、监控、测试和发布回滚能力。
二、审计边界与结论说明
2.1 已检查范围
| 范围 | 主要文件 |
|---|---|
| PPT 前端 | frontend/src/pages/PptPage.vue、frontend/src/pages/components/ppt/ |
| 海报前端 | frontend/src/pages/PosterPage.vue、frontend/src/components/poster/ |
| 前端 API | frontend/src/utils/ppt-api.ts、poster-api.ts、api.ts |
| PPT 后端 | api/insurance/ppt/ |
| 海报后端 | api/insurance/poster/ |
| 配置管理 | api/insurance/admin/ppt_admin_routes.py、ppt_admin_service.py |
| 数据模型 | api/insurance/models/ppt_*、poster_*、system_setting.py |
| 数据迁移 | api/insurance/db/migrate_014.py~migrate_018.py |
| 部署 | Dockerfile.dify-custom、docker-compose.dify.yml、deploy/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/uploads、downloads |
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-01:slidesConfig 结构错误
现状
migrate_017.py写入:
{
"slides": [
{"pageType": "cover"}
]
}
PptTemplatesAdmin.vue、PptTemplate.to_dict()和渲染器期望:
[
{"pageType": "cover"}
]
- 渲染器
_slide_meta()对对象使用整数下标,可能抛出KeyError: 0。
修改方案
- 将
migrate_017._build_default_slides_config()返回值统一为数组。 - 管理端、模型层、渲染器只接受数组,不再兼容多种永久格式。
- 新增修复迁移,将历史对象结构转换为数组:
如果值是 {"slides": [...]} → 提取 slides
如果值已经是数组 → 原样保留
如果为空 → 根据 required_page_types_json 生成
如果无法解析 → 记录异常,不静默覆盖
- 在
PptTemplate.to_dict()中增加安全解析,非法 JSON 返回明确配置错误。 - 渲染前验证
slidesConfig:- 必须为数组;
- 每项必须有
pageType; pageType必须在允许集合内;- 至少包含一页。
涉及文件
api/insurance/db/migrate_017.py- 新增
api/insurance/db/migrate_019.py api/insurance/models/ppt_config.pyapi/insurance/ppt/scripts/fast_pptx_renderer.pyapi/insurance/admin/ppt_admin_service.pyfrontend/src/pages/admin/PptTemplatesAdmin.vue
验收
- 历史对象结构能够自动转为数组;
- 所有启用模板均能生成至少一页 PPT;
- 非法结构在管理后台明确显示“模板配置无效”;
- 不再出现
KeyError: 0。
5.2 DB-P0-01/P0-02:迁移顺序与事务
现状
deploy/sql/init.sql的模板表缺少slides_config_json。migrate_014使用包含该字段的新版 ORM 模型。migrate_017才添加字段。migrate_015._add_column()捕获错误后不回滚。run_migrations()捕获迁移异常后不统一回滚。- 每个 Web Worker 启动时都可能并发执行迁移。
修改原则
- 已经在生产执行过的迁移文件不作为唯一修复手段;
- 修正旧迁移以保证新环境可部署;
- 另加一个幂等修复迁移处理已有环境;
- 单个迁移必须原子提交;
- 失败必须回滚并停止,不允许“带病继续”;
- 迁移只能由一个进程执行。
修改方案
- 更新
deploy/sql/init.sql,使新建表结构与当前模型一致。 - 调整
migrate_014:- 使用 SQLAlchemy Inspector 检查表和字段;
- 不在字段不存在时通过完整 ORM 模型查询;
- 种子导入改为显式 SQL 或确保最新列已存在。
- 修复
migrate_015:- 添加字段前先检查,不通过异常判断“已存在”;
- 捕获异常时 rollback;
- 任一必要字段失败时抛出异常。
- 修复迁移调度器:
- 每个迁移开始前 rollback 残留事务;
- 迁移失败后 rollback;
- 不记录失败迁移;
- 必要迁移失败时停止应用启动。
- PostgreSQL 使用 advisory lock,避免多个 Gunicorn Worker 同时迁移。
- 新增
migrate_019.py:- 补齐缺失字段;
- 修复
slides_config_json; - 校验 PPT、海报表;
- 补必要索引;
- 输出修复数量。
- 提供只读迁移预检命令,显示:
- 当前 schema;
- 已执行迁移;
- 缺失字段;
- 异常配置数量。
涉及文件
deploy/sql/init.sqlapi/insurance/db/__init__.pyapi/insurance/db/migrate_014.pyapi/insurance/db/migrate_015.pyapi/insurance/db/migrate_017.pyapi/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 以明文保存;
- 前端加载后会显示完整值,并在保存时再次回传。
修改方案
- settings GET、available-models 和历史管理接口增加明确权限。
- 设置项建立允许列表,禁止任意 key 写入。
- 密钥类字段加密存储:
- 复用项目已有密钥/加密能力;
- 数据库只保存密文;
- 日志不得输出明文。
- GET 只返回:
{
"poster_image_api_key_configured": true,
"poster_image_api_key_masked": "sk-****abcd"
}
- PUT 语义:
- 字段缺失:保持不变;
- 空字符串:不覆盖旧值;
- 明确
clear=true:删除密钥; - 新密钥:加密后保存。
- 增加密钥修改审计日志。
- 对现有明文数据执行一次加密迁移。
涉及文件
api/insurance/admin/ppt_admin_routes.pyapi/insurance/admin/ppt_admin_service.pyapi/insurance/models/system_setting.pyfrontend/src/pages/admin/PptSettingsAdmin.vuefrontend/src/utils/ppt-admin-api.ts- 新增密钥加解密工具或复用现有工具
验收
- 普通用户请求 settings 返回 403;
- 管理员看不到完整旧密钥;
- 数据库中不存在可直接使用的明文 API Key;
- 日志、错误响应和前端状态中不包含完整密钥。
5.4 SEC-P0-02/P0-03:历史权限与跨用户数据
修改方案
- 管理历史列表、导出、删除统一要求
audit_view或config_manage。 - 应用
get_data_scope():- 超级管理员:全部;
- 部门主管:本部门;
- 普通用户:仅本人。
- 海报
generate_copy()必须校验:- case 存在;
case.user_id == current_user;- case 已确认;
- case.product_id 与请求 productId 一致。
- 海报
generate_poster()同样校验 case、产品和模板。 - 所有按 ID 查询的用户数据接口建立统一所有权检查。
- 越权访问统一返回 404,避免暴露 ID 是否存在。
涉及文件
api/insurance/admin/ppt_admin_routes.pyapi/insurance/admin/ppt_admin_service.pyapi/insurance/poster/service.pyapi/insurance/poster/routes.pyapi/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 调用 |
修改方案
- 在
api/insurance/requirements.txt声明全部直接依赖。 - Dockerfile 安装该 requirements,不再维护另一份手写依赖列表。
- 增加启动依赖自检。
- 快速启动、完整部署文档使用同一安装命令。
- 固定兼容版本区间,避免基础镜像升级导致 SDK 行为变化。
涉及文件
api/insurance/requirements.txtDockerfile.dify-customdocs/快速启动指南.mddocs/部署文档_完整版.md
验收
- 新 Python 环境安装 requirements 后可导入全部生成依赖;
- Docker 构建阶段执行导入检查;
- 使用样例 DeckContract 能生成有效 PPTX;
- 无图片 API Key 时能生成明确标记的 fallback PNG。
5.6 POSTER-P0-01:鉴权预览和下载
修改方案
- 海报 API 增加 Blob 下载方法,与 PPT 下载方式一致。
- 前端通过 Axios 请求 Blob,创建临时 Object URL 供
<el-image>预览。 - 下载按钮复用同一 Blob,不再
window.open受保护 URL。 - 组件卸载或重新生成时释放 Object URL。
- PPT 的
window.open降级路径一并移除。 - 历史页使用相同认证图片加载组件。
涉及文件
frontend/src/utils/poster-api.tsfrontend/src/components/poster/PosterStepPreview.vuefrontend/src/pages/PosterHistoryPage.vuefrontend/src/pages/components/ppt/PptResult.vue
验收
- 登录模式和访客模式均能预览、下载本人海报;
- 浏览器网络请求携带 Authorization;
- 用户之间不能互相下载;
- 多次预览不持续占用 Blob 内存。
5.7 POSTER-P0-02:基础数据初始化
修改方案
- 至少准备:
- 1 个 reviewed 测试产品;
- 2 个海报模板;
- 2 个文案模板。
- 正式产品仍必须走“小册子解析 → 人工审核 → reviewed”。
- 开发/验收种子与生产业务数据区分:
- 开发环境自动导入;
- 生产环境提供显式初始化命令;
- 不自动覆盖管理员修改。
- 页面空状态明确区分:
- 没有产品;
- 产品未审核;
- 没有模板;
- 没有文案模板。
- 管理后台增加配置完整度提示。
涉及文件
- 新增
api/insurance/poster/config/或数据库种子文件 - 新增修复迁移/初始化脚本
frontend/src/components/poster/PosterStepProduct.vuefrontend/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_*
修改方案
LLMClient支持传入用途或配置命名空间,不让海报文案继续读取 PPT 配置。- 不配置模型时不再返回 mock
{};正式接口应返回“模型未配置”错误。 - 结构化输出执行真实 Schema/Pydantic 校验。
- 图片供应商按协议拆分最小适配器:
- OpenAI;
- 豆包;
- 智谱;
- 自定义 OpenAI-compatible。
- 同时支持:
b64_json;- 图片 URL 下载;
- 各供应商允许的尺寸;
- 参考图是否受支持。
- 保存配置前提供测试连接。
- 图片生成记录增加:
- provider;
- model;
- generation_mode;
- error_code;
- latency;
- fallback_reason。
- AI 失败时是否允许 fallback 由明确配置决定。
涉及文件
api/insurance/ppt/llm_client.pyapi/insurance/poster/copy_generator.pyapi/insurance/poster/manual_parser.pyapi/insurance/poster/image_generator.pyapi/insurance/poster/service.pyapi/insurance/models/poster_record.pyfrontend/src/pages/admin/PptSettingsAdmin.vue
验收
- 海报文案实际使用后台配置的
poster_llm_*; - 每个声明支持的图片供应商至少有一个契约测试;
- 错误 Key、错误模型、欠费、超时返回不同错误码;
- 空对象或缺少标题、正文、行动号召时不能进入下一步。
5.9 POSTER-P0-04/TASK-P1-01:异步任务
目标状态机
created
→ queued
→ parsing / generating
→ review_required / done
→ failed
修改方案
- 复用 BaoDan Celery,不新增独立任务系统。
- 上传接口只完成:
- 文件校验;
- 保存;
- 创建记录;
- 投递任务;
- 返回任务 ID。
- Celery 负责:
- PDF 解析;
- 产品小册子解析;
- 海报图片生成;
- 必要时 PPT 渲染。
- Redis 只用于任务锁和进度,不作为最终状态唯一来源。
- 数据库保存最终状态、错误和结果路径。
- 增加幂等键:
- 同一 session 同一版本只生成一次;
- 重复点击返回现有任务;
- 明确“重新生成”时创建新版本。
- Redis 锁使用随机 owner token,释放时比较 owner。
- 前端按 2 秒轮询,连续错误采用退避。
- 页面刷新后根据 session/record ID 恢复进度。
- 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/
修改方案
- 新增
INSURANCE_STORAGE_ROOT配置,默认指向 BaoDan 持久化 storage。 - 数据库保存相对对象键,不保存依赖当前工作目录的绝对路径。
- 下载时通过统一 StorageService 解析路径。
- 使用
os.path.commonpath校验安全目录,不使用字符串startswith。 - Docker API、Worker 共享同一 storage volume。
- 工作目录可清理,结果目录按保留策略清理。
- 对旧路径提供一次迁移/兼容读取。
涉及文件
api/insurance/config.pyapi/insurance/ppt/routes.pyapi/insurance/ppt/renderer.pyapi/insurance/poster/service.pyapi/insurance/poster/routes.pydocker-compose.dify.yml- Worker volume 配置
验收
- API 容器重建后历史文件仍能下载;
- Worker 生成的文件 API 能读取;
- 非 storage 路径无法通过下载接口访问;
- 清理任务不会误删仍在保留期的文件。
5.11 PPT-P1-01~P1-04:模板、保司、产品和知识库
统一运行时数据源
运行时以数据库为唯一权威数据源;JSON 仅作为版本化种子,不在请求中直接读取。
生成上下文
{
"customer": {},
"plans": [
{
"planType": "savings",
"company": {},
"product": {},
"extractedData": {},
"evidence": []
}
],
"template": {},
"generation": {}
}
修改方案
- 上传时的
companyId保留到每个 extraction。 - 解析后用产品名称、别名匹配产品和保司。
- 匹配优先级:
- 用户强制选择;
- 产品目录精确匹配;
- 产品别名;
- 保司别名;
- 无法确定时人工确认。
- 低置信度匹配不得自动进入正式生成。
- 将
company-kb/match接入解析完成流程。 - 调用 BaoDan 知识库获取公司介绍证据,结果保存:
- 文档 ID;
- 文件名;
- 片段;
- 版本/更新时间。
- 多产品按每份 plan 保存独立保司,不再使用一个全局 companyId。
- 前端不硬编码风格列表,按险种和产品读取可用模板。
- 模板选择校验:
- status;
- planType;
- applicableCompanyIds;
- applicableProductIds。
- 公司
to_dict()补齐品牌字段,管理后台允许维护知识目录、评级和亮点。 - 增加显式种子 upsert 命令,默认只新增,不覆盖人工修改。
涉及文件
api/insurance/ppt/routes.pyapi/insurance/ppt/parse_worker.py或对应 Celery taskapi/insurance/ppt/knowledge.pyapi/insurance/ppt/renderer.pyapi/insurance/models/ppt_config.pyapi/insurance/admin/ppt_admin_service.pyfrontend/src/pages/components/ppt/PptUpload.vuefrontend/src/pages/components/ppt/PptDataReview.vuefrontend/src/pages/components/ppt/PptGenerate.vue
验收
- 同一产品在 PPT 和海报中使用相同保司名称、Logo 和卖点;
- 上传阶段选择的保司自动带入生成阶段;
- 两家保司的组合方案分别显示各自公司信息;
- 公司介绍可以追溯到知识库证据;
- 前端只展示当前方案真正可用的模板。
5.12 PPT-P1-05:正式数据校验
修改方案
- 统一校验级别:
error:禁止生成;warn:用户确认后允许;info:展示但不阻塞。
- 生成接口必须再次服务端校验,不能依赖前端。
- 用户确认 warning 时保存:
- 确认人;
- 时间;
- 校验快照;
- 数据版本。
- PPT 历史记录保存正式输入快照和校验摘要。
- 关键保险数字禁止由 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.1、169.254.169.254、私网地址不能作为自定义 Base URL;- 文件拒绝不创建脏记录。
5.14 DATA-P1-02:隐私、保留和清理
敏感数据
- 上传的计划书 PDF;
- 客户姓名、年龄、性别;
- 保额、保费、利益演示;
- 解析结果和人工确认数据;
- AI 原始文案;
- 生成 prompt;
- 模型调用日志。
修改方案
- 明确保留策略的适用对象,而不只配置历史记录天数。
- 增加每日 Celery 清理任务。
- 删除顺序:
- 标记待删除;
- 删除物理文件;
- 清理或匿名化数据库数据;
- 写审计记录。
- prompt 日志默认不保存完整客户数据,保存脱敏摘要或 hash。
- 发送外部模型前记录供应商、用途和数据范围。
- 管理后台提供按用户/会话删除能力。
- 明确失败任务和临时工作目录的短保留期。
验收
- 将保留期设置为测试值后,过期文件和记录能自动清理;
- 清理任务可重复执行;
- 日志中搜索不到完整保额、保费和 API Key;
- 删除后历史接口不再返回失效下载链接。
5.15 API-P1-01:接口响应统一
统一格式
{
"code": 0,
"message": "success",
"data": {}
}
修改方案
- PPT、海报路由统一使用
success()、error()。 - Service 返回业务数据或抛出业务异常,不返回第二层完整响应包装。
- 前端 Axios 拦截器只解包一次。
- 删除:
res?.data?.data ?? res?.data
- 为任务类接口统一字段:
taskIdstatusprogressmessageerrorCoderetryable
- 在 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 端到端测试
- 储蓄险 PDF → 解析 → 人工核对 → PPT → 下载;
- 重疾险 PDF → business 模板 → PPT → 下载;
- IUL PDF → 数据校验 → PPT → 下载;
- Reviewed 产品 → 客户计划书 → 模板文案 → 海报 → 预览 → 下载;
- Reviewed 产品 → AI 文案 → AI 海报;
- 图片供应商失败 → fallback 海报;
- 两家保司组合 → 自动匹配 → 对比 PPT;
- 页面刷新 → 恢复任务;
- API/Worker 容器重建 → 历史文件仍可下载;
- 普通用户越权访问 → 全部拒绝。
8.4 数据库迁移测试
至少准备四种数据库状态:
- 完全空库;
- 只执行到 014;
- 015~018 部分字段存在;
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_ENABLEDPOSTER_GENERATION_ENABLEDPOSTER_AI_IMAGE_ENABLEDPOSTER_FALLBACK_ENABLED
10.2 灰度顺序
- 只开放管理员和测试账号;
- 开放 PPT 基础生成;
- 开放海报模板/fallback 模式;
- 开放 AI 文案;
- 开放 AI 图片;
- 开放全部用户。
10.3 回滚原则
- 代码回滚不自动回滚已成功的数据修复;
- 新字段保持向后兼容;
- migration 019 不删除旧字段和旧文件;
- 功能异常优先关闭开关,不立即执行破坏性数据库回滚;
- 文件路径迁移期间保留旧路径兼容读取;
- 回滚后仍需保证历史文件可下载。
10.4 回滚触发条件
- PPT/海报生成失败率持续超过基线;
- 出现跨用户数据访问;
- 模型密钥暴露;
- 迁移导致表结构异常;
- 生成任务大量重复计费;
- storage 文件无法读取;
- 数据校验失效并输出错误保险数字。
十一、文档同步清单
实施代码时必须同步更新:
| 文档 | 更新内容 |
|---|---|
保险智能客服系统_API接口文档.md |
异步任务、状态、错误码、鉴权 |
保险智能客服系统_需求文档.md |
PPT/海报业务规则和异常场景 |
保险智能客服系统_测试用例.md |
本文测试用例 |
部署文档_完整版.md |
依赖、storage、Celery、配置 |
快速启动指南.md |
统一依赖安装和验证 |
PPT与海报功能开发任务清单.md |
修复任务状态 |
CHANGELOG.md |
安全、迁移、生成链路修复 |
十二、实施约束
- 自研代码仍全部位于
api/insurance/。 - 不修改 BaoDan 核心模块来承载业务逻辑。
- 复用 BaoDan 数据库、Redis、Celery、日志和认证。
- 不在一次提交中同时重构无关模块。
- 每项代码修改必须关联本文问题编号。
- 先写失败测试,再修复对应问题。
- 不以 fallback 掩盖配置错误。
- 不以“前端已经校验”为理由跳过服务端校验。
- 不提交
.env、API Key、客户 PDF 或未脱敏样例。 - 迁移必须在生产数据副本上演练后上线。
十三、完成定义
只有同时满足以下条件,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 完成后才建议面向全部生产用户开放。