docs(任务模块): 任务归档 T-ARC 需求与设计 (T-ARC-01~08)
- 需求文档 §3.15: 手动归档(⋯菜单+连带子任务)/自动归档(项目级档位 3天/一周/一月/永不)/归档排除/游标分页大量查询/恢复/彻底删除/配置入口/权限 - 功能设计 §15: Project.autoArchive + Task.archived/archivedAt/archiveSource、 归档/恢复/归档查询 API、自动归档双保险执行(读时惰性+小时cron)、 边界处理; 覆盖自检表更新为 157 条 - 数据库设计: Project 补 notes/taskKinds/icon/autoArchive 字段, Task 补归档三字段与两索引, 迁移清单标注 20261026000000_task_archive 待实施
This commit is contained in:
@@ -319,7 +319,63 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 15. 覆盖自检表
|
## 15. 任务归档(T-ARC)
|
||||||
|
|
||||||
|
> 归档与 T-FL 显示控制的分层:**隐藏 = 展示过滤(可逆、不落库)**;**归档 = 移出主工作集(落库、可恢复)**。
|
||||||
|
> 自动归档的倒计时起点是任务**被隐藏的时刻**(由 `doneAt + 列保留窗口` 推导,不新增 hiddenAt 字段)。
|
||||||
|
|
||||||
|
### 15.1 数据
|
||||||
|
|
||||||
|
| 落点 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `Project.autoArchive VARCHAR(8) NOT NULL DEFAULT 'never'` | 自动归档档位:`3d` / `1w` / `1m` / `never`;项目级共享(同 T-FL-12 口径) |
|
||||||
|
| `Task.archived BOOLEAN NOT NULL DEFAULT false` | 归档标记;归档**不迁移表**,主查询统一追加 `archived = false` |
|
||||||
|
| `Task.archivedAt DATETIME(3) NULL` | 归档时刻(查询过滤与排序键) |
|
||||||
|
| `Task.archiveSource VARCHAR(8) NULL` | `'manual'` / `'auto'`,溯源用 |
|
||||||
|
| 索引 | `idx_task_project_archived (projectId, archived, archivedAt)`;`idx_task_project_status_archived (projectId, status, archived)` |
|
||||||
|
| 迁移(待实施) | `20261026000000_task_archive`:三列 + 两索引(MySQL DDL 见数据库设计文档 §3.1/§3.4) |
|
||||||
|
|
||||||
|
### 15.2 API
|
||||||
|
|
||||||
|
| 接口 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| `POST /api/tasks/:id/archive` | 手动归档:父任务连带全部后代置 `archived/archivedAt/archiveSource='manual'`;幂等(已归档直接返回当前态) |
|
||||||
|
| `POST /api/tasks/:id/unarchive` | 恢复:父任务连带后代清除归档三字段;原列因 `ON DELETE RESTRICT` 必存在,状态保持不变 |
|
||||||
|
| `GET /api/projects/:id/tasks/archived?keyword&kind&columnId&from&to&cursor&limit` | 归档查询:`keyword` 标题 LIKE、`kind`、`columnId`、`from/to`(归档日期,含边界);`limit` 默认 20、≤100;游标 = `archivedAt+id` 复合降序,返回 `{ items, nextCursor }`,`nextCursor` 为空即到底 |
|
||||||
|
| `PUT /api/projects/:id` | 增接受 `autoArchive`,枚举校验(非法 400) |
|
||||||
|
| 既有查询 | `GET /projects/:id/tasks`、统计、列计数等全部追加 `archived: false`;`taskCount`(projectSummary)同口径 |
|
||||||
|
|
||||||
|
### 15.3 自动归档执行
|
||||||
|
|
||||||
|
- **归档到期判定**:`due = 隐藏时刻 + autoArchive 档位`;隐藏时刻 = `doneAt + 列保留窗口`(复用 `retentionStart` 口径,含 `today/3d/week/month`;`forever` 列任务永不隐藏、不进入自动归档)。
|
||||||
|
- **执行时机(双保险)**:
|
||||||
|
1. **读时惰性**:加载项目任务时对该项目先执行一轮到期归档(保证用户看到的数据与判定一致,刚到期的任务当场消失);
|
||||||
|
2. **每小时 cron 兜底**:扫描全部 `autoArchive != 'never'` 项目的到期 done 任务批量归档(`archiveSource='auto'`)。
|
||||||
|
- **幂等**:`archived = false` 且 `status = 'done'` 且 `doneAt` 非空才进入判定;重复执行无副作用。
|
||||||
|
- **批量写**:cron 用 `updateMany`(按到期集合),读时惰性用单项目范围 `updateMany`。
|
||||||
|
|
||||||
|
### 15.4 前端
|
||||||
|
|
||||||
|
| 落点 | 说明 |
|
||||||
|
| --- | --- |
|
||||||
|
| 任务 `⋯` 菜单 | 新增「归档」(`Dialog.confirm`,含子任务时提示连带条数);成功后列表/看板乐观移除 |
|
||||||
|
| 项目管理 | 「归档设置」分区:`Selector` 四档(3 天后/一周后/一月后/永不),保存即生效 |
|
||||||
|
| 项目视图头部 | 项目栏新增「已归档」入口 → 全屏弹层:过滤条(关键词/类型/列/归档时段)+ 分页「加载更多」+ 行内「恢复 / 删除」 |
|
||||||
|
| 主视图 | 列表/看板无需感知归档(数据源已排除);恢复后经 `load()` 回到原位 |
|
||||||
|
|
||||||
|
### 15.5 边界
|
||||||
|
|
||||||
|
| 场景 | 处理 |
|
||||||
|
| --- | --- |
|
||||||
|
| 归档父任务时新增/恢复子任务 | 子任务随父判定:父 archived 则子不单独出现在主列表(查询按父排除) |
|
||||||
|
| 自动归档期间用户正在编辑该任务 | 详情弹层保存正常(归档不锁写);保存后任务仍在归档集 |
|
||||||
|
| 恢复后任务再次到期隐藏 | 重新进入自动归档倒计时(`doneAt` 未变则可能立即再次到期,属预期) |
|
||||||
|
| 「永不」档位 | 不做任何自动归档;手动归档不受影响 |
|
||||||
|
| 拷贝任务 | 拷贝件不继承归档态(`copyTask` 不复制 archived 三字段) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 16. 覆盖自检表
|
||||||
|
|
||||||
| 模块 | 编号范围 | 条数 | 本文对应章节 |
|
| 模块 | 编号范围 | 条数 | 本文对应章节 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
@@ -337,6 +393,7 @@
|
|||||||
| 数据需求 | T-DT-01 ~ 09 | 9 | §12 |
|
| 数据需求 | T-DT-01 ~ 09 | 9 | §12 |
|
||||||
| 非功能需求 | T-NF-01 ~ 05 | 5 | §13 |
|
| 非功能需求 | T-NF-01 ~ 05 | 5 | §13 |
|
||||||
| 异常与边界 | T-EX-01 ~ 19 | 19 | §14 |
|
| 异常与边界 | T-EX-01 ~ 19 | 19 | §14 |
|
||||||
| **合计** | — | **149** | — |
|
| 任务归档 | T-ARC-01 ~ 08 | 8 | §15 |
|
||||||
|
| **合计** | — | **157** | — |
|
||||||
|
|
||||||
> 需求文档共 **149 条**需求点,本文逐条给出设计落点,覆盖率 **149/149**。(其中 `T-BD-14~16`、`T-COL-*`、`T-EX-18~19`、`T-VW-08~10`、`T-PJ-11/12`、`T-UI-11` 为评审后新增,编号不连续,非笔误。)
|
> 需求文档共 **157 条**需求点,本文逐条给出设计落点,覆盖率 **157/157**。(其中 `T-BD-14~16`、`T-COL-*`、`T-EX-18~19`、`T-VW-08~10`、`T-PJ-11/12`、`T-UI-11`、`T-ARC-*` 为评审后新增,编号不连续,非笔误。)
|
||||||
|
|||||||
@@ -93,6 +93,10 @@
|
|||||||
| `ownerId` | `INT` | 否 | — | 创建人(可退出项目,故与成员表分离) |
|
| `ownerId` | `INT` | 否 | — | 创建人(可退出项目,故与成员表分离) |
|
||||||
| `isPersonal` | `BOOLEAN` | 否 | `false` | 个人默认项目:不可删、不可加成员(T-PJ-02/03) |
|
| `isPersonal` | `BOOLEAN` | 否 | `false` | 个人默认项目:不可删、不可加成员(T-PJ-02/03) |
|
||||||
| `archived` | `BOOLEAN` | 否 | `false` | 归档(T-PJ-09) |
|
| `archived` | `BOOLEAN` | 否 | `false` | 归档(T-PJ-09) |
|
||||||
|
| `notes` | `JSON` | 否 | `[]` | 项目信息块 `[{title, content}]`(T-PJ-13) |
|
||||||
|
| `taskKinds` | `JSON` | **是** | `NULL` | 任务类型标签列表;`NULL` 时服务端回落默认四类(T-TK-14) |
|
||||||
|
| `icon` | `TEXT` | **是** | `NULL` | 项目图标 data URL(客户端裁剪 96x96 JPEG) |
|
||||||
|
| `autoArchive` | `VARCHAR(8)` | 否 | `'never'` | 自动归档档位:`3d`/`1w`/`1m`/`never`(T-ARC-02) |
|
||||||
| `createdAt` | `DATETIME(3)` | 否 | `now()` | — |
|
| `createdAt` | `DATETIME(3)` | 否 | `now()` | — |
|
||||||
| `updatedAt` | `DATETIME(3)` | 否 | — | `@updatedAt` |
|
| `updatedAt` | `DATETIME(3)` | 否 | — | `@updatedAt` |
|
||||||
|
|
||||||
@@ -186,6 +190,9 @@
|
|||||||
| `creatorId` | `INT` | 否 | — | 创建人;拷贝时为操作者(T-CP-04) |
|
| `creatorId` | `INT` | 否 | — | 创建人;拷贝时为操作者(T-CP-04) |
|
||||||
| `extras` | `JSON` | 否 | `[]` | 扩展信息卡片 `[{label,value,url?}]`(T-TK-04/05) |
|
| `extras` | `JSON` | 否 | `[]` | 扩展信息卡片 `[{label,value,url?}]`(T-TK-04/05) |
|
||||||
| `sortOrder` | `INT` | 否 | `0` | 列内/列表内排序 |
|
| `sortOrder` | `INT` | 否 | `0` | 列内/列表内排序 |
|
||||||
|
| `archived` | `BOOLEAN` | 否 | `false` | 归档标记;主查询一律 `archived = false`(T-ARC-01/03) |
|
||||||
|
| `archivedAt` | `DATETIME(3)` | **是** | `NULL` | 归档时刻;归档查询的过滤/排序键(T-ARC-04) |
|
||||||
|
| `archiveSource` | `VARCHAR(8)` | **是** | `NULL` | `'manual'` / `'auto'` 溯源(T-ARC-01/02) |
|
||||||
| `createdAt` | `DATETIME(3)` | 否 | `now()` | — |
|
| `createdAt` | `DATETIME(3)` | 否 | `now()` | — |
|
||||||
| `updatedAt` | `DATETIME(3)` | 否 | — | — |
|
| `updatedAt` | `DATETIME(3)` | 否 | — | — |
|
||||||
|
|
||||||
@@ -202,6 +209,8 @@
|
|||||||
| `idx_task_project_status` | `(projectId, status)` | — | 列表/统计 |
|
| `idx_task_project_status` | `(projectId, status)` | — | 列表/统计 |
|
||||||
| `idx_task_parent` | `(parentId)` | — | 取子任务 |
|
| `idx_task_parent` | `(parentId)` | — | 取子任务 |
|
||||||
| `idx_task_done_at` | `(projectId, doneAt)` | — | 保留窗口计算 |
|
| `idx_task_done_at` | `(projectId, doneAt)` | — | 保留窗口计算 |
|
||||||
|
| `idx_task_project_archived` | `(projectId, archived, archivedAt)` | — | 已归档查询(T-ARC-04) |
|
||||||
|
| `idx_task_project_status_arch` | `(projectId, status, archived)` | — | 主列表/看板排除归档(T-ARC-03) |
|
||||||
|
|
||||||
> \* `creatorId` 用 Cascade 会丢失"他人创建的任务"的历史;更稳妥的做法是 `Restrict` 并禁止删除有任务的用户。**建议改为 `Restrict`**,与既有 `User` 删除行为保持一致(本期系统无删除用户入口)。
|
> \* `creatorId` 用 Cascade 会丢失"他人创建的任务"的历史;更稳妥的做法是 `Restrict` 并禁止删除有任务的用户。**建议改为 `Restrict`**,与既有 `User` 删除行为保持一致(本期系统无删除用户入口)。
|
||||||
|
|
||||||
@@ -214,6 +223,8 @@
|
|||||||
| 子任务 `projectId` == 父任务 `projectId`(T-EX-12) | 应用层 + 事务内校验 |
|
| 子任务 `projectId` == 父任务 `projectId`(T-EX-12) | 应用层 + 事务内校验 |
|
||||||
| `parentId` 不得指向自身或其后代(T-EX-15) | 应用层 |
|
| `parentId` 不得指向自身或其后代(T-EX-15) | 应用层 |
|
||||||
| `status='done'` 时写 `doneAt=now()`;退出 `done` 时置 `NULL` | 应用层(领域统一裁决) |
|
| `status='done'` 时写 `doneAt=now()`;退出 `done` 时置 `NULL` | 应用层(领域统一裁决) |
|
||||||
|
| 归档/恢复父任务连带全部后代,同事务内更新 `archived`/`archivedAt`/`archiveSource`(T-ARC-01/05) | 应用层 |
|
||||||
|
| `autoArchive` 枚举 `3d`/`1w`/`1m`/`never`,非法 400(T-ARC-02) | 应用层 |
|
||||||
| `assigneeId` 必须是 `ProjectMember` 中的用户(T-AS-03) | 应用层 |
|
| `assigneeId` 必须是 `ProjectMember` 中的用户(T-AS-03) | 应用层 |
|
||||||
|
|
||||||
### 3.5 `TaskMember` — 任务成员
|
### 3.5 `TaskMember` — 任务成员
|
||||||
@@ -375,6 +386,7 @@ model User {
|
|||||||
| 权限校验 | `SELECT 1 FROM ProjectMember WHERE projectId=? AND userId=?` |
|
| 权限校验 | `SELECT 1 FROM ProjectMember WHERE projectId=? AND userId=?` |
|
||||||
| 保留窗口过滤 | 服务端可选:`status <> 'done' OR (doneAt >= ?)`;前端另有一份同口径实现 |
|
| 保留窗口过滤 | 服务端可选:`status <> 'done' OR (doneAt >= ?)`;前端另有一份同口径实现 |
|
||||||
| 自动归位选列 | 内存中对已加载的 `columns`(按 sortOrder)`find(c => c.boundStatus === status)` |
|
| 自动归位选列 | 内存中对已加载的 `columns`(按 sortOrder)`find(c => c.boundStatus === status)` |
|
||||||
|
| 已归档查询(T-ARC-04) | `WHERE projectId=? AND archived=1 [AND kind=?] [AND columnId=?] [AND archivedAt BETWEEN ? AND ?] [AND title LIKE ?] ORDER BY archivedAt DESC, id DESC LIMIT ?+1`;多取 1 条判定 `nextCursor` |
|
||||||
|
|
||||||
**防 N+1**:一次请求返回 `{ project, columns, members, tasks }`,子任务与父任务同批返回,由前端按 `parentId` 分组。
|
**防 N+1**:一次请求返回 `{ project, columns, members, tasks }`,子任务与父任务同批返回,由前端按 `parentId` 分组。
|
||||||
|
|
||||||
@@ -391,6 +403,16 @@ model User {
|
|||||||
3. 建唯一索引与普通索引;
|
3. 建唯一索引与普通索引;
|
||||||
4. **不做数据回填**(本模块为新功能,无历史数据)。
|
4. **不做数据回填**(本模块为新功能,无历史数据)。
|
||||||
|
|
||||||
|
后续增量迁移:
|
||||||
|
|
||||||
|
| 迁移 | 内容 | 状态 |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `20261022120000_add_checkin_checklist` | 打卡/清单模块 | 已上线 |
|
||||||
|
| `20261025030000_task_kind` | `Project.taskKinds` / `Task.kind` | 已上线 |
|
||||||
|
| `20261025120000_task_mark_date` | `Task.markDate` | 已上线 |
|
||||||
|
| `20261025130000_project_icon` | `Project.icon` | 已上线 |
|
||||||
|
| `20261026000000_task_archive` | 任务归档三字段 + 两索引(T-ARC,DDL 见 §3.1/§3.4 与功能设计 §15) | **待实施** |
|
||||||
|
|
||||||
### 6.2 个人默认项目初始化
|
### 6.2 个人默认项目初始化
|
||||||
|
|
||||||
```sql
|
```sql
|
||||||
|
|||||||
@@ -384,12 +384,29 @@
|
|||||||
| T-EX-19 | 新增列时已达列数上限 | 拒绝新增并提示已达上限,不产生空列 | P1 |
|
| T-EX-19 | 新增列时已达列数上限 | 拒绝新增并提示已达上限,不产生空列 | P1 |
|
||||||
| T-EX-18 | 已完成任务跨周期(周一零点 / 月初零点)后应隐藏 | **当场消失**并带移除动画,其余任务平滑重排,不留空白 | P1 |
|
| T-EX-18 | 已完成任务跨周期(周一零点 / 月初零点)后应隐藏 | **当场消失**并带移除动画,其余任务平滑重排,不留空白 | P1 |
|
||||||
|
|
||||||
|
### 3.15 任务归档(T-ARC)
|
||||||
|
|
||||||
|
> 与「已完成显示控制(T-FL)」的关系:T-FL 决定任务**看不看得见**(可逆的展示过滤),
|
||||||
|
> 归档决定任务**在不在主工作集**(数据层面的收纳)。隐藏是自动归档的前置条件,归档移出主列表,
|
||||||
|
> 可通过「已归档查询」恢复。
|
||||||
|
|
||||||
|
| 编号 | 需求描述 | 验收要点 | 优先级 |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| T-ARC-01 | **手动归档**:任务 `⋯` 菜单增加「归档」;任意未归档任务(不限状态)均可归档;父任务归档时**连带全部子任务**一并归档;操作需二次确认(提示连带条数) | 归档后任务从列表/看板消失;子任务同步消失;确认弹窗条数正确 | P0 |
|
||||||
|
| T-ARC-02 | **自动归档**:项目级配置「**隐藏的任务**多久归档」,可选 `3 天后 / 一周后 / 一月后 / 永不`(默认永不);已隐藏(T-FL 保留窗口过期)的 done 任务到期后由系统自动归档 | 配置保存后生效;隐藏任务到期被自动归档;「永不」时永不自动归档;未隐藏的任务不受影响 | P1 |
|
||||||
|
| T-ARC-03 | **归档排除**:已归档任务从任务列表、看板、列计数、项目任务数统计中排除 | 归档后各视图与计数立即减少;恢复后回到原位 | P0 |
|
||||||
|
| T-ARC-04 | **已归档查询**:独立入口查看本项目已归档任务;支持关键词(标题)、任务类型、看板列、归档时间范围过滤;**游标分页**支撑大量数据(千级) | 过滤条件生效;分页可连续加载到底;千级数据下查询流畅 | P0 |
|
||||||
|
| T-ARC-05 | **恢复**:已归档任务可取消归档,回到原看板列(列被删除的场景因外键 `ON DELETE RESTRICT` 不存在),状态保持不变;父任务恢复连带子任务 | 恢复后任务与子任务重新出现在列表/看板原列 | P0 |
|
||||||
|
| T-ARC-06 | **彻底删除**:已归档任务可物理删除(含子任务),二次确认 | 删除后不再出现在已归档查询 | P1 |
|
||||||
|
| T-ARC-07 | **配置入口**:项目管理新增「归档设置」分区,展示与修改自动归档档位 | 修改后即时保存并对全项目生效 | P1 |
|
||||||
|
| T-ARC-08 | **权限口径**:项目成员均可归档/恢复/删除归档任务(权限均等口径同 T-PM-04) | 与其他写操作权限一致,无额外限制 | P1 |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. 优先级汇总(本期交付范围)
|
## 4. 优先级汇总(本期交付范围)
|
||||||
|
|
||||||
- **P0(必须)**:T-VW-01~04、T-VW-06~09;T-COL-01~07、T-COL-09、T-COL-10;T-BD-01~10、13~16;T-FL-01~13、15;T-TK-01~11;T-AS-01~05、07;T-ST-01~09、11~14;T-PJ-01~08、10~13;T-PM-01、02、04~07;T-CP-01~06、08;T-UI-01~05、07~11;T-DT-01~08;T-NF-01~04;T-EX-01~05、07、09~12、15~17。
|
- **P0(必须)**:T-VW-01~04、T-VW-06~09;T-COL-01~07、T-COL-09、T-COL-10;T-BD-01~10、13~16;T-FL-01~13、15;T-TK-01~11;T-AS-01~05、07;T-ST-01~09、11~14;T-PJ-01~08、10~13;T-PM-01、02、04~07;T-CP-01~06、08;T-UI-01~05、07~11;T-DT-01~08;T-NF-01~04;T-EX-01~05、07、09~12、15~17;T-ARC-01、03、04、05。
|
||||||
- **P1(应该)**:T-VW-05、10;T-COL-08;T-BD-11、12;T-FL-14;T-TK-12、13;T-AS-06;T-ST-10、15;T-PJ-09;T-PM-03;T-CP-07、09;T-UI-06;T-DT-09;T-NF-05;T-EX-06、08、13、14、18、19。
|
- **P1(应该)**:T-VW-05、10;T-COL-08;T-BD-11、12;T-FL-14;T-TK-12、13;T-AS-06;T-ST-10、15;T-PJ-09;T-PM-03;T-CP-07、09;T-UI-06;T-DT-09;T-NF-05;T-EX-06、08、13、14、18、19;T-ARC-02、06、07、08。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user