7.0 KiB
7.0 KiB
需求文档:P4 前置依赖修复
简介
P4 核心业务层已实现并通过属性测试,但对比 Spec 发现 6 处实现偏差(来源:docs/reports/P4-spec-vs-implementation-gap-analysis.md)。这些偏差会影响 P6(前端任务模块)的正常开发,必须前置修复。本需求覆盖 GAP-3、GAP-6、GAP-7、GAP-9 的代码修复,以及两项新增修正(回访完成条件、cron 时间)。
修复范围:6 个定点修复,无新表、无新接口,仅修改现有服务层逻辑和一条迁移脚本。
术语表
- Task_Manager: 任务管理服务(
task_manager.py),负责任务列表查询和状态操作 - Recall_Detector: 召回完成检测器(
recall_detector.py),ETL 数据更新后匹配服务记录完成召回任务 - Note_Reclassifier: 备注回溯重分类器(
note_reclassifier.py),召回完成后回溯普通备注为回访备注并创建回访任务 - Note_Service: 备注服务(
note_service.py),负责备注 CRUD 和回访任务自动完成 - Trigger_Scheduler: 触发器调度框架(
trigger_scheduler.py),统一管理 cron/interval/event 三种触发方式 - coach_tasks: 助教任务表(
biz.coach_tasks) - trigger_jobs: 触发器配置表(
biz.trigger_jobs) - active: 任务有效状态
- abandoned: 任务已放弃状态
- inactive: 任务无效状态(被顶替)
- completed: 任务已完成状态
- high_priority_recall: 高优先召回任务类型
- priority_recall: 优先召回任务类型
- follow_up_visit: 客户回访任务类型
- relationship_building: 关系构建任务类型
- abandon_reason: 放弃原因字段
- last_run_at: 触发器上次运行时间戳
- ai_analyze_note(): AI 备注分析占位函数(P5 实现,当前返回 None)
需求
需求 1:任务列表返回已放弃任务
用户故事: 作为助教,我希望在任务列表中看到已放弃的任务及其放弃原因,以便执行「取消放弃」操作恢复任务。
验收标准
- WHEN 助教请求任务列表, THE Task_Manager SHALL 返回 active 和 abandoned 两种状态的任务
- THE Task_Manager SHALL 按以下顺序排列任务:abandoned 排在所有 active 任务之后,active 任务内部按 is_pinned DESC、priority_score DESC NULLS LAST、created_at ASC 排序
- THE Task_Manager SHALL 在返回结构中始终包含 abandon_reason 字段,active 状态任务的 abandon_reason 为 null,abandoned 状态任务的 abandon_reason 为非空字符串
- WHEN 不存在 abandoned 任务时, THE Task_Manager SHALL 仅返回 active 任务,返回结构不变(abandon_reason 为 null)
需求 2:召回完成检测器过滤任务类型
用户故事: 作为系统运维人员,我希望召回完成检测器仅完成召回类任务,避免错误地将回访和关系构建任务标记为已完成。
验收标准
- WHEN ETL 检测到新增服务记录, THE Recall_Detector SHALL 仅匹配 task_type 为 high_priority_recall 或 priority_recall 的 active 任务进行完成标记
- WHILE 存在 active 的 follow_up_visit 任务, THE Recall_Detector SHALL 跳过该任务,不执行完成标记
- WHILE 存在 active 的 relationship_building 任务, THE Recall_Detector SHALL 跳过该任务,不执行完成标记
需求 3:备注回溯重分类器冲突处理
用户故事: 作为系统运维人员,我希望备注回溯重分类器在创建回访任务时正确处理冲突,避免唯一约束违反和重复任务。
验收标准
- WHEN Note_Reclassifier 准备创建 follow_up_visit 任务且同 (site_id, assistant_id, member_id) 已存在 completed 的 follow_up_visit 任务, THEN THE Note_Reclassifier SHALL 跳过创建(回访完成语义已满足)
- WHEN Note_Reclassifier 准备创建 follow_up_visit 任务且同 (site_id, assistant_id, member_id) 已存在 active 的 follow_up_visit 任务, THEN THE Note_Reclassifier SHALL 将旧任务标记为 inactive 并记录变更历史,然后创建新的 follow_up_visit 任务(顶替方案)
- WHEN Note_Reclassifier 准备创建 follow_up_visit 任务且同 (site_id, assistant_id, member_id) 不存在任何 follow_up_visit 任务, THE Note_Reclassifier SHALL 正常创建新任务
- IF Note_Reclassifier 重复触发相同 payload, THEN THE Note_Reclassifier SHALL 不产生唯一约束冲突错误
需求 4:回访任务完成条件改为「有备注即完成」
用户故事: 作为助教,我希望为客户提交备注后对应的回访任务自动完成,无需等待 AI 评分。
验收标准
- WHEN 助教通过 Note_Service 为某客户创建备注, THE Note_Service SHALL 查询该客户是否存在 active 的 follow_up_visit 任务(通过 user_assistant_binding 获取 assistant_id,再匹配 site_id、assistant_id、member_id)
- WHEN 存在 active 的 follow_up_visit 任务且助教提交了备注, THE Note_Service SHALL 将该任务标记为 completed 并记录变更历史,完成判定不依赖 ai_analyze_note() 的返回值
- THE Note_Service SHALL 保留 ai_analyze_note() 占位调用(P5 接入时调用链不变),但 ai_analyze_note() 的返回值不作为回访任务完成的判定条件
- WHEN Note_Reclassifier 回溯发现已有备注, THE Note_Reclassifier SHALL 直接创建 status='completed' 的回访任务(回溯完成),不依赖 AI 评分
- WHEN Note_Reclassifier 回溯未发现备注, THE Note_Reclassifier SHALL 创建 status='active' 的回访任务(等待助教提交备注)
需求 5:trigger_scheduler last_run_at 事务安全
用户故事: 作为系统运维人员,我希望触发器的 last_run_at 更新与 handler 执行在同一事务中,避免 handler 成功但 last_run_at 更新失败导致重复处理。
验收标准
- WHEN Trigger_Scheduler 执行 event 类型触发器(fire_event), THE Trigger_Scheduler SHALL 将 last_run_at 更新纳入 handler 执行的同一事务范围,handler 成功与 last_run_at 更新要么一起提交要么一起回滚
- WHEN Trigger_Scheduler 执行 cron/interval 类型触发器(check_scheduled_jobs), THE Trigger_Scheduler SHALL 将 last_run_at 和 next_run_at 更新纳入 handler 执行的同一事务范围
- IF handler 执行成功但事务提交失败, THEN THE Trigger_Scheduler SHALL 回滚整个事务(包括 handler 的数据变更和 last_run_at 更新),下次重跑时 handler 的幂等性保证不会产生副作用
需求 6:任务生成器 cron 时间改为 07:00
用户故事: 作为运营人员,我希望任务生成器在每天早上 7 点运行,与门店营业节奏匹配。
验收标准
- THE 迁移脚本 SHALL 将 trigger_jobs 表中 task_generator 的 cron_expression 从
0 4 * * *更新为0 7 * * * - THE Trigger_Scheduler SHALL 在 _calculate_next_run() 中使用
0 7 * * *作为 cron 默认值 - WHEN task_generator 触发器下次运行时间被计算, THE Trigger_Scheduler SHALL 基于
0 7 * * *计算正确的 next_run_at