baodan/docs/0728修复文件.md
2026-07-28 16:45:14 +08:00

25 KiB
Raw Blame History

PPT / 海报多任务持久化工作台改造计划书

一、项目背景

当前 PPT 和海报生成流程存在以下问题:

  • 刷新页面后,当前任务 ID 和步骤丢失。
  • 关闭页面后,用户无法继续查看任务。
  • PPT 最终生成仍依赖长时间 HTTP 请求。
  • 海报虽然异步,但依赖 API 进程内后台线程。
  • 用户离开生成页面后,无法直观看到任务进度。
  • 历史记录主要面向完成后的下载,不是完整任务中心。
  • 一个生成页面只能维护一个当前任务。
  • 新建或重新开始会清空当前页面状态。
  • 用户无法同时挂多个 PPT、海报任务并自由切换。
  • 生成过程中继续编辑时,缺少清晰的版本规则。

本次改造目标是把 PPT 和海报从“一次性生成页面”升级为:

支持多个独立工作区、后台可靠执行、刷新恢复、全局任务常驻、任务选择切换、版本化编辑和结果追踪的生成工作台。


二、目标效果

改造完成后,用户可以:

  1. 同时创建多个 PPT 和海报任务。
  2. 在任何页面看到正在运行、排队或待处理的任务。
  3. 点击全局任务坞中的任务,切换到对应工作区。
  4. 刷新页面、关闭标签页、重新登录后继续处理。
  5. 离开生成页面后,任务仍在后台运行。
  6. 查看当前执行阶段、进度和失败原因。
  7. 在任务生成期间继续编辑下一版草稿。
  8. 查看当前成品对应的草稿版本。
  9. 修改后重新生成,不覆盖旧文件。
  10. 从任务中心找回被关闭或已完成的任务。
  11. 复制已有任务,生成不同模板、尺寸或脱敏版本。

三、现状与根因

3.1 PPT 页面状态只存在浏览器内存

当前 PPT 页使用 Vue 局部变量保存:

  • currentStep
  • sessionId

位置:PptPage.vue

刷新组件后,这些变量恢复默认值。后端 Session 虽然存在,但前端失去了关联 ID。

3.2 海报页面状态同样没有持久恢复

海报页把以下内容保存在局部变量:

  • 当前步骤
  • 产品 ID
  • 上传记录 ID
  • 模板 ID
  • 文案
  • 尺寸
  • 脱敏选项

位置:PosterPage.vue

海报预览组件中的任务状态和 recordId 也只存在组件内部:

PosterStepPreview.vue

刷新后页面无法重新连接后台任务。

3.3 PPT 最终生成是同步请求

前端调用生成接口后持续等待:

PptGenerate.vue

后端在同一 HTTP 请求里完成校验、模板加载、渲染和文件保存:

routes.py

这会受到:

  • 页面刷新
  • 网络断开
  • 网关超时
  • API Worker 回收
  • 容器重启

等因素影响。

3.4 海报使用 API 进程内守护线程

当前海报解析和生成使用:

threading.Thread(..., daemon=True)

位置:

浏览器刷新一般不会直接结束线程,但 API 进程或容器重启会中断任务。

3.5 海报存在潜在文件路径问题

海报输出保存到:

tasks.py

<storage_root>/outputs/posters

下载接口却只允许:

routes.py

uploads/posters

这可能导致海报已完成,但预览或下载仍提示文件不存在。

3.6 历史记录不是任务中心

PPT 历史主要在导出、下载时写入,无法完整展示:

  • 草稿
  • 解析中
  • 待校验
  • 排队中
  • 生成中
  • 失败待重试

因此需要独立的任务执行记录和统一任务查询。


四、最终架构

用户浏览器
    │
    ├── PPT/海报入口页
    ├── 工作区详情页
    ├── 全局任务坞
    └── 全部任务中心
            │
            ▼
    工作区 API + 任务 API
            │
    ┌───────┴────────┐
    │                │
    ▼                ▼
