集合:任务清单(tasks)
1. 目的与使用场景
- 任务Tab是用户的行动中枢,所有待办事项都在这里汇集和管理。系统管理多来源任务看板,支持来自"今天"页的AI建议、来自"心情随笔"的派生任务、来自"树洞"社区领取的行动卡,以及用户自建的任务等。提供分类与筛选功能,按来源或情绪类别过滤列表,自动按状态分组(进行中/已完成/延期)。支持执行与复盘闭环,用户可以对任务进行开始、暂停、完成、跳过等操作,完成后引导填写复盘表单记录情绪变化。提供数据洞察功能,通过上拉抽屉查看任务完成度曲线、延迟原因分析等统计信息。
枚举值:
- source:
suggestion(建议)、journal(随笔)、action_card(行动卡)、weekly_report(周报)、quick_note(快速记录)、manual(手动)、counselor(辅导员)、mentor(导师) - status:
pending(待办)、in_progress(进行中)、completed(已完成)、skipped(已跳过)、expired(已过期)、pending_confirmation(待确认)、needs_review(需复盘)、on_hold(暂停)、deferred(延期) - category:
study(学习)、sleep(睡眠)、emotion(情绪)、relationship(关系)、health(健康)、other(其他) - emotion_category:
social(社交)、interpersonal(人际)、academic(学业)、self(自我)、health(健康)、other(其他) - priority:
low(低)、medium(中)、high(高)
2. Schema 定义
| 字段 | 类型 | 必填 | 默认值 | 约束/校验 | 说明 | 隐私分级 |
|---|---|---|---|---|---|---|
| _id | string | 是 | - | TCB 自动 | 主键 | P2 |
| _openid | string | 是 | - | TCB 自动 | 创建人 openid | P2 |
| user_id | 指针id | 是 | - | 指向 users._id | 用户 | P2 |
| title | string | 是 | - | 长度 1-80 | 任务标题 | P1 |
| description | string | 否 | - | 长度 ≤ 512 | 任务说明 | P1 |
| source | string | 是 | suggestion | 枚举(suggestion,journal,action_card,weekly_report,quick_note,manual,counselor,mentor) | 来源 | P1 |
| origin_reference_type | string | 否 | - | 枚举(suggestion,post,journal,template,weekly_report) | 来源实体类型 | P1 |
| origin_reference_id | 指针id | 否 | - | 指向关联集合 | 来源实体 ID | P1 |
| category | string | 否 | - | 枚举(study,sleep,emotion,relationship,health,other) | 分类标签 | P1 |
| emotion_category | string | 否 | - | 枚举(social,interpersonal,academic,self,health,other) | 情绪类别 | P1 |
| status | string | 是 | pending | 枚举(pending,in_progress,completed,skipped,expired,pending_confirmation,needs_review,on_hold,deferred) | 状态 | P1 |
| due_at | date | 否 | - | ISO 字符串 | 截止时间 | P1 |
| reminder_at | date | 否 | - | ISO 字符串 | 提醒时间 | P1 |
| estimated_duration_minutes | number | 否 | 10 | 正整数 ≤ 240 | 预计耗时 | P1 |
| priority | string | 否 | medium | 枚举(low,medium,high) | 优先级 | P1 |
| recurrence_rule | object | 否 | - | {type, interval, end_date} | 重复配置 | P1 |
| checklist_steps | array | 否 | [] | 元素 object {text,is_done,note} | 子步骤 | P1 |
| linked_checkin_id | 指针id | 否 | - | 指向 checkins._id | 完成回执 | P2 |
| adherence_score | number | 否 | - | 0-1 | 完成后效果评分 | P2 |
| tags | array | 否 | [] | - | 自定义标签 | P1 |
| is_pinned | bool | 否 | false | - | 今天页置顶 | P1 |
| assignment_info | object | 否 | - | {assignee_id, assignee_type, status, due_date} | 真人指派信息 | P2 |
| pomodoro_sessions | array | 否 | [] | 元素 object {start_time,end_time,duration_minutes,actual_duration,completed} | 番茄计时记录 | P2 |
| review_data | object | 否 | - | {mood_before,mood_after,review_notes,attachments,needs_human_followup} | 复盘数据 | P2 |
| task_status_logs | array | 否 | [] | 元素 object {status,timestamp,reason,operator_id} | 状态变更日志 | P2 |
| batch_operation_id | string | 否 | - | - | 批量操作标识 | P1 |
| createdAt | date | 是 | - | 服务端时间 | 创建时间 | P1 |
| updatedAt | date | 是 | - | 服务端时间 | 更新时间 | P1 |
| is_deleted | bool | 否 | false | - | 软删 | P1 |
3. 索引与唯一约束
- 单字段索引:
user_id、status、due_at、source、emotion_category、priority - 复合索引:
[user_id, status, due_at]、[user_id, is_deleted, createdAt desc]、[user_id, source, status]、[assignment_info.assignee_id, status]、[origin_reference_type, origin_reference_id] - 唯一性约束:无
- 设计理由:清单页过滤状态和时间排序频繁;今天页需要快速定位置顶未完成任务;来源和情绪类别筛选统计;真人指派任务跟踪;支持来源关联查询。
4. 访问控制(TCB 权限)
{
"read": "_openid == auth.openid || auth.role in ['counselor']",
"write": "_openid == auth.openid || auth.role in ['ops','counselor']"
}辅导员可在干预时标记完成;运营可协助发布运营任务。
- 关系与级联
user_id→usersorigin_reference_id→suggestions/journals/postslinked_checkin_id→checkins- 删除策略:软删并保留在周报统计;相关 checkins 不级联删除。
- 反范式:
adherence_score供周报直接读取;更新时需同步周报缓存。
- 数据生命周期与合规
- 留存:历史任务保留 1 年以便行为分析,过期后保留汇总指标并软删详情。
- 匿名化:导出时去除
description,仅保留分类与状态。 - 删除流程:用户可在清单页删除 → 软删 → 每晚批处理硬删过期任务。
- 审计字段:
createdAt、updatedAt、status、adherence_score。
API/云函数契约映射 | 接口/函数 | 读/写 | 使用字段 | 过滤条件 | 排序/分页 | 备注 | |
/api/tasksPOST | 写 | title, description, source, origin_reference_id, due_at, reminder_at, emotion_category |_openid = auth.openid| - | 今天页一键生成 | |/api/tasksGET | 读 | title, status, due_at, estimated_duration_minutes, checklist_steps, source, emotion_category |user_id = auth.uid,is_deleted=false| 多种排序 | 任务列表 | |/api/tasksPATCH | 写 | status, checklist_steps, linked_checkin_id, adherence_score, review_data |_id匹配 | - | 状态更新 | |/api/tasks/bulk| 写 | status, due_at, reminder_at, batch_operation_id |_openid = auth.openid| - | 批量操作 | | 番茄计时云函数pomodoroTracker| 写 | pomodoro_sessions |task_id匹配 | - | 计时记录 | | 行动卡同步云函数syncActionCardTask| 写 | title, source, origin_reference_type, origin_reference_id, category | - | - | 树洞行动卡 | | 周报云函数generateWeeklyReport| 读 | status, adherence_score, due_at, source | 周期过滤 | - | 统计完成率 | | 真人指派云函数taskAssignment| 写 | assignment_info, status |task_id匹配 | - | 指派管理 |示例文档(≥3条)
{
"_id": "task_305",
"_openid": "oAbcd123",
"user_id": "usr_001",
"title": "和室友约定安静时间",
"description": "准备三个建议时段,与室友讨论并确认。",
"source": "suggestion",
"origin_reference_type": "suggestion",
"origin_reference_id": "sg_001",
"category": "relationship",
"emotion_category": "interpersonal",
"status": "in_progress",
"due_at": "2024-04-08T15:00:00Z",
"estimated_duration_minutes": 20,
"priority": "medium",
"checklist_steps": [{"text": "列出三个时间方案", "is_done": true}, {"text": "与室友讨论", "is_done": false}],
"pomodoro_sessions": [{"start_time": "2024-04-06T14:00:00Z", "end_time": "2024-04-06T14:15:00Z", "duration_minutes": 15, "actual_duration": 12, "completed": true}],
"createdAt": "2024-04-06T13:25:00Z",
"updatedAt": "2024-04-06T19:10:00Z"
}
{
"_id": "task_401",
"_openid": "oAbcd456",
"user_id": "usr_002",
"title": "晚安呼吸练习",
"source": "weekly_report",
"category": "sleep",
"emotion_category": "health",
"status": "pending",
"due_at": "2024-04-07T22:00:00Z",
"reminder_at": "2024-04-07T21:30:00Z",
"estimated_duration_minutes": 10,
"createdAt": "2024-04-06T12:00:00Z",
"updatedAt": "2024-04-06T12:00:00Z"
}
{
"_id": "task_512",
"_openid": "oAbcd123",
"user_id": "usr_001",
"title": "完成心理测评",
"source": "counselor",
"origin_reference_type": "weekly_report",
"origin_reference_id": "wr_001",
"category": "emotion",
"emotion_category": "self",
"status": "pending_confirmation",
"assignment_info": {"assignee_id": "counselor_001", "assignee_type": "counselor", "status": "pending", "due_date": "2024-04-10T17:00:00Z"},
"due_at": "2024-04-10T17:00:00Z",
"estimated_duration_minutes": 30,
"createdAt": "2024-04-01T08:00:00Z",
"updatedAt": "2024-04-07T20:05:00Z"
}
{
"_id": "task_600",
"_openid": "oAbcd123",
"user_id": "usr_001",
"title": "冥想练习",
"description": "进行10分钟的正念冥想练习",
"source": "action_card",
"origin_reference_type": "post",
"origin_reference_id": "post_901",
"category": "emotion",
"emotion_category": "self",
"status": "completed",
"linked_checkin_id": "ck_20240407_task",
"adherence_score": 0.9,
"review_data": {"mood_before": 45, "mood_after": 72, "review_notes": "感觉放松了很多,会继续坚持", "attachments": [], "needs_human_followup": false},
"pomodoro_sessions": [{"start_time": "2024-04-07T20:00:00Z", "end_time": "2024-04-07T20:10:00Z", "duration_minutes": 10, "actual_duration": 10, "completed": true}],
"createdAt": "2024-04-07T19:50:00Z",
"updatedAt": "2024-04-07T20:15:00Z"
}- 常用查询样例(≥3条)
// 获取待办任务
const todo = await db.collection('tasks').where({
user_id: auth.uid,
status: db.command.in(['pending', 'in_progress']),
is_deleted: false
}).orderBy('due_at', 'asc').get();
// 统计完成任务数量
const completedCount = await db.collection('tasks').where({
user_id: auth.uid,
status: 'completed',
createdAt: db.command.gte(new Date(Date.now() - 7 * 86400000).toISOString())
}).count();
// 查找来源于某行动卡的任务
const actionTasks = await db.collection('tasks').where({
origin_reference_type: 'post',
origin_reference_id: postId,
is_deleted: false
}).get();- 边界与错误码
- 重复生成:相同来源在 10 分钟内重复写入返回
E_TASK_DUPLICATE。 - 状态回退:已完成任务再次标记为进行中需确认,若违例返回
E_TASK_STATE_INVALID。 - 真人指派:非 counselor/mentor 角色指派任务返回
E_ASSIGNMENT_FORBIDDEN。 - 番茄计时冲突:同一时间多个计时器运行返回
E_POMODORO_CONFLICT。 - 越权:非本人修改返回
E_FORBIDDEN。
- 变更影响评估
- 新字段影响
/api/tasks、/api/checklistsDTO 与埋点task_status_update、task_review_submit。 - 索引调整需同步周报聚合及行为看板。
- 假设与待确认
- 假设任务与 checkins 为 1:1 回执,待确认是否允许多个情绪记录关联同一任务。
- 待确认
recurrence_rule是否需要单独的实例化表。