85 lines
4.2 KiB
Markdown
85 lines
4.2 KiB
Markdown
# 架构决策:BusinessEvent 业务事件落库
|
||
|
||
> 日期:2026-08-01
|
||
>
|
||
> 状态:已采纳,待生产数据迁移
|
||
>
|
||
> Owner Agent:Backend Core(报告/留资写入点联动 Report Media Backend)
|
||
>
|
||
> Service Lock:`BusinessEvent*`、预约/报告/留资事件调用点、工作台报告漏斗
|
||
>
|
||
> Paired QA:Core Flow QA + Report Share QA
|
||
>
|
||
> Data Model Review Required:Yes
|
||
>
|
||
> Access / Privacy Review Required:Yes
|
||
|
||
## 1. 决策背景
|
||
|
||
此前本体中的 Event 多数只表达“动作发生后应存在的事实”,并没有统一事件表。报告打开仅写应用日志,工作台只能由前端临时拼预约、报告和留资,无法可靠支持经营漏斗、StoreCustomer 时间线、回访归因和复购统计。
|
||
|
||
## 2. 冻结模型
|
||
|
||
`BusinessEvent` 是不可变事实,不是业务对象当前状态,也不是消息队列。
|
||
|
||
| 字段 | 语义 |
|
||
|---|---|
|
||
| `event_id` | 随机事件 ID;不使用数据库自增 ID 做外部关联 |
|
||
| `event_type` / `event_version` | 过去时事件名与 schema 版本 |
|
||
| `store_id` | 强制门店数据范围 |
|
||
| `store_customer_id` | 可选门店客户稳定关系,供客户时间线使用 |
|
||
| `aggregate_type` / `aggregate_id` | 事件对应的 Appointment、Report 或 ReportLead |
|
||
| `actor_user_id` / `actor_role` | 操作人快照;匿名公开事件可空 |
|
||
| `source` | customer / admin / public_report / system / migration |
|
||
| `occurred_at` | 业务事实发生时间 |
|
||
| `metadata_json` | 服务端白名单低敏维度,不接受原始请求体 |
|
||
| `idempotency_key` | 可选稳定键,保证一个业务结果只落一条 |
|
||
| `create_time` | 事件入库时间 |
|
||
|
||
事件表只追加,不提供更新、软删除或通用明细 API。
|
||
|
||
## 3. 第一批真实事件
|
||
|
||
| 事件 | 可靠触发点 | 幂等口径 |
|
||
|---|---|---|
|
||
| `appointment_created` | 预约保存成功 | appointment ID |
|
||
| `appointment_status_changed` | 合法状态迁移保存成功 | appointment ID + 目标状态 |
|
||
| `service_started` | `new -> doing` | appointment ID |
|
||
| `service_completed` | `doing -> done` | appointment ID |
|
||
| `report_submitted` | 报告及图片保存成功 | report ID |
|
||
| `report_opened` / `report_reopened` | 公开报告打开埋点 | 每次有效打开;统计按 report ID 去重 |
|
||
| `lead_submitted` | 每次留资提交成功 | 可重复记录;漏斗按 lead ID 去重 |
|
||
|
||
`report_sent`、`follow_up_completed`、`rebook_created` 尚无可靠状态机或归因字段,本阶段明确不伪造;对应工作台指标返回 `null + trackingReady=false`。
|
||
|
||
## 4. 隐私边界
|
||
|
||
- 事件表不得保存手机号、报告 token 或 token hash、媒体 URL、IP、openid/unionid、备注、内容全文、密码、密钥或任意请求体。
|
||
- 报告打开接口只用 token 在服务端定位 Report,事件仅保存内部 report ID;日志继续只记 SHA-256 前 8 位 hex。
|
||
- 管理端只开放按当前会话 `storeId` 聚合的漏斗结果,不开放事件明细。
|
||
|
||
## 5. 一致性与迁移
|
||
|
||
- 预约、状态迁移、报告提交与事件写入处于同一数据库事务。
|
||
- 留资沿用现有并发幂等事务边界;重复提交可产生动作事件,漏斗按 lead ID 去重,不放大转化数。
|
||
- 历史迁移只回填可从数据库确定的预约创建、报告提交、由报告确认的服务完成和留资提交。
|
||
- 历史报告打开与开始服务时间无法可靠恢复,不做猜测性回填。
|
||
|
||
迁移顺序:
|
||
|
||
1. `20260801_split_service_identity.sql`
|
||
2. `20260801_create_store_customer.sql`
|
||
3. `20260801_create_business_event.sql`
|
||
|
||
## 6. BusinessEvent 与 AuditLog 边界
|
||
|
||
BusinessEvent 记录“经营上发生了什么”;AuditLog 记录“谁对受保护配置或数据做了什么变更”。本阶段先完成服务闭环事实,后续在门店开通、员工权限、设置修改和敏感操作进入生产化时建立独立 AuditLog,不把安全审计字段混入经营事件。
|
||
|
||
## 7. 验收
|
||
|
||
- `mvn test` 全绿,迁移 SQL 集成测试通过。
|
||
- `npm run build`(admin)通过,工作台“已打开”来自事件汇总。
|
||
- customer 访问工作台事件汇总为 403;boss/staff 只能读取本店。
|
||
- 事件 metadata 隐私单测证明不含原始 token、手机号或 IP。
|
||
- 本体校验通过且 controller/entity 0 漂移。
|