petstore-docs/架构决策-FollowUpTask回访状态机与再次预约归因-2026-08-02.md

4.3 KiB
Raw Blame History

架构决策FollowUpTask 回访状态机与再次预约归因

日期2026-08-02
状态Accepted / Code Complete
本体:entity:follow_up_taskrule:BR-FU-001rule:BR-FU-002

1. 决策

ReportLead 继续保存宠主同意、来源报告、建议日期与退订凭证等留资事实;门店员工的领取、联系、改期、关闭和再次预约改由独立 FollowUpTask 承载。不得继续用 ReportLead.remind_status 同时表达同意事实和多人协作状态。

留资成功后,同一 sourceLeadId 至多存在一个开放任务。重复提交复用开放任务并同步建议日期;上一轮已终结后再次提交,创建新的任务轮次。历史迁移只为 remind_status=pending 的线索回填任务,不推测 sent/canceled/unsubscribed 的真实处理结果。

ReportLead.remind_status 暂保留为兼容投影:新建或改期为 pending,无意向/无效联系方式为 canceled,真实再次预约为 sent,退订为 unsubscribed。Admin 回访工作流与经营统计不再以该兼容字段为事实源。

2. 状态机

pending --领取--> in_progress
in_progress --未接通/稍后联系--> pendingattempt_count + 1更新 due_date
in_progress --暂无意向/联系方式无效--> completedattempt_count + 1
in_progress --真实预约创建成功--> completed / rebookedattempt_count + 1
pending|in_progress --宠主退订--> canceled / unsubscribed不增加员工联系次数
  • pending 不允许有领取人;in_progress 必须有领取人。
  • 只有领取该任务的当前员工才能改期、关闭或发起再次预约。
  • 两名员工并发领取时通过悲观锁和 @Version 防止静默覆盖;他人已领取返回 409。
  • completed/canceled 为终态,不回退。跨店任务按不存在返回 404。
  • no_answer/follow_later 仅是非终态联系结果;rebooked/not_interested/invalid_contact/unsubscribed 仅是终态结果。

3. 真实再次预约归因

rebooked 不是员工可手工选择的关闭结果。只有 POST /api/appointment/create 在同一事务中满足下列条件,才能写入:

  1. followUpTaskId 属于当前门店,处于 in_progress 且由当前员工领取;
  2. 任务的 canonical StoreCustomer 与本次代客预约解析出的客户一致;
  3. 预约通过未来时间、服务时长和每个半小时桶容量校验并真实保存;
  4. 保存后任务写 rebooked_appointment_id,同时落 follow_up_completedrebook_created 事实。

无号源、客户不匹配、状态冲突或预约保存失败时,整笔事务回滚,任务保持处理中。漏斗中的“回访后再次预约”只统计 rebook_created,不从手机号相同或后续页面点击推断。

4. 权限、隐私与审计

  • GET/POST /api/admin/follow-up-tasks/** 仅本店 boss/staffstoreId 和操作人均从 session 派生。
  • 同店已认证员工为了实际电话联系可在开放任务中看到完整手机号终态任务只返回脱敏手机号。工作台、客户时间线、BusinessEvent 和 AuditLog 只保留脱敏或低敏字段。
  • BusinessEvent metadata 仅允许 dueDate/attemptOutcome/outcome/attemptCount/bookingOrigin 等白名单标量不存手机号、备注、微信标识、token、IP 或请求体。
  • 领取、改期、关闭和真实再次预约均写不可变低敏 AuditLog退订由公开凭证触发不伪装成员工联系次数或操作人。

5. 迁移与验收

迁移:backend/db/migrations/20260802_create_follow_up_task.sql,固定在 StoreCustomer 时间线迁移后第八个执行。末尾五项验证必须为 0

  1. 待处理历史线索无开放任务;
  2. 任务客户归属或 canonical 范围无效;
  3. 状态、结果、领取人、联系回执或真实预约引用不一致;
  4. 同一线索存在多个开放任务;
  5. 任务缺少创建事实。

生产只读预检同步检查上述不变量,并额外要求 rebooked_appointment_id 指向同店、未删除的真实预约以及对应 rebook_created 事件。

自动化验收覆盖:历史回填、状态机、并发领取冲突、跨店 404、非领取人拒绝、退订取消、真实预约成功闭环、容量失败不闭环、客户时间线低敏投影、Admin 控制器权限与 production build。