Files
smartmate/docs/功能决策记录/任务四象限_自动平移_决策记录.md
Losita 84371e2ff8 Version: 0.6.3.dev.260316
 feat(task): 新增四象限任务懒触发自动平移链路(读时派生 + Outbox 异步收敛)

- 🧩 为 `Task` 模型新增 `urgency_threshold_at` 字段,并补充复合索引 `user_id,is_completed,urgency_threshold_at,priority` 及相关事件 payload
- ♻️ 重构 `TaskService.GetUserTasks`:调整为“缓存/DB 读取原始任务 -> 读时派生优先级(`2 -> 1`、`4 -> 3`)-> 通过 `SETNX` 去重后发布平移事件”的处理链路
- 🚚 新增任务平移事件链路:
  - `service/events/task_urgency_promote.go`
  - 事件类型:`task.urgency.promote.requested`
  - 支持 `Publish` + `RegisterHandler` + `ConsumeAndMarkConsumed` 的事务化消费流程
- 🛡️ 为 `TaskDAO` 新增幂等批量更新能力 `PromoteTaskUrgencyByIDs`,采用条件更新策略,仅对“达到阈值且未完成”的任务生效
- 🔌 更新启动接线逻辑:注册任务平移 handler,并将 `eventBus` 注入 `NewTaskService`
- 🧹 修复并升级任务缓存层,统一为 `[]model.Task` 原始模型缓存;同时清理误导性注释,并补充详细中文步骤化注释
- 🔗 打通 `QuickNote` 链路中的 `urgency_threshold_at` 透传与校验,覆盖 `state` / `tool` / `nodes` / `prompt` / `agent_quick_note` 全链路
- 💾 写库时补充落库 `task.UrgencyThresholdAt`
- 📝 新增功能决策记录

之前画的饼正在一块块填上~这一块饼填上之后,第一批开发的后端部分基本已经搞定了。后面的功能全都是天马行空的拓展功能。
2026-03-16 20:33:33 +08:00