PPT Session      Poster Record
可编辑工作区       可编辑工作区
    │                │
    └───────┬────────┘
            ▼
 insurance_generation_tasks
 执行状态、快照、进度、错误
            │
            ▼
      BaoDan Celery
            │
     ┌──────┴──────┐
     ▼             ▼
 PPT解析/生成    海报解析/生成
            │
            ▼
      持久化文件存储

系统分为三层:

工作区

保存用户持续编辑的业务内容。

任务执行

保存一次解析或生成的输入快照、状态、进度和结果。

成品文件

按照任务 ID 和版本独立保存,避免覆盖。


五、核心业务规则

5.1 用户可以有多个工作区

例如:

PPT A张先生储蓄险建议书
PPT B李女士重疾险建议书
海报 C家庭保障朋友圈海报
海报 D讲座邀约海报

这些工作区相互独立。

5.2 不同工作区可以同时执行

不同工作区允许:

  • 同时解析
  • 同时排队
  • 同时生成
  • 一边生成一边编辑其他任务

实际并发数量由 Celery Worker 控制。

5.3 同一工作区一次只运行一个生成任务

同一工作区处于 queuedrunning 时,不允许再次提交生成。

用户可以:

  • 查看当前运行
  • 等待完成
  • 复制为新工作区后生成另一版本

这可以避免状态、进度和输出文件互相覆盖。

5.4 生成任务使用不可变输入快照

例如:

PPT 工作区当前是草稿版本 3
用户点击生成
任务保存版本 3 的输入快照
用户继续编辑,工作区变成版本 4

版本 3 的生成结果不受版本 4 修改影响。

完成后显示:

当前成品基于版本 3
当前草稿为版本 4
存在未生成修改

六、数据模型设计

6.1 PPT 工作区

继续使用现有 PptSession,新增:

字段 用途
title 用户可识别名称
workflow_step 当前业务步骤
draft_options_json 模板、公司、脱敏等草稿设置
draft_revision 当前草稿版本
generated_revision 最新成品版本
latest_task_id 最近一次任务
latest_output_path 最新成品路径
archived_at 归档时间
updated_at 最后修改时间

原有解析结果、文件、对话和预览字段继续保留。

6.2 海报工作区

扩展现有 PosterRecord,使其在用户开始海报流程时创建,而不是到最终生成时才创建。

新增:

字段 用途
title 工作区名称
workflow_step product/upload/template/preview/result
draft_status active/archived
draft_revision 当前草稿版本
generated_revision 最新成品版本
latest_task_id 最近任务
updated_at 最后修改时间
archived_at 归档时间

现有产品、模板、文案、尺寸和输出字段继续使用。

6.3 统一任务执行表

新增:

insurance_generation_tasks

建议字段:

字段 用途
id UUID
user_id 所属用户
artifact_type ppt/poster
operation parse/generate
workspace_id PPT Session 或 Poster Record ID
title_snapshot 提交时任务名称
status queued/running/done/failed/cancelled
stage 当前执行阶段
progress 0100
message 用户可读进度
error_code 结构化错误代码
error_message 用户可读错误
input_revision 本次生成使用的草稿版本
input_snapshot_json 不可变输入快照
output_json 输出路径、页数、预览等
idempotency_key 防止重复提交
celery_task_id Celery ID
attempt_count 重试次数
heartbeat_at Worker 心跳
started_at 开始时间
finished_at 完成时间
viewed_at 用户查看时间
dock_hidden_at 从任务坞隐藏时间
created_at 创建时间
updated_at 更新时间

统一任务表负责执行状态,工作区负责可编辑数据,避免状态职责混乱。


七、状态设计

7.1 工作区步骤

PPT

upload
parsing
review
ready
generating
result

海报:

product
upload
review
template
preview
generating
result

7.2 任务执行状态

queued
   │
   ▼
running ─────► done
   │
   └─────────► failed

MVP 支持取消尚未执行的 queued 任务。

运行中的强制取消不进入 MVP因为外部模型请求通常无法保证立即终止。

7.3 PPT 阶段

validating
normalizing
loading_template
building_slides
rendering
creating_preview
saving
completed

7.4 海报阶段

preparing_data
building_prompt
requesting_image
processing_image
saving
completed

