85 lines
4.0 KiB
Markdown
85 lines
4.0 KiB
Markdown
# 架构决策:报告显式确认发送状态
|
||
|
||
日期:2026-08-02
|
||
状态:Accepted / Implemented(生产迁移待执行)
|
||
Owner:Report Media Backend / Store Miniapp FE / Store Admin FE / Data Model
|
||
Paired QA:Report 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_channel`:`wechat | 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/小程序构建及本体校验/漂移审计通过。
|