# Implementation Plan: P4 前置依赖修复 ## Overview 6 个定点修复,修正 P4 核心业务层与 Spec 的实现偏差。T1/T2 已在之前修复中完成,仅需属性测试验证;T3/T4/T5/T6 需要代码变更。所有修改限于现有服务层内部逻辑,无新表、无新接口。 测试框架:Hypothesis(项目已有依赖),测试文件位于 `tests/` 目录。 ## Tasks - [x] 1. 验证 T1(任务列表)和 T2(召回检测器)已有实现 + 属性测试 - [x] 1.1 编写 T1 属性测试:任务列表状态过滤、排序、abandon_reason 一致性 - 创建 `tests/test_p52_task_list_properties.py` - 提取 `get_task_list` 的过滤和排序逻辑为纯函数(或 mock DB 层),用 Hypothesis 生成随机任务集合 - **Property 1: 任务列表状态过滤** — 仅返回 active/abandoned,不含 completed/inactive - **Property 2: 任务列表排序正确性** — abandoned 排在 active 之后,active 内部按 is_pinned DESC, priority_score DESC NULLS LAST, created_at ASC - **Property 3: abandon_reason 与 status 一致性不变量** — active → null, abandoned → 非空字符串 - **Validates: Requirements 1.1, 1.2, 1.3, 1.4** - [x] 1.2 编写 T2 属性测试:召回检测器仅完成 recall 类型任务 - 在 `tests/test_p52_recall_detector_properties.py` 中编写 - 用 Hypothesis 生成包含四种 task_type 的任务集合和服务记录 - **Property 4: 召回检测器仅完成 recall 类型任务** — high_priority_recall/priority_recall 被完成,follow_up_visit/relationship_building 状态不变 - **Validates: Requirements 2.1, 2.2, 2.3** - [x] 2. Checkpoint — 验证 T1/T2 属性测试通过 - Ensure all tests pass, ask the user if questions arise. - [x] 3. 实现 T3:备注回溯重分类器冲突处理 - [x] 3.1 修改 `apps/backend/app/services/note_reclassifier.py` 的 `run()` 方法 - 在创建 follow_up_visit 任务前,查询同 (site_id, assistant_id, member_id) 是否已有 follow_up_visit 任务 - 已有 `completed` → 跳过创建(回访完成语义已满足) - 已有 `active` → 旧任务标记 `inactive`,记录 `superseded` 历史,创建新任务 - 不存在(或仅有 inactive/abandoned)→ 正常创建 - 确保重复触发相同 payload 不产生唯一约束冲突 - _Requirements: 3.1, 3.2, 3.3, 3.4_ - [x] 3.2 编写 T3 属性测试:冲突处理三分支 - 在 `tests/test_p52_note_reclassifier_properties.py` 中编写 - **Property 5: 已完成回访任务阻止新建** — 存在 completed 的 follow_up_visit 时跳过创建 - **Property 6: active 回访任务被顶替** — 旧任务 → inactive + superseded 历史,新任务创建 - **Property 7: 无冲突时正常创建** — 不存在 follow_up_visit(或仅 inactive/abandoned)时正常创建 - **Validates: Requirements 3.1, 3.2, 3.3, 3.4** - [x] 4. 实现 T4:回访完成条件改为「有备注即完成」 - [x] 4.1 修改 `apps/backend/app/services/note_service.py` 的 `create_note()` 方法 - 当 `note_type == "follow_up"` 且 `task_id is not None` 时: - 保留 `ai_analyze_note()` 占位调用,返回值仅用于更新 `ai_score` 字段 - 不论 `ai_score` 如何,有备注即标记关联的 active follow_up_visit 任务为 `completed` - 记录 `completed_by_note` 历史 - _Requirements: 4.1, 4.2, 4.3_ - [x] 4.2 修改 `apps/backend/app/services/note_reclassifier.py` 的 `run()` 方法(T4 部分) - 去掉 AI 评分判定逻辑(`ai_score >= 6` 条件) - 保留 `ai_analyze_note()` 占位调用 - 找到备注(`note_id is not None`)→ 创建 `status='completed'` 的回访任务(回溯完成) - 未找到备注(`note_id is None`)→ 创建 `status='active'` 的回访任务(等待备注) - 注意:此步骤需与 3.1 的冲突处理逻辑协同,冲突检查在任务创建前执行 - _Requirements: 4.4, 4.5_ - [x] 4.3 编写 T4 属性测试:有备注即完成 - 在 `tests/test_p52_note_service_properties.py` 中编写 - **Property 8: 有备注即完成回访任务,不依赖 AI 评分** — 对任意 ai_score(None/0-5/6-10),提交备注后 active follow_up_visit 任务都标记 completed - **Property 9: 回溯有备注时创建 completed 回访任务** — note_reclassifier 找到备注 → status='completed' - **Property 10: 回溯无备注时创建 active 回访任务** — note_reclassifier 未找到备注 → status='active' - **Validates: Requirements 4.2, 4.3, 4.4, 4.5** - [x] 5. Checkpoint — 验证 T3/T4 实现和测试通过 - Ensure all tests pass, ask the user if questions arise. - [x] 6. 实现 T5:trigger_scheduler last_run_at 事务安全 - [x] 6.1 修改 `apps/backend/app/services/trigger_scheduler.py` - `fire_event()`:handler 接受可选 `conn` 参数,在 handler 最终 commit 前附带更新 `last_run_at`;handler 失败时整个事务回滚(last_run_at 不更新) - `check_scheduled_jobs()`:同理,将 `last_run_at` 和 `next_run_at` 更新纳入 handler 事务范围 - 需同步修改 `recall_detector.run()` 和 `note_reclassifier.run()` 的签名,接受可选 `conn` 参数 - _Requirements: 5.1, 5.2, 5.3_ - [x] 6.2 编写 T5 属性测试:事务一致性 - 在 `tests/test_p52_trigger_scheduler_properties.py` 中编写 - **Property 11: 触发器 last_run_at 事务一致性** — handler 成功 → last_run_at 更新;handler 异常 → last_run_at 不变(整个事务回滚) - **Validates: Requirements 5.1, 5.2** - [x] 7. 实现 T6:cron 默认值改为 07:00 + 迁移脚本 - [x] 7.1 修改 `apps/backend/app/services/trigger_scheduler.py` 的 `_calculate_next_run()` - 将 `trigger_config.get("cron_expression", "0 4 * * *")` 改为 `"0 7 * * *"` - _Requirements: 6.2, 6.3_ - [x] 7.2 创建迁移脚本 `db/zqyy_app/migrations/2026-03-15__p52_update_cron_0700.sql` - 幂等 UPDATE:`UPDATE biz.trigger_jobs SET trigger_config = jsonb_set(trigger_config, '{cron_expression}', '"0 7 * * *"') WHERE job_name = 'task_generator'` - 包含回滚注释 - _Requirements: 6.1_ - [x] 8. Final checkpoint — 全部测试通过 - Ensure all tests pass, ask the user if questions arise. - 运行 `pytest tests/test_p52_*.py -v` 验证所有 P5.2 属性测试通过 ## Notes - Tasks marked with `*` are optional and can be skipped for faster MVP - T1/T2 已在之前修复中实现,任务 1 仅编写属性测试验证覆盖 - T3 和 T4 对 `note_reclassifier.py` 有交叉修改,任务 3.1 和 4.2 需协同实施 - T5 涉及 handler 签名变更,需同步修改 recall_detector 和 note_reclassifier - T6 种子数据已是 `0 7 * * *`,迁移脚本为幂等更新确保生产环境一致 - 属性测试使用 Hypothesis,每个属性最少 100 次迭代