进度必须跟实际阶段更新,不做虚假的自动增长。


八、后台任务改造

8.1 复用 BaoDan Celery

BaoDan 已有 Celery、Redis和 Worker

ext_celery.py

自研任务建议放入:

api/insurance/generation/
├── __init__.py
├── celery_tasks.py
├── task_service.py
├── task_query.py
└── task_state.py

任务包括:

  • parse_ppt_workspace
  • generate_ppt_workspace
  • parse_poster_case
  • generate_poster_workspace

8.2 不修改 BaoDan Celery 基座

通过 insurance 初始化过程显式导入自研 task 模块完成注册。

实施前必须验证:

Worker 启动
→ insurance task 出现在 registered tasks
→ API 提交测试任务
→ Worker 成功领取
→ 数据库状态更新

如果未注册,只调整自研初始化和容器启动配置,不直接修改 BaoDan Celery 源码。

8.3 API 立即返回

生成接口只负责:

  1. 校验工作区
  2. 检查并发和幂等
  3. 创建任务记录
  4. 保存输入快照
  5. 发送 Celery 任务
  6. 返回 202 Accepted

不再等待生成完成。

8.4 Worker 幂等领取

Worker 执行前通过数据库事务把:

queued → running

只有成功更新的 Worker 才能执行。

如果任务已经完成,重复投递直接退出。

8.5 重试策略

自动重试:

  • 网络超时
  • 模型接口 429
  • 模型接口 5xx
  • Redis/数据库暂时不可用
  • 存储暂时不可用

不自动重试:

  • 文件格式错误
  • 数据校验失败
  • 模板不存在
  • 配置缺失
  • 用户权限错误

建议重试:

最多 3 次
30 秒 → 90 秒 → 270 秒

8.6 Worker 异常恢复

使用:

  • late acknowledgement
  • Worker 丢失重新投递
  • 数据库幂等
  • 心跳更新
  • 任务软超时和硬超时
  • 临时文件写入
  • 成功后原子替换

超时判断改用 heartbeat_at,不再使用工作区 created_at


九、文件存储设计

9.1 PPT

当前 {sessionId}.pptx 容易在重新生成时覆盖。

改为:

outputs/ppt/{userId}/{workspaceId}/{taskId}/result.pptx
outputs/ppt/{userId}/{workspaceId}/{taskId}/preview.pdf
outputs/ppt/{userId}/{workspaceId}/{taskId}/previews/

9.2 海报

改为:

outputs/posters/{userId}/{workspaceId}/{taskId}/result.png

9.3 安全规则

下载接口必须:

  • 根据数据库记录解析文件
  • 校验 user_id
  • 校验文件位于配置的 storage root 下
  • 不向前端返回服务器绝对路径
  • 不使用写死的 uploads/posters 判断

十、API 设计

10.1 PPT 工作区

POST  /insurance/ppt/workspaces
GET   /insurance/ppt/workspaces/{id}
PATCH /insurance/ppt/workspaces/{id}
POST  /insurance/ppt/workspaces/{id}/parse
POST  /insurance/ppt/workspaces/{id}/generate
POST  /insurance/ppt/workspaces/{id}/retry
POST  /insurance/ppt/workspaces/{id}/duplicate
POST  /insurance/ppt/workspaces/{id}/archive

现有接口暂时保留并转发到新服务,保证兼容。

10.2 海报工作区

POST  /insurance/poster/workspaces
GET   /insurance/poster/workspaces/{id}
PATCH /insurance/poster/workspaces/{id}
POST  /insurance/poster/workspaces/{id}/generate-copy
POST  /insurance/poster/workspaces/{id}/generate
POST  /insurance/poster/workspaces/{id}/retry
POST  /insurance/poster/workspaces/{id}/duplicate
POST  /insurance/poster/workspaces/{id}/archive

10.3 统一任务接口

GET  /insurance/generation/tasks
GET  /insurance/generation/tasks/active
GET  /insurance/generation/tasks/{taskId}
POST /insurance/generation/tasks/{taskId}/retry
POST /insurance/generation/tasks/{taskId}/cancel
POST /insurance/generation/tasks/{taskId}/mark-viewed
POST /insurance/generation/tasks/{taskId}/hide-from-dock

