petstore-docs/架构决策-StoreCustomer事实时间线-2026-08-02.md

40 lines
3.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 架构决策StoreCustomer 事实时间线
> 日期2026-08-02
> 状态Accepted / Code Complete
## 1. 决策
客户详情时间线以 canonical `StoreCustomer.id` 为唯一门店关系主键,以已持久化的 `BusinessEvent` 为事实源。它是只读投影,不新建可编辑“动态”表,也不从页面点击、当前对象状态或日志猜测历史。
手机号留资投影与账号主档合并时,旧投影软删除并写入 `merged_into_store_customer_id`,既有 BusinessEvent 继续引用旧 ID 且不可改写。时间线解析 canonical 后同时查询它和全部同店别名的事件,因此合并前后的客户事实保持连续。别名只允许单层指向同店、有效且未再合并的 canonical生产预检拒绝活动别名、自引用、跨店、缺失或链式目标。
`GET /api/admin/service-customers/timeline` 只接受 `storeCustomerId/page/pageSize`。门店范围从 session 派生;`current.storeId + storeCustomerId` 不匹配时按不存在返回 404避免枚举他店客户。
## 2. 展示口径
-`occurredAt DESC, id DESC` 对 canonical 与别名事件统一稳定分页;同一时刻用数据库事件 ID 决定顺序。
- 展示预约创建/状态、开始/完成服务、报告提交/确认发送/首次打开/再次打开、回访意向等已经落库的事实。
- 批量补齐预约、报告、回访对象的当前低敏摘要和详情跳转 ID对象已删除时仍保留事件标题和发生时间不伪造摘要。
- Admin 客户抽屉可跳到预约详情、报告列表和回访池;本批不创建回访任务或再次预约归因。
## 3. 隐私与数据边界
允许返回:客户称呼、脱敏手机号、宠物名、服务类型、预约时间、当前业务状态、发送渠道、提醒日期、操作人显示名/角色。
禁止返回:手机号原文、预约/报告备注、图片/视频 URL、报告或邀请 token/hash、openid/unionid、IP、请求体和原始 `metadata_json`。服务端只解析 `fromStatus/toStatus/channel/repeatSubmit` 四个白名单 metadata 字段。
时间线不承载会员等级、余额、次卡、积分、营销标签或自由文本跟进记录。这些能力若进入后续阶段,必须使用独立对象与状态机,不得改写历史事实。
## 4. 验收
- 同店 boss/staff 可查customer 禁止;跨店返回 404。
- 分页上限 100顺序稳定返回 `total/page/pageSize/hasMore`
- 预约、报告、回访聚合 ID 相同时仍按 `aggregateType` 精确解析。
- canonical ID 与已合并别名 ID 均返回同一 canonical 客户摘要,并包含合并前后的全部事件。
- system/public 事件无操作人时正常返回。
- 自动化反例确认响应不包含完整手机号、token、备注或媒体字段。
- 本体校验与代码端点漂移均为 0Backend 全测与 Admin production build 通过。
数据库迁移:`backend/db/migrations/20260802_create_store_customer_timeline.sql`,在门店开通迁移后第七个执行;末尾两项别名不变量必须为 0。