核心数据结构:任务(Task)的定义、属性与状态机模型
好,咱们进入第二章。上一章我们聊了任务规划器能干什么,这一章得落地了——聊聊任务这个最核心的数据结构。
说白了,整个规划器就是围绕「任务」转的。你创建它、修改它、推进它、完成它。如果任务的定义没想清楚,后面所有功能都会跟着歪。我见过不少项目,一开始图省事,任务就存个标题和状态,结果做到一半发现缺字段,回头补数据,那叫一个痛苦。
2.1 任务的核心属性
一个任务,到底需要哪些字段?我个人的习惯是:够用就好,别过度设计。但有几个属性是跑不掉的。
| 属性 | 类型 | 说明 |
|---|---|---|
| ID | 字符串 / UUID | 唯一标识,用于查找、关联、引用 |
| 名称 | 字符串 | 任务的简短标题,一眼能看懂 |
| 描述 | 文本 | 详细说明、验收标准、备注 |
| 优先级 | 枚举 | P0 ~ P3 或 紧急/高/中/低 |
| 状态 | 枚举 | 待办、进行中、已完成、已取消 |
| 截止日期 | 日期 / 时间戳 | 最晚完成时间,可为空 |
嗯,这里要注意:ID 不要用自增整数。我在一个老项目里吃过亏,任务 ID 用自增主键,结果后来要做数据合并、跨系统同步,全乱套了。现在统一用 UUID,虽然长一点,但省心。
2.2 优先级:别让所有任务都是「紧急」
优先级这个字段,看着简单,用起来最容易出问题。你想想看,如果团队里每个人都把自己的任务标成「紧急」,那这个字段就废了。
我建议用四档制:
- P0:阻塞性任务,不做就卡住整个流程
- P1:重要任务,有明确截止日期
- P2:常规任务,按计划推进
- P3:低优先级,有空再做
2.3 状态机模型:任务的生命周期
任务不是静止的。它从创建到完成,会经历一系列状态变化。这个变化路径,就是状态机。
我曾经见过一个项目,任务状态搞了十几个,什么「待评审」「评审中」「待测试」「测试中」「待发布」……看着很细,实际上没人记得住当前该用哪个状态。最后大家统一用「进行中」糊弄过去。
所以我的建议是:状态越少越好。核心就四个:
- 待办(Todo):任务已创建,还没开始做
- 进行中(In Progress):正在处理
- 已完成(Done):任务完成,验收通过
- 已取消(Cancelled):不再需要,或废弃
状态之间的转换,也不是随便跳的。比如「已完成」不能直接回到「待办」,得先变成「进行中」再重新处理。这个约束,必须在代码里写死。
2.4 状态机流程图
下面这张图,就是任务状态机的标准流转。我建议你把它贴在项目文档里,团队所有人都能看懂。
2.5 代码实现:一个干净的任务模型
理论说完了,咱们看代码。下面是一个 TypeScript 定义,我尽量保持简洁:
// 任务状态枚举
enum TaskStatus {
Todo = 'todo',
InProgress = 'in_progress',
Done = 'done',
Cancelled = 'cancelled'
}
// 优先级枚举
enum Priority {
P0 = 0, // 紧急
P1 = 1, // 高
P2 = 2, // 中
P3 = 3 // 低
}
// 任务接口
interface Task {
id: string; // UUID
name: string; // 任务名称
description: string; // 详细描述
priority: Priority; // 优先级
status: TaskStatus; // 当前状态
dueDate: Date | null; // 截止日期,可为空
createdAt: Date; // 创建时间
updatedAt: Date; // 最后更新时间
}
// 状态转换规则
const validTransitions: Record<TaskStatus, TaskStatus[]> = {
[TaskStatus.Todo]: [TaskStatus.InProgress, TaskStatus.Cancelled],
[TaskStatus.InProgress]: [TaskStatus.Done, TaskStatus.Todo, TaskStatus.Cancelled],
[TaskStatus.Done]: [TaskStatus.InProgress], // 只能重新打开
[TaskStatus.Cancelled]: [TaskStatus.Todo] // 取消后可以恢复
};
// 校验状态转换
function canTransition(from: TaskStatus, to: TaskStatus): boolean {
return validTransitions[from]?.includes(to) ?? false;
}
这段代码里,validTransitions 就是状态机的核心。你想想看,如果以后要加新状态,比如「审核中」,只需要在这里加一行映射就行,其他地方不用动。这就是状态机的好处——变化集中,影响可控。
2.6 截止日期:别让它变成摆设
截止日期这个字段,很多人就是填个日期,然后就不管了。但实际用起来,有几个细节要注意:
- 允许为空:不是所有任务都有明确截止日期,别强制必填
- 过期提醒:状态不是「已完成」且当前时间超过截止日期,应该标记为「已逾期」
- 时间精度:大部分场景精确到天就够了,别搞到时分秒,徒增复杂度
2.7 小结
这一章我们聊了任务的核心数据结构。记住几个关键点:
- ID 用 UUID,别用自增整数
- 优先级分四档,别搞太复杂
- 状态机四个状态,转换规则写死在代码里
- 截止日期允许为空,但过期要能提醒