10.4 幂等提交

前端每次点击生成时创建:

Idempotency-Key: UUID

同一次网络重试复用相同 Key。

用户明确重新生成或创建新工作区时生成新 Key。

不能只根据输入内容相同就合并任务。


十一、前端多任务工作台

11.1 路由

新增:

/ppt
/ppt/task/:workspaceId
/poster
/poster/task/:workspaceId
/tasks

/ppt/poster 作为入口页,不再代表唯一当前任务。

11.2 PPT/海报入口页

入口页展示:

  • 新建任务
  • 进行中
  • 草稿
  • 失败待处理
  • 最近完成

创建新任务不会清空旧任务。

11.3 全局任务坞

任务坞挂载在全局布局:

App.vue

桌面端:

  • 页面底部常驻折叠栏
  • 横向显示活跃任务
  • 当前任务高亮
  • 支持打开、隐藏、查看全部

移动端:

  • 显示“任务 N”浮动按钮
  • 点击打开底部抽屉

每个任务显示:

  • 类型
  • 名称
  • 状态
  • 阶段
  • 进度
  • 失败标记
  • 未读完成标记
  • 未生成修改标记

11.4 任务坞展示范围

显示:

  • 草稿
  • 解析中
  • 排队中
  • 生成中
  • 失败待处理
  • 完成未查看

“从任务坞关闭”只隐藏,不删除、不终止。

11.5 全局状态管理

项目当前没有 Pinia。

MVP 新增:

frontend/src/composables/useGenerationTasks.ts

使用模块级响应式状态管理:

  • 活跃任务
  • 当前任务
  • 批量刷新
  • 未读完成数量
  • 打开任务
  • 隐藏任务

后端仍是唯一事实来源。


十二、轮询与状态同步

全局任务坞

有活跃任务:每 5 秒批量查询
没有活跃任务:停止轮询
浏览器后台:每 15 秒
重新可见:立即查询

当前详情页

解析或生成中:每 2 秒查询
完成或失败:停止轮询

批量接口

任务坞只能调用一次:

GET /insurance/generation/tasks/active

不能为每个任务分别建立定时请求。

跨标签页

可使用 BroadcastChannel 同步:

  • 任务创建
  • 草稿更新
  • 任务完成
  • 任务隐藏

不支持时使用服务器轮询自然恢复。


十三、草稿保存和编辑版本

13.1 自动保存

文本、模板、尺寸等修改后800 毫秒防抖保存。

显示:

保存中
已保存
保存失败

13.2 乐观锁

保存时提交:

{
  "revision": 4,
  "draft": {}
}

服务端版本不一致时返回 409 Conflict

13.3 生成期间继续编辑

允许编辑,但修改只进入下一草稿版本。

正在执行的任务始终读取提交时的 input_snapshot_json

13.4 本地缓存

如果保留本地临时草稿Key 必须包含:

用户 ID + 类型 + 工作区 ID

例如:

draft:user-1:ppt:abc123

本地缓存只用于临时容错,不能代替后端保存。


十四、任务命名与选择

PPT 自动名称

客户名称 + 产品名称 + PPT

无法识别时:

PPT任务 2026-07-28 14:30

海报自动名称

产品名称 + 场景名称

例如:

XX储蓄计划 · 朋友圈海报

支持用户手动重命名。

任务名称只用于识别,不改变文件内容。


十五、并发与配额

推荐默认规则:

项目 默认限制
活跃草稿 50 个/用户
排队+运行任务 5 个/用户
同一工作区运行任务 1 个
实际同时运行数量 由 Worker 并发控制
超出处理能力 保持 queued

系统允许用户同时提交多个任务,但不承诺全部立即并行。

同一工作区已有任务时,接口返回:

{
  "code": 4091,
  "message": "当前工作区已有生成任务",
  "data": {
    "taskId": "...",
    "status": "running"
  }
}

前端提供:

  • 查看当前运行
  • 复制为新任务

十六、任务操作定义