120 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 功能决策记录FDR任务四象限自动平移懒触发
## 1. 基本信息
- 记录编号FDR-2026-03-TASK-URGENCY-PROMOTE
- 功能名称:任务四象限自动平移(不紧急 -> 紧急)
- 记录日期2026-03-16
- 决策状态:已采纳
- 负责人:项目协作实现(你 + Codex
- 关联模块:
- `backend/service/task.go`
- `backend/service/events/task_urgency_promote.go`
- `backend/dao/task.go`
- `backend/infra/outbox/*`
- `backend/middleware/cache_deleter.go`
## 2. 背景与问题
- 业务背景:任务在创建时属于某个四象限,随着时间推进,部分“不紧急”任务应自动进入“紧急”象限。
- 关键约束:
- 不引入全局定时任务(避免资源常驻扫描)。
- 已有缓存删除机制依赖 `cache_deleter`GORM 写库后自动删缓存)。
- 已有 outbox 总线可承载异步更新。
- 原始问题:
- 如果“读时直接改库 + 删缓存”,会引入写放大与重复触发;
- 如果“只靠 outbox 异步更新”,在消费延迟窗口内二次访问可能读到旧缓存;
- 需要在一致性与性能之间取平衡,且用户体验不能差。
## 3. 决策目标
- 目标 1用户访问任务列表时优先看到“符合当前时间语义”的象限结果。
- 目标 2真实数据修正异步执行避免把写库延迟绑定到读接口。
- 目标 3避免重复投递与重复更新控制总线与数据库压力。
- 非目标:
- 本次不做定时批处理平台;
- 本次不做分布式多级缓存一致性框架;
- 本次不做任务系统分库分表改造。
## 4. 备选方案
### 方案 A定时任务全量扫描并更新
- 描述:后台周期扫描 `tasks`,到线即批量更新优先级。
- 优点:数据收敛快、语义直观。
- 缺点:常驻资源开销高;扫描窗口与峰值冲突风险高;运维复杂度增加。
- 成本:中高(需要调度、监控、限流与容错)。
### 方案 B读时同步改库请求内直接 UPDATE
- 描述:用户读任务列表时,检测到到线任务后立即写库并删缓存。
- 优点:实现简单,数据立刻落地。
- 缺点:读接口变“读+写”;高并发时放大锁竞争;延迟直接转嫁给用户请求。
- 成本:中(实现快,但后续稳定性成本高)。
### 方案 C采纳读时派生 + SETNX 去重 + outbox 异步落库
- 描述:
- 读请求先按 `urgency_threshold_at` 做“派生展示”(仅本次响应生效);
- 对需要平移的任务做 Redis `SETNX` 去重;
- 去重成功后发布 outbox 事件,消费者异步条件更新 DB
- DB 更新后由 `cache_deleter` 自动删缓存,完成最终收敛。
- 优点:
- 用户先看到正确业务语义,不被异步延迟影响体感;
- 读链路不阻塞写库;
- 复用现有 outbox 与 cache_deleter侵入小、可扩展。
- 缺点:
- 派生视图与真实落库存在短暂时间差;
- 需要维护去重键 TTL 与消费可观测性。
- 成本:中(实现复杂度适中,长期收益更高)。
## 5. 最终决策
- 采纳方案:方案 C读时派生 + 异步收敛)。
- 关键理由:
- 最符合“性能优先 + 最终一致”目标;
- 与现有 outbox、缓存失效机制天然兼容
- 面向后续通用事件总线扩展(更多异步业务)更友好。
## 6. 影响范围
- 涉及模块:
- `TaskService.GetUserTasks`:新增读时派生与异步触发。
- `TaskDAO.PromoteTaskUrgencyByIDs`:新增幂等条件更新。
- `service/events`:新增任务平移事件发布与消费注册。
- `cmd/start.go`:新增 handler 注册与 service 注入。
- 数据与存储影响:
- `tasks` 新增字段:`urgency_threshold_at`
- 新增复合索引:`(user_id, is_completed, urgency_threshold_at, priority)`
- outbox 新增事件类型:`task.urgency.promote.requested`
- 接口影响:
- 对外 API 协议不变;
- 行为层面变为“读时先派生展示、随后异步收敛”。
- 监控与日志影响:
- 新增任务平移事件发布/消费日志;
- 可按 `event_type` 维度观测积压与重试。
## 7. 风险与应对
- 风险 1outbox 消费延迟导致短时间内 DB 仍是旧值。
- 应对策略:读时派生保证用户看到新语义;最终由异步消费收敛。
- 风险 2同一任务被多次重复投递。
- 应对策略:`SETNX + TTL` 去重DAO 层条件更新保证幂等。
- 风险 3缓存一致性混乱误删/抖动)。
- 应对策略:不在读链路主动删缓存;只在真实写库后由 `cache_deleter` 自动失效。
## 8. 验证与回滚
- 验证方式:
- 用到线前后样例任务验证 `GetUserTasks` 返回象限变化;
- 验证 outbox 事件是否成功入队、消费并更新 DB
- 验证 DB 更新后缓存是否由 `cache_deleter` 自动清理。
- 成功判定标准:
- 用户可在到线后第一时间看到“已平移”结果;
- DB 在异步窗口后收敛为相同状态;
- 无明显重复投递和异常写放大。
- 回滚方案:
- 关闭任务平移事件发布(保留读时派生)可快速止损;
- 或回退到“只读不平移”行为(临时关闭派生逻辑)。
## 9. 里程碑与后续计划
- 里程碑 1完成懒触发主链路与事件消费者落地已完成
- 里程碑 2完成联调与基本回归已完成
- 后续优化项:
- 增加按 `event_type=task.urgency.promote.requested` 的积压监控面板;
- 根据线上情况调优去重 TTL
- 引入批量聚合发布(按用户或时间窗口)进一步降事件量。
## 10. 复盘结论(上线后补充)
- 实际效果:待补充。
- 与预期偏差:待补充。
- 是否需要二次决策:待补充。