Files
gaokao-volunteer-system/docs/DELIVERY_SERVICE_DESIGN.md
Hermes Agent 94200960b2
Some checks failed
CI / pytest (Python 3.10) (push) Has been cancelled
CI / pytest (Python 3.11) (push) Has been cancelled
CI / pytest (Python 3.12) (push) Has been cancelled
docs(design): tighten X-02/X-03/X-05/X-08 design docs and add CROWD_DB_DATA_QUALITY
2026-06-15 15:14:27 +08:00

4.5 KiB
Raw Blame History

DELIVERY_SERVICE_DESIGN

最后更新: 2026-06-14

1. 目标

将“报告生成完成”与“已交付给用户”明确区分,避免把 delivered 误当成只是一条后台状态修改。

2. 设计原则

  1. 交付是独立业务对象,不等于报告文件存在
  2. 用户可见状态必须基于真实交付物
  3. 通知触发点应尽量靠近稳定主链,而不是只挂在单一路由
  4. 至少一种 MVP 交付方式先闭环

3. MVP 交付方式

当前优先:

  • 站内查看 HTML 报告
  • 站内下载 PDF

后续可扩展:

  • 邮件投递
  • 微信/渠道通知

4. 建议对象

delivery_job

  • delivery_id
  • order_id
  • channel (station/email/...)
  • status (pending/ready/sent/failed)
  • payload_json
  • created_at
  • updated_at

delivery_attempt

  • delivery_id
  • attempt_no
  • status
  • failure_reason
  • created_at

5. 当前实现状态

已存在:

  • delivery_notifications 事件表
  • portal 状态页 / 报告页 / PDF 下载页
  • delivered 但无交付物时不再误报 report_ready
  • data.notifications.dispatcher.DeliveryDispatcher
  • scripts/gaokao-delivery-dispatch.py
  • scripts/gaokao-delivery-watchdog.py(失败返回 exit code 2
  • ready -> sent / 缺文件 -> failed / attempt_count 递增
  • station 通道 dispatch 时会真实生成 station_noticeportal 状态页展示“通知已发送”消息卡片
  • email 通道使用 SMTPDeliverySender 发送真实邮件通知,支持 GAOKAO_SMTP_* 配置,并已通过本地 stub SMTP 验证
  • docs/DELIVERY_RETENTION_OPS_RUNBOOK.md
  • deploy/systemd/gaokao-delivery-{dispatch,watchdog}.{service,timer}
  • deploy/cron/gaokao-jobs.crontab

未完成:

  • 独立 delivery_job / delivery_attempt 模型
  • 邮件/渠道真实发送执行器
  • 告警推送集成(当前只有 watchdog 非 0 退出码)
  • 多通道统一投递状态页

6. 当前最短闭环

  1. 订单进入 delivered
  2. 真实 HTML/PDF 都存在
  3. portal 显示 report_ready
  4. 状态页提供查看/下载入口
  5. report_ready 事件落库且幂等
  6. gaokao-delivery-dispatch.py --channel station 可把 ready 事件推进到 sent
  7. 缺失交付物时事件转 failed并记录 failure_reason
  8. 可通过 systemd timer / cron 周期执行 dispatcher并用 watchdog 暴露失败退出码

7. 下一步实施建议

  1. 增加失败重试阈值与告警推送
  2. 增加面向用户的独立通知审计页
  3. 把事件生命周期细化为 ready / validated / delivered / failed
  4. 区分 station(本地渲染)与 email(真实下游推送)两类 channel 的 delivered 判定
  5. 再决定是否扩到更多通道

8. 事件生命周期2026-06-14 修订)

P2-3 整改后, dispatcher 强制走如下状态机:

ready   ── 初始化, 还没校验
  │
  ▼
validated ── payload 解析通过 + 交付物 (html / pdf) 校验通过
  │           注意: station channel 在这一步会落 payload, 但不再升级为 delivered
  │
  ▼
delivered ── 真实下游推送完成 (仅 email)
  │
  ▼
failed ── 任意环节失败
  • ready -> validated 必须有 artifact (html / pdf) 真实存在于磁盘
  • validated -> delivered 必须由真实 sender 回调成功 (email.send_report_ready 返回成功)
  • station 通道因为没有真实下游, 永远停在 validated
  • sent 字段是 legacy alias, 仍然存在但不应再被新代码依赖

9. 生产化接入口径

仓库内已提供最小 runbook 与调度样例:

  • runbookdocs/DELIVERY_RETENTION_OPS_RUNBOOK.md
  • systemddeploy/systemd/gaokao-delivery-dispatch.{service,timer} / deploy/systemd/gaokao-delivery-watchdog.{service,timer}
  • crondeploy/cron/gaokao-jobs.crontab

注意:当前 watchdog 仍是“带失败退出码的 dispatch 巡检”,并不等于独立通知告警系统。

10. 不可降级的不变量

  • 事件状态机 ready / validated / delivered / failed 必须被 dispatcher 严格遵守
  • delivered 永远只能由真实 sender 成功返回
  • failed 事件必须有 failure_reason 字段
  • 同一 (order_id, event_type) 必须幂等, 重复 dispatcher 不会产生额外副作用
  • delivered 之后, payload 写入的 delivered_at 必须等于 last_attempt_at

11. 后续工作

  • 把独立 delivery_job / delivery_attempt 模型实现为 SQLite 表
  • 引入 attempt_countlast_attempt_at 之外的成功重试预算
  • 多通道统一投递状态页 (类似 notification audit 页面)
  • 告警推送 (Sentry / 飞书 / 钉钉) 与 watchdog 整合