petstore-docs/架构决策-报告确认发送状态-2026-08-02.md

4.0 KiB
Raw Blame History

架构决策:报告显式确认发送状态

日期2026-08-02
状态Accepted / Implemented生产迁移待执行
OwnerReport Media Backend / Store Miniapp FE / Store Admin FE / Data Model
Paired QAReport Share QA

1. 决策背景

报告提交后,门店可以复制链接、复制话术、展示二维码或预览公开页,但这些动作都不能证明宠主已经收到。若把任一传播准备动作自动记为“已发送”,工作台待办、发送率与后续打开率都会被系统性高估,也无法支持真实门店试点。

当前系统没有微信消息投递回执,因此只能记录门店员工能够负责确认的业务事实:宠主已通过某种方式实际收到报告

2. 决策

2.1 状态模型

Report.send_status 只有三态:

  • unknown:迁移前历史报告,无法可靠判断是否发送;
  • unsent:迁移后新报告,尚未被员工显式确认发送;
  • sent:本店 boss/staff 已显式确认宠主收到。

历史报告统一回填 unknown,不回填 report_sent 事件,也不进入“报告尚未发送”行动组。迁移后新报告由应用和数据库默认值共同保证为 unsent

2.2 首次确认回执

首次确认同时写入:

  • sent_at:确认发生时间;
  • sent_by_user_id:当前会话员工;
  • send_channelwechat | qr | other
  • 不可变 BusinessEvent(event_type=report_sent),幂等键 report_sent:{reportId}

报告行以悲观写锁串行确认。状态进入 sent 后不可撤销,也不可用后续点击覆盖首次时间、员工或渠道;重复确认返回成功并带 alreadyConfirmed=true,不重复保存、不重复发事件。

确认发送不改写 Report.update_time:该字段目前参与成片 processing 超时判断,发送回执以独立 sent_at 表达,避免传播动作重置成片异常计时。

2.3 不构成发送的动作

以下动作永远不自动改变发送状态:

  • 复制公开链接;
  • 复制话术;
  • 生成或展示二维码;
  • 预览或打开 H5
  • 宠主打开埋点。

产品文案不得宣称“微信发送成功”或第三方投递成功,只能表达“已记录员工确认”。

3. 权限与隐私

  • POST /api/report/confirm-sent 是受保护接口,仅 boss/staff 可调用;storeId/userId/role 全部从会话派生。
  • 服务层再次校验报告属于当前门店,跨店返回 403。
  • 公开 token 报告响应不返回 sendStatus/sentAt/sentByUserId/sendChannel
  • report_sent.metadata_json 只保存低敏 channel,不保存 token、链接、手机号、微信标识或请求体。

4. 工作台与漏斗

  • 工作台新增 unsent_report,只统计 send_status=unsent,按报告创建时间升序提醒;历史 unknown 不进入行动队列。
  • reportSentCount 按当日 report_sent 的 report ID 去重,reportSentTrackingReady=true
  • 漏斗口径为 report_submitted → report_sent → report_opened/reopened → lead_submitted;再次预约仍保持未知,直到可靠归因模型落地。

5. 数据迁移与发布门禁

迁移脚本:backend/db/migrations/20260801_create_report_send_status.sql

固定迁移顺序中本脚本排在预约容量之后。生产只读预检新增三项不变量:

  1. 发送状态只能是 unknown|unsent|sent
  2. sent 必须有完整首次回执;
  3. unknown|unsent 不得残留回执字段。

因此生产预检从 15 项增至 18 项;本批版本化迁移从四个增至五个。常规回滚仍只回应用,不自动删除新增列和索引。

6. 验收

  • 新报告创建后为 unsent;历史迁移后为 unknown
  • 复制链接、二维码、预览均不调用确认接口。
  • 首次确认写回执和 report_sent;重复确认不覆盖、不重复写事件。
  • customer 与跨店员工均不能确认。
  • Admin 和门店小程序都要求用户选择真实发送方式并再次确认。
  • 工作台只提醒 unsent;漏斗发送数来自 report_sent
  • backend 全测、admin/H5/小程序构建及本体校验/漂移审计通过。