schema.prisma
89 KB
-
feat(plan): 关闭超时自动回收 + 新增 plan 生命周期事件账本 · 3fd9af36
## 1. 自动回收改为默认关闭(PAC_PLAN_AUTO_RECYCLE=on 才跑) 它一直在生产跑着(ScheduleModule 已启用、服务已注册),日志可查:两天触发 9 次、 累计回收 32 单。前端那句"暂无自动回收机制"说的是倒计时控件被隐藏,不是后端。 关闭理由:回收会把 assignee_user_id / assigned_at **就地置 null**,而「认领」现在要 当作「该患者已被客服处理」用于统计 —— 每 10 分钟抹一次,统计地基是漏的。 代码保留不删:超时兜底的原始问题(领了不做 → 患者被锁死)依然成立,后续想改成 更长超时或"只提醒不回收",打开开关即可。启动时打印开关状态(它会静默改数据)。 注:生产当前登录角色全是 leader,自认领 / 自返池都在权限内,无"staff 无法释放工单" 的沉淀风险。将来放开 staff 角色需重新评估(staff 无 PLAN_RECYCLE)。 ## 2. PlanEventLog — plan 生命周期事件账本(append-only) 为什么 PlanGenerationLog 顶不上:它是**每次引擎跑批一行**的管道账本,**连 plan_id 都没有**,只记"引擎跑了没有、产出几个"(生产 750 万行 / 24 万患者,约 80 万行/天)。 回答不了"这个 plan 经历了什么"。 已收事件:claim / assign / release / auto_release(归属)+ feedback(召回
👍 👎 )。 assign 为门诊经理分配预留 —— actor != assignee 时自动区分,无需事后反推。⭐ 收录边界(注释里划死,防止变垃圾桶):✅ 人工动作 + 会被就地覆盖且事后无法还原的状态变更❌ 引擎重算 / supersede —— 80 万行/天,与人工动作(约 30~100/天)差 4 个数量级, 混入会淹没人工动作;引擎侧已有 PlanGenerationLog + followup_plans 版本流❌ 前端行为埋点(浏览/点击)—— 噪声大、可刷,属产品分析那一摊 ### 写入方式:应用层 + 同事务,**不用数据库触发器** 1. 触发器拿不到操作人 —— actor 在 JWT 里,DB 只看得见行变了;靠 session 变量传递 还是得应用配合,只是变隐式更易漏。本项目 AsyncLocalStorage 目前也不含 user。 2. 拿不到 actor 就分不出 claim / assign —— 这恰是本表最核心的价值。 3. 触发器会被运维脚本误触发:任何一句 `UPDATE followup_plans SET assignee_user_id=NULL` (数据修复 / 清理测试数据)都会凭空造出假的 release 事件。 4. Prisma 不管触发器,得走 raw SQL 迁移,schema 里看不见、易随漂移丢失。 弱点(已认):靠自觉。缓解 = 写入收口到 recordPlanEvent() 单一入口 + 只接受事务客户端 (状态变更与账本同生共死)+ 事件类型走枚举编译期拦拼写错误。 ### 扩展性 新增 PlanEventType + PLAN_EVENT_META(@pac/types,照 EXECUTION_OUTCOME_META 的样子): 事件类型的单一真理源,带 labelZh / group / byHuman / holdsPatient。 新增事件 = META 加一行,不是在调用处随手写字符串。 HUMAN_TOUCH_EVENTS 收口「算不算客服处理过」的口径(auto_release 是系统行为,不算)。 两个关键字段的存在理由: · heldSeconds 必须在清空 assigned_at **之前**算好(computeHeldSeconds),事后算不出来 · feedback 立 reason 列(up/down):followup_plans.recall_feedback 是就地覆盖的, 先👍 后👎 会把前一次冲掉,准确度统计只看最终值会低估分子、也看不出"改判" 配套:assign / recycle 接口补传操作人(controller 原先根本没取 user.sub, 自认领与指派他人在数据上完全无法区分)。 ## 验证 - 340 单测通过(新增 15 例:四种归属事件、幂等不重复记账、事件类型登记完整性、 auto_release 不计入人工、computeHeldSeconds 边界、开关默认关且只认显式 'on') - 本地端到端:认领 →👎 (带 note)→ 返池,账本 3 行齐全、held_seconds 准确; 同期 followup_plans.recall_feedback 只剩最终值 —— 正是就地覆盖会丢的那部分 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>luoqi committed