操作 行为
打开 进入工作区
新建 创建独立工作区
复制 复制草稿为新工作区
从任务坞关闭 只隐藏
归档 移出活跃列表,保留数据和文件
取消排队 仅取消 queued
重试 创建新的执行记录
删除 二次确认,运行中禁止
下载 下载指定 task 的成品

运行中强制停止放在后续版本。


十七、任务排序

任务坞顺序:

  1. 当前打开
  2. 失败待处理
  3. 运行中
  4. 排队中
  5. 解析中
  6. 有未生成修改的草稿
  7. 完成未查看

任务中心默认按最后更新时间倒序。


十八、通知

MVP

  • 全局任务坞状态变化
  • 完成未读标记
  • 在线完成消息
  • 浏览器标题提示

后续:

  • 站内通知
  • 邮件
  • 企业微信通知

通知失败不能影响任务完成状态。


十九、存储与生命周期

建议初始规则:

活跃草稿:最多 50 个/用户
排队+运行:最多 5 个/用户
完成文件:保留 90 天
失败临时文件7 天后清理

清理任务必须:

  • 只处理满足保留规则的文件
  • 通过数据库记录定位
  • 校验 storage root
  • 不因任务坞隐藏而删除
  • 不删除运行中的文件

二十、安全要求

  • 所有工作区和任务查询必须校验 user_id
  • 用户不能通过修改 URL 查看他人任务。
  • 下载必须基于数据库授权。
  • 前端不得获得绝对文件路径。
  • 输入快照可能包含客户数据,日志中不能完整打印。
  • 任务错误信息对用户脱敏,完整异常写入服务日志。
  • 复制任务时重新校验源工作区所有权。
  • 管理员任务监控必须使用独立权限。

二十一、实施阶段

阶段 0复现与技术验证

预计1 人日。

任务:

  • 复现 PPT/海报各阶段刷新。
  • 验证海报路径问题。
  • 验证 Celery 自研任务注册。
  • 验证 API/Worker 共享存储。
  • 记录现有数据库状态和文件结果。

验收:

  • 能区分页面丢失、HTTP 中断、Worker 中断和文件访问失败。

阶段 1数据库与工作区

预计23 人日。

任务:

  • 新增数据库迁移。
  • 扩展 PptSession
  • 扩展 PosterRecord
  • 新增 insurance_generation_tasks
  • 新增工作区查询、保存、重命名、归档接口。
  • 兼容旧记录。

验收:

  • 多个工作区可独立创建、查询和保存。

阶段 2Celery 后台任务

预计34 人日。

任务:

  • 新增自研 Celery tasks。
  • 迁移 PPT 解析。
  • 异步化 PPT 生成。
  • 迁移海报解析。
  • 迁移海报生成。
  • 增加状态、阶段、进度、心跳和重试。
  • 替换后台线程。
  • 修复海报文件路径。

验收:

  • 浏览器关闭后任务继续。
  • API 重启不影响已进入 Worker 的任务。
  • Worker 异常后可重投或重试。

阶段 3前端刷新恢复与多工作区

预计34 人日。

任务:

  • 新增工作区路由。
  • 状态驱动当前步骤。
  • 恢复草稿和解析结果。
  • 新建任务不清空旧任务。
  • 增加任务入口页。
  • 增加任务复制。

验收:

  • 用户可以同时维护多个 PPT 和海报工作区。

阶段 4全局任务坞与任务中心

预计23 人日。

任务:

  • 新增 GenerationTaskDock
  • 新增全局组合式状态。
  • 新增活跃任务批量接口。
  • 改造历史页为统一任务中心。
  • 增加未读完成和失败提示。
  • 完成移动端抽屉适配。

验收:

  • 用户在任意业务页面都能看到和选择任务。

阶段 5版本化编辑

预计23 人日。

任务:

  • 草稿自动保存。
  • 乐观锁。
  • 输入快照。
  • 草稿版本和成品版本。
  • 未生成修改提示。
  • 输出文件按 task 隔离。

验收:

  • 生成期间编辑不会污染正在执行的版本。

阶段 6测试与灰度

预计23 人日。

