105 lines
6.8 KiB
Markdown
105 lines
6.8 KiB
Markdown
# 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 次迭代
|