From 39282b96330e9b75be23c0b1e46467ee0c033bc1 Mon Sep 17 00:00:00 2001 From: "jif2.zhang" Date: Sun, 25 Oct 2026 22:59:08 +0800 Subject: [PATCH] =?UTF-8?q?docs(=E4=BB=BB=E5=8A=A1=E6=A8=A1=E5=9D=97):=20?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E5=BD=92=E6=A1=A3=20T-ARC=20=E9=9C=80?= =?UTF-8?q?=E6=B1=82=E4=B8=8E=E8=AE=BE=E8=AE=A1=20(T-ARC-01~08)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 需求文档 §3.15: 手动归档(⋯菜单+连带子任务)/自动归档(项目级档位 3天/一周/一月/永不)/归档排除/游标分页大量查询/恢复/彻底删除/配置入口/权限 - 功能设计 §15: Project.autoArchive + Task.archived/archivedAt/archiveSource、 归档/恢复/归档查询 API、自动归档双保险执行(读时惰性+小时cron)、 边界处理; 覆盖自检表更新为 157 条 - 数据库设计: Project 补 notes/taskKinds/icon/autoArchive 字段, Task 补归档三字段与两索引, 迁移清单标注 20261026000000_task_archive 待实施 --- docs/V0.2/任务模块/任务模块功能设计.md | 63 +++++++++++++++++++- docs/V0.2/任务模块/任务模块数据库设计文档.md | 22 +++++++ docs/V0.2/任务模块/任务模块需求文档.md | 21 ++++++- 3 files changed, 101 insertions(+), 5 deletions(-) diff --git a/docs/V0.2/任务模块/任务模块功能设计.md b/docs/V0.2/任务模块/任务模块功能设计.md index 868c1e2..79f2cd8 100644 --- a/docs/V0.2/任务模块/任务模块功能设计.md +++ b/docs/V0.2/任务模块/任务模块功能设计.md @@ -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-NF-01 ~ 05 | 5 | §13 | | 异常与边界 | 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-*` 为评审后新增,编号不连续,非笔误。) diff --git a/docs/V0.2/任务模块/任务模块数据库设计文档.md b/docs/V0.2/任务模块/任务模块数据库设计文档.md index e7e138e..9c28732 100644 --- a/docs/V0.2/任务模块/任务模块数据库设计文档.md +++ b/docs/V0.2/任务模块/任务模块数据库设计文档.md @@ -93,6 +93,10 @@ | `ownerId` | `INT` | 否 | — | 创建人(可退出项目,故与成员表分离) | | `isPersonal` | `BOOLEAN` | 否 | `false` | 个人默认项目:不可删、不可加成员(T-PJ-02/03) | | `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()` | — | | `updatedAt` | `DATETIME(3)` | 否 | — | `@updatedAt` | @@ -186,6 +190,9 @@ | `creatorId` | `INT` | 否 | — | 创建人;拷贝时为操作者(T-CP-04) | | `extras` | `JSON` | 否 | `[]` | 扩展信息卡片 `[{label,value,url?}]`(T-TK-04/05) | | `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()` | — | | `updatedAt` | `DATETIME(3)` | 否 | — | — | @@ -202,6 +209,8 @@ | `idx_task_project_status` | `(projectId, status)` | — | 列表/统计 | | `idx_task_parent` | `(parentId)` | — | 取子任务 | | `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` 删除行为保持一致(本期系统无删除用户入口)。 @@ -214,6 +223,8 @@ | 子任务 `projectId` == 父任务 `projectId`(T-EX-12) | 应用层 + 事务内校验 | | `parentId` 不得指向自身或其后代(T-EX-15) | 应用层 | | `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) | 应用层 | ### 3.5 `TaskMember` — 任务成员 @@ -375,6 +386,7 @@ model User { | 权限校验 | `SELECT 1 FROM ProjectMember WHERE projectId=? AND userId=?` | | 保留窗口过滤 | 服务端可选:`status <> 'done' OR (doneAt >= ?)`;前端另有一份同口径实现 | | 自动归位选列 | 内存中对已加载的 `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` 分组。 @@ -391,6 +403,16 @@ model User { 3. 建唯一索引与普通索引; 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 个人默认项目初始化 ```sql diff --git a/docs/V0.2/任务模块/任务模块需求文档.md b/docs/V0.2/任务模块/任务模块需求文档.md index 8ba1ee5..126d0a5 100644 --- a/docs/V0.2/任务模块/任务模块需求文档.md +++ b/docs/V0.2/任务模块/任务模块需求文档.md @@ -384,12 +384,29 @@ | T-EX-19 | 新增列时已达列数上限 | 拒绝新增并提示已达上限,不产生空列 | 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. 优先级汇总(本期交付范围) -- **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。 -- **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。 +- **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;T-ARC-02、06、07、08。 ---