任务:

  • 单元测试。
  • Celery 集成测试。
  • 多任务端到端测试。
  • Worker 中断测试。
  • 多标签页测试。
  • 权限和文件安全测试。
  • 数据迁移和旧历史兼容测试。
  • 灰度上线与指标观察。

二十二、测试重点

刷新恢复

在以下节点刷新:

  • 上传后
  • 解析中
  • 数据校验中
  • 模板选择后
  • 排队中
  • 生成中
  • 完成后
  • 失败后

多任务

同时创建:

3 个 PPT
3 个海报

验证:

  • 任务相互独立
  • 可自由切换
  • 进度不会串任务
  • 一个失败不影响其他任务
  • 创建新任务不清空旧任务

幂等

  • 双击生成
  • 网络超时后重试
  • Celery 重复投递
  • Worker 保存文件后中断
  • 同一工作区重复提交

跨页面

  • 从 PPT 切到智能问答
  • 从海报切到产品推荐
  • 关闭详情页
  • 退出后重新登录
  • 换设备继续

编辑版本

  • 生成版本 3 时编辑版本 4
  • 版本 3 完成后继续下载
  • 用版本 4 重新生成
  • 两标签页同时修改
  • 冲突时返回 409

权限

  • 用户 A 无法读取用户 B 的工作区
  • 用户 A 无法下载用户 B 的文件
  • 修改 URL 不能越权
  • 绝对路径不出现在 API 响应中

二十三、最终验收标准

以下全部通过才算完成:

  1. 用户可以同时创建多个 PPT 和海报工作区。
  2. 新建任务不会覆盖旧任务。
  3. 所有活跃任务在全局任务坞可见。
  4. 用户可以点击任务进行切换。
  5. 刷新后任务坞和当前工作区自动恢复。
  6. 关闭页面后后台继续执行。
  7. PPT 生成不再依赖长时间 HTTP 请求。
  8. 海报不再依赖 API 进程内线程。
  9. 不同工作区可以并行或排队。
  10. 同一工作区不能同时生成两次。
  11. 双击和网络重试不会产生重复执行。
  12. 页面显示真实阶段和进度。
  13. 生成期间可以编辑下一版。
  14. 正在执行的输入快照保持不变。
  15. 不同版本文件不会互相覆盖。
  16. 一个任务失败不影响其他任务。
  17. 用户可以从任务中心找回隐藏任务。
  18. Worker 异常后任务可恢复或明确重试。
  19. 文件预览和下载稳定。
  20. 用户之间严格隔离。
  21. 旧历史记录仍可访问。
  22. 自研代码保持在 api/insurance/ 和独立 Vue 前端中。
  23. 不修改 BaoDan 业务基座的 Celery实现。

二十四、工作量评估

模块 预计工作量
技术验证 1 人日
数据模型与迁移 23 人日
Celery 后台任务 34 人日
多工作区前端 34 人日
全局任务坞与任务中心 23 人日
版本化编辑 23 人日
测试与灰度 23 人日
合计 1521 人日

MVP

预计 912 人日,包含:

  • 多工作区
  • 刷新恢复
  • Celery 后台执行
  • 全局任务坞
  • 任务选择切换
  • 基础进度
  • 文件隔离
  • 幂等和权限

完整版

预计 1521 人日,增加:

  • 自动保存
  • 乐观锁
  • 版本化编辑
  • 复制任务
  • 完整任务中心
  • 通知
  • 生命周期清理
  • 故障恢复测试

二十五、明确不进入 MVP 的内容

为控制复杂度,以下暂不进入 MVP

  • WebSocket/SSE
  • 运行中强制终止外部模型请求
  • 手动调整队列优先级
  • 管理员拖动任务排序
  • 多个任务之间的数据合并
  • 同一工作区同时运行多个生成
  • 完整的成品版本对比界面
  • 新增第二套队列、认证、数据库或日志基础设施

MVP 使用批量轮询、同工作区单执行和 BaoDan 现有 Celery已经可以完整解决当前问题。

这份计划现在同时覆盖了“任务不会因刷新丢失”和“多个任务常驻页面、可选择切换”两项目标,可直接作为后续开发基线。当前未修改代码。