diff --git a/ZHI_TASKS.md b/ZHI_TASKS.md new file mode 100644 index 0000000..b54f5c0 --- /dev/null +++ b/ZHI_TASKS.md @@ -0,0 +1,590 @@ +# ZHI_TASKS.md + +> 基于 `docs/milestone-propose-requirements.md` 拆分的开发 TODO +> 更新时间:2026-03-16 +> 目标:把需求拆到接近“可直接开工 / 可独立提 PR”的粒度 + +--- + +# 使用建议 + +- 每个一级任务尽量对应一个小阶段 +- 每个二级任务尽量对应一个可独立提交的改动点 +- 做之前先勾选“设计/盘点”,做完再勾选“测试/文档” + +--- + +# P0 - 盘点与设计冻结 + +## P0.1 旧状态与旧逻辑盘点 +- [ ] 搜出所有 milestone status 的旧枚举使用位置 +- [ ] 搜出所有 task status 的旧枚举使用位置 +- [ ] 搜出所有前端 status badge / 文案 / 下拉框位置 +- [ ] 搜出所有后端 transition / update / filter 逻辑位置 +- [ ] 搜出所有 CLI 中 status choices 的位置 +- [ ] 搜出所有测试里写死旧状态名的位置 + +## P0.2 现有类型体系盘点 +- [ ] 盘点所有 task_type 的定义位置 +- [ ] 盘点所有 task_subtype 的定义位置 +- [ ] 确认前后端 `task_type = task` 的全部使用点 +- [ ] 列出历史数据迁移时 `task_type = task` 的处理方案候选 + +## P0.3 数据迁移设计 +- [ ] 设计 milestone 旧状态 -> 新状态映射 +- [ ] 设计 task 旧状态 -> 新状态映射 +- [ ] 设计 `assignee` 与现有 `assignee_id` 的过渡策略 +- [ ] 设计 `feat_task_id` 存储什么值(task id string / task code) +- [ ] 设计 reopen 记录落地方式(comment / activity / 独立表) + +## P0.4 权限设计冻结 +- [ ] 确认 milestone 新权限名 +- [ ] 确认 task 新权限名 +- [ ] 确认 propose 新权限名 +- [ ] 确认 admin 是否默认拥有全部新增权限 +- [ ] 确认默认 role 是否需要预置部分权限 + +--- + +# P1 - 数据模型与迁移骨架 + +## P1.1 Milestone 模型改造 +- [ ] 修改后端 milestone enum 定义 +- [ ] 移除 `pending` +- [ ] 移除 `deferred` +- [ ] 移除 `progressing` +- [ ] 增加 `freeze` +- [ ] 增加 `undergoing` +- [ ] 增加 `completed` +- [ ] 增加 `started_at` +- [ ] 更新 milestone schema +- [ ] 更新 milestone response model + +## P1.2 Task 模型改造 +- [ ] 修改后端 task enum 定义 +- [ ] 移除 `progressing` +- [ ] 增加 `undergoing` +- [ ] 增加 `completed` +- [ ] 增加 `assignee` 字段 +- [ ] 明确 `assignee` 是否替代 `assignee_id` +- [ ] 更新 task schema +- [ ] 更新 task response model + +## P1.3 Propose 模型新增 +- [ ] 新建 propose model +- [ ] 增加 `project_id` +- [ ] 增加 `propose_code` +- [ ] 增加 `title` +- [ ] 增加 `description` +- [ ] 增加 `status` +- [ ] 增加 `created_by_id` +- [ ] 增加 `created_at` +- [ ] 增加 `updated_at` +- [ ] 增加 `feat_task_id` +- [ ] 将 `feat_task_id` 标记为服务端只读 + +## P1.4 Propose 编码支持 +- [ ] 设计 propose counter 表/字段复用方案 +- [ ] 实现按 project 独立计数 +- [ ] 实现 `P{i:05x}` 格式生成 +- [ ] 实现 `{proj_code}:P{i:05x}` 拼接 +- [ ] 为并发创建补唯一性保护 + +## P1.5 数据库迁移脚本 +- [ ] 编写 milestone 状态迁移脚本 +- [ ] 编写 task 状态迁移脚本 +- [ ] 编写新增 `started_at` 迁移 +- [ ] 编写新增 `assignee` 迁移 +- [ ] 编写新增 propose 表迁移 +- [ ] 编写新增 `feat_task_id` 迁移 +- [ ] 编写旧数据回填/默认值策略 + +## P1.6 模型层校验与 smoke test +- [ ] 确认应用启动不报 enum/schema 错误 +- [ ] 确认旧接口序列化不崩 +- [ ] 确认 migration 后可读写 milestone/task +- [ ] 确认 propose 表能正常创建记录 + +--- + +# P2 - 权限骨架 + +## P2.1 后端权限枚举/注册 +- [ ] 注册 `freeze milestone` +- [ ] 注册 `start milestone` +- [ ] 注册 `close milestone` +- [ ] 注册 `close task` +- [ ] 注册 `reopen closed task` +- [ ] 注册 `reopen completed task` +- [ ] 注册 `accept propose` +- [ ] 注册 `reject propose` +- [ ] 注册 `reopen rejected propose` + +## P2.2 默认角色与权限种子 +- [ ] 更新默认权限种子 +- [ ] 更新 admin 默认权限 +- [ ] 更新可能的 owner / manager / dev 权限分配 +- [ ] 写一份新旧权限差异说明 + +## P2.3 权限检查辅助方法 +- [ ] 为 milestone action 增加统一权限检查 helper +- [ ] 为 task action 增加统一权限检查 helper +- [ ] 为 propose action 增加统一权限检查 helper + +--- + +# P3 - Milestone 状态机后端 + +## P3.1 Milestone 动作接口 +- [ ] 设计 milestone 动作接口路径 +- [ ] 实现 freeze 接口 +- [ ] 实现 start 接口 +- [ ] 实现 close 接口 +- [ ] 明确 completed 自动触发入口 + +## P3.2 Freeze 业务规则 +- [ ] 查询当前 milestone 下 maintenance/release task 数量 +- [ ] 校验“有且仅有一个 release maintenance task” +- [ ] 失败时返回明确错误 detail +- [ ] 成功时写入 `status=freeze` +- [ ] 写 activity log / 操作记录 + +## P3.3 Start 业务规则 +- [ ] 读取 milestone 前置依赖 +- [ ] 校验依赖 milestone 是否完成 +- [ ] 校验依赖 task 是否完成 +- [ ] 校验其他依赖对象是否完成 +- [ ] 校验通过后写入 `status=undergoing` +- [ ] 自动写入 `started_at` +- [ ] 写 activity log / 操作记录 + +## P3.4 Close 业务规则 +- [ ] 仅允许 `open/freeze/undergoing -> closed` +- [ ] 校验 `close milestone` 权限 +- [ ] 对 `undergoing` close 增加附加确认/原因字段支持(如需要) +- [ ] 为后续“依赖修正”预留服务端入口 +- [ ] 写 activity log / 操作记录 + +## P3.5 Completed 自动触发 +- [ ] 在 task 完成逻辑里检测 release maintenance task +- [ ] 校验 milestone 下唯一 release task 已完成 +- [ ] 自动写入 milestone `completed` +- [ ] 防止重复触发 +- [ ] 写 activity log / 操作记录 + +## P3.6 Milestone 编辑限制 +- [ ] `open` 允许编辑基本信息 +- [ ] `freeze` 禁止范围调整相关字段修改 +- [ ] `completed` 禁止编辑 +- [ ] `closed` 禁止编辑 +- [ ] 对 feature 相关变更做服务端防守 + +--- + +# P4 - 依赖检查后端 + +## P4.1 通用依赖检查能力 +- [ ] 抽一个依赖检查 service/helper +- [ ] 支持检查 milestone 依赖 +- [ ] 支持检查 task 依赖 +- [ ] 支持检查 support 依赖 +- [ ] 支持检查 meeting 依赖 + +## P4.2 milestone 开始前检查 +- [ ] milestone start 复用通用依赖检查 +- [ ] 返回详细未满足依赖信息 + +## P4.3 task 开始前检查 +- [ ] task `pending -> open` 复用通用依赖检查 +- [ ] 返回详细未满足依赖信息 + +## P4.4 close 后依赖修正预留 +- [ ] 设计 milestone close 后依赖修正数据结构 +- [ ] 决定先阻塞 close 还是允许 close 后手动修复 +- [ ] 为未来依赖修正 UI/API 预留接口 + +--- + +# P5 - Task 状态机后端 + +## P5.1 Task 动作接口梳理 +- [ ] 明确继续复用现有 transition 接口还是拆动作接口 +- [ ] 如复用 transition,补足新状态合法性校验 +- [ ] 如拆动作接口,设计 open/start/finish/close/reopen API + +## P5.2 `pending -> open` +- [ ] 校验当前状态必须为 `pending` +- [ ] 校验前置依赖全部满足 +- [ ] 校验所在 milestone 允许 task 进入 open +- [ ] 更新状态为 `open` +- [ ] 写 activity log / 操作记录 + +## P5.3 `open -> undergoing` +- [ ] 校验当前状态必须为 `open` +- [ ] 校验 `assignee` 非空 +- [ ] 校验操作者就是 assignee +- [ ] 校验所在 milestone 为 `undergoing` +- [ ] 更新状态为 `undergoing` +- [ ] 写开始时间(如沿用 started_on) +- [ ] 写 activity log / 操作记录 + +## P5.4 `undergoing -> completed` +- [ ] 校验当前状态必须为 `undergoing` +- [ ] 校验操作者就是 assignee +- [ ] 校验 completion comment 已提交 +- [ ] 更新状态为 `completed` +- [ ] 写完成时间(如沿用 finished_on) +- [ ] 写 activity log / 操作记录 + +## P5.5 `pending/open/undergoing -> closed` +- [ ] 校验当前状态合法 +- [ ] 校验 `close task` 权限 +- [ ] 更新状态为 `closed` +- [ ] 写 close reason/comment(如需要) +- [ ] 写 activity log / 操作记录 + +## P5.6 `closed/completed -> open` reopen +- [ ] 校验当前状态为 `closed` 或 `completed` +- [ ] 对 `closed` 校验 `reopen closed task` 权限 +- [ ] 对 `completed` 校验 `reopen completed task` 权限 +- [ ] 更新状态回 `open` +- [ ] 写 reopen 记录 +- [ ] 写 activity log / 操作记录 + +## P5.7 Task 编辑权限服务端防守 +- [ ] `open + assignee=null` 允许编辑主体 +- [ ] `open + assignee!=null` 仅 assignee/admin 可编辑主体 +- [ ] `undergoing/completed/closed` 禁止主体编辑 +- [ ] 保留 comment 接口可用性 + +## P5.8 与 release maintenance task 联动 +- [ ] 在 task 完成时识别 release maintenance task +- [ ] 与 milestone completed 自动触发逻辑打通 +- [ ] 防止普通 task 完成误触发 milestone completed + +--- + +# P6 - Propose 后端 + +## P6.1 基础 CRUD +- [ ] 实现 create propose +- [ ] 实现 list proposes +- [ ] 实现 get propose detail +- [ ] 实现 update propose +- [ ] 限制仅 `open` 可编辑主体 + +## P6.2 Accept +- [ ] 设计 accept API +- [ ] 接收 milestone 选择参数 +- [ ] 校验 propose 当前状态为 `open` +- [ ] 校验 `accept propose` 权限 +- [ ] 校验 milestone 属于同 project +- [ ] 校验 milestone status 为 `open` +- [ ] 自动创建 `feature story task` +- [ ] 复制 `title` +- [ ] 复制 `description` +- [ ] 复制 `created_by` +- [ ] 设置新 task 默认状态 `pending` +- [ ] 自动回填 `feat_task_id` +- [ ] 更新 propose status 为 `accepted` +- [ ] 写 activity log / 操作记录 + +## P6.3 Reject +- [ ] 设计 reject API +- [ ] 校验 propose 当前状态为 `open` +- [ ] 校验 `reject propose` 权限 +- [ ] 支持 reason/comment +- [ ] 更新 propose status 为 `rejected` +- [ ] 写 activity log / 操作记录 + +## P6.4 Reopen +- [ ] 设计 reopen API +- [ ] 校验 propose 当前状态为 `rejected` +- [ ] 校验 `reopen rejected propose` 权限 +- [ ] 复用当前 propose,不创建新记录 +- [ ] 更新 propose status 为 `open` +- [ ] 写 reopen 记录 +- [ ] 写 activity log / 操作记录 + +## P6.5 `feat_task_id` 只读保护 +- [ ] update 接口中忽略客户端传入的 `feat_task_id` +- [ ] schema 中将 `feat_task_id` 标为只读输出字段 +- [ ] accept 成功后才允许服务端填充 `feat_task_id` + +--- + +# P7 - 移除 `task_type = task` + +## P7.1 后端清理 +- [ ] 从后端 enum/type 定义中移除 `task` +- [ ] 从 `TASK_SUBTYPE_MAP` 中移除 `task` +- [ ] 清理 create/update 校验逻辑中的 `task` + +## P7.2 前端清理 +- [ ] 从 CreateTaskModal 移除 `task` +- [ ] 从 CreateTaskPage 移除 `task` +- [ ] 更新 task type 下拉默认值 +- [ ] 确认默认类型改成什么(待定后落地) + +## P7.3 历史数据处理 +- [ ] 统计数据库中已有多少 `task_type=task` +- [ ] 确认迁移到哪个新 type/subtype +- [ ] 编写数据修复脚本 + +--- + +# P8 - 前端:Milestone 页面 + +## P8.1 类型与状态文案 +- [ ] 更新前端 milestone type/status 定义 +- [ ] 替换旧状态 badge +- [ ] 增加新状态 badge +- [ ] 展示 `started_at` + +## P8.2 Detail 页按钮 +- [ ] `open` 显示 `freeze` + `close` +- [ ] `freeze` 显示 `start` + `close` +- [ ] `undergoing` 显示 `close` +- [ ] `completed` 不显示状态动作按钮 +- [ ] `closed` 不显示状态动作按钮 + +## P8.3 按钮禁用与提示 +- [ ] `freeze` 不满足条件时禁用 +- [ ] `freeze` 提示缺少/多于 release maintenance task +- [ ] `start` 不满足依赖时禁用 +- [ ] `start` 提示未完成依赖 + +## P8.4 编辑态限制 +- [ ] `open` 时允许编辑基本信息 +- [ ] `freeze` 时禁止范围调整相关编辑项 +- [ ] `completed/closed` 时禁用编辑入口 + +--- + +# P9 - 前端:Task 页面 + +## P9.1 类型与状态文案 +- [ ] 更新前端 task status 类型定义 +- [ ] 移除 `progressing` +- [ ] 增加 `undergoing` +- [ ] 增加 `completed` + +## P9.2 Detail 页按钮 +- [ ] `pending` 显示 `open` + `close` +- [ ] `open` 显示 `start` + `close` +- [ ] `undergoing` 显示 `finish` + `close` +- [ ] `completed` 显示 `reopen` +- [ ] `closed` 显示 `reopen` +- [ ] 所有按钮按权限可见 + +## P9.3 编辑行为 +- [ ] `open + assignee=null` 时显示编辑入口 +- [ ] `open + assignee!=null` 时按 assignee/admin 显示编辑入口 +- [ ] `undergoing/completed/closed` 时隐藏/禁用主体编辑入口 +- [ ] 保留 comment 区域 + +## P9.4 Finish 流程 +- [ ] finish 前要求填写 comment +- [ ] 设计 finish + comment 的交互(同弹窗 / 先评论再点完成) +- [ ] finish 成功后刷新状态与 comment 列表 + +## P9.5 Reopen 流程 +- [ ] `completed` 显示 reopen +- [ ] `closed` 显示 reopen +- [ ] reopen 成功后状态回到 `open` +- [ ] 显示 reopen 历史记录(如可行) + +## P9.6 创建表单限制 +- [ ] 禁止通用 create task 创建 `feature story task` +- [ ] 禁止通用 create task 创建 `release maintenance task` +- [ ] 调整默认 task status 体验为 `pending` +- [ ] 调整默认 task type(移除 `task` 后) + +--- + +# P10 - 前端:Propose 页面 + +## P10.1 列表页 +- [ ] 新建 propose 列表页 +- [ ] 支持按 project 查看 +- [ ] 支持显示 status +- [ ] 支持显示 `propose_code` + +## P10.2 创建页/弹窗 +- [ ] 新建 propose 创建表单 +- [ ] 支持填写 title/description +- [ ] 自动归属当前 project + +## P10.3 详情页 +- [ ] 展示 `propose_code` +- [ ] 展示 `title` +- [ ] 展示 `description` +- [ ] 展示 `status` +- [ ] 展示 `feat_task_id` +- [ ] 展示 `created_by_id` +- [ ] 展示 `created_at` +- [ ] 展示 `updated_at` + +## P10.4 按钮 +- [ ] `open` 显示 `accept` / `reject` +- [ ] `accepted` 不显示状态动作按钮 +- [ ] `accepted` 显示 `View Generated Task` +- [ ] `rejected` 显示 `reopen` +- [ ] 所有按钮按权限可见 + +## P10.5 Accept 交互 +- [ ] 弹出 milestone 选择框 +- [ ] 仅列出当前 project 下 `open` 的 milestone +- [ ] accept 成功后展示生成的 feature task 跳转 +- [ ] 页面刷新后显示 `feat_task_id` + +## P10.6 Reject / Reopen 交互 +- [ ] reject 时支持填写 reason/comment +- [ ] reopen 时提示会把 propose 恢复到 `open` +- [ ] reopen 成功后刷新详情页状态 + +## P10.7 编辑限制 +- [ ] `open` 时允许编辑 title/description +- [ ] `accepted` 禁止编辑主体 +- [ ] `rejected` 禁止编辑主体 + +--- + +# P11 - 权限管理前端/管理页 + +## P11.1 新权限展示 +- [ ] 在权限列表中展示 milestone 新权限 +- [ ] 在权限列表中展示 task 新权限 +- [ ] 在权限列表中展示 propose 新权限 + +## P11.2 Role 编辑 +- [ ] Role 编辑页支持勾选新权限 +- [ ] 默认角色展示正确 +- [ ] 保存后权限持久化正确 + +--- + +# P12 - CLI / 文档 / 开发者说明 + +## P12.1 CLI +- [ ] 更新 task status choices +- [ ] 更新 milestone status choices +- [ ] 如需要,新增 propose 子命令 + +## P12.2 README / docs +- [ ] 更新状态枚举说明 +- [ ] 更新按钮/动作说明 +- [ ] 新增 propose 流程说明 +- [ ] 新增权限说明 + +## P12.3 开发说明 +- [ ] 在 docs 中补一页“状态机总览” +- [ ] 在 docs 中补一页“权限总览” +- [ ] 在 docs 中补一页“propose -> feature story -> milestone”流程图/文字 + +--- + +# P13 - 自动化测试 + +## P13.1 Milestone 后端测试 +- [ ] 测 `open -> freeze` 成功路径 +- [ ] 测 `open -> freeze` 缺少 release task 失败 +- [ ] 测 `open -> freeze` 多个 release task 失败 +- [ ] 测 `freeze -> undergoing` 依赖未完成失败 +- [ ] 测 `freeze -> undergoing` 成功并写 `started_at` +- [ ] 测 release maintenance task 完成后 milestone 自动 completed +- [ ] 测 milestone close 权限 + +## P13.2 Task 后端测试 +- [ ] 测 `pending -> open` 依赖满足成功 +- [ ] 测 `pending -> open` 依赖不满足失败 +- [ ] 测 `open -> undergoing` assignee 为空失败 +- [ ] 测 `open -> undergoing` 非 assignee 操作失败 +- [ ] 测 `open -> undergoing` assignee 操作成功 +- [ ] 测 `undergoing -> completed` 无 comment 失败 +- [ ] 测 `undergoing -> completed` 成功 +- [ ] 测 `close task` 权限 +- [ ] 测 `reopen closed task` 权限 +- [ ] 测 `reopen completed task` 权限 + +## P13.3 Propose 后端测试 +- [ ] 测 propose code 按 project 独立递增 +- [ ] 测 `open -> accepted` +- [ ] 测 accept 时 milestone 非 open 失败 +- [ ] 测 accept 后自动生成 feature story task +- [ ] 测 accept 后自动写 `feat_task_id` +- [ ] 测 `open -> rejected` +- [ ] 测 `rejected -> open` +- [ ] 测 `feat_task_id` 无法手动修改 + +## P13.4 前端 / E2E +- [ ] 测 milestone 按钮显示规则 +- [ ] 测 milestone 按钮禁用提示 +- [ ] 测 task 按钮显示规则 +- [ ] 测 task finish 需要 comment +- [ ] 测 task reopen 后回到 open +- [ ] 测 propose create -> accept -> task generated 流程 +- [ ] 测 propose reject -> reopen 流程 +- [ ] 回归已有 wizard / role-editor / task / milestone 测试 + +--- + +# P14 - 收尾与上线前检查 + +## P14.1 数据校验 +- [ ] 用旧数据跑迁移 +- [ ] 抽查 milestone 状态迁移结果 +- [ ] 抽查 task 状态迁移结果 +- [ ] 抽查 `task_type = task` 数据迁移结果 +- [ ] 抽查 propose code 生成结果 + +## P14.2 手工验收 +- [ ] 手工走 milestone freeze/start/close +- [ ] 手工走 task open/start/finish/close/reopen +- [ ] 手工走 propose create/accept/reject/reopen +- [ ] 手工检查权限可见性 +- [ ] 手工检查禁用按钮与提示语 + +## P14.3 最终文档整理 +- [ ] 更新 `docs/milestone-propose-requirements.md` 与最终实现差异 +- [ ] 删除过时说明 +- [ ] 标出最终未做项 / 延后项 + +--- + +# 建议 PR 切分 + +## PR-1:枚举、模型、迁移骨架 +- [ ] milestone/task/propose 模型与 schema +- [ ] migration +- [ ] 不碰前端交互 + +## PR-2:权限骨架 +- [ ] 新权限注册 +- [ ] 角色配置与权限 UI 基础支持 + +## PR-3:milestone 状态机后端 +- [ ] freeze/start/close/completed 自动触发 + +## PR-4:task 状态机后端 +- [ ] open/start/finish/close/reopen +- [ ] assignee 与编辑控制 + +## PR-5:propose 后端 +- [ ] CRUD + accept/reject/reopen + feat_task_id + +## PR-6:task type 清理 +- [ ] 移除 `task_type = task` +- [ ] 数据修复 + +## PR-7:milestone 前端 +- [ ] status 展示 + 按钮 + 禁用提示 + +## PR-8:task 前端 +- [ ] status 展示 + 按钮 + finish/comment + reopen + +## PR-9:propose 前端 +- [ ] 列表/详情/创建/accept/reject/reopen + +## PR-10:测试与文档 +- [ ] E2E / 回归 / docs / CLI diff --git a/docs/milestone-propose-requirements.md b/docs/milestone-propose-requirements.md new file mode 100644 index 0000000..d2f8896 --- /dev/null +++ b/docs/milestone-propose-requirements.md @@ -0,0 +1,718 @@ +# HarborForge 需求草案:Milestone 状态机与 Propose 流程 + +> 状态:草案 +> 更新时间:2026-03-16 +> 目的:先把业务想法固定下来,后续再拆数据库、API、前端和测试实现。 + +--- + +## 1. 背景 + +当前希望把 milestone 的推进过程做成一个更严格的状态机,并把 feature story 的进入方式从“直接创建 task”改为“先提 propose,再 accept 进入 milestone”。 + +核心目标: +- milestone 有明确生命周期 +- feature 进入 milestone 要受控 +- release 节点要能约束 freeze / complete +- 依赖关系在开始前做严格检查 + +--- + +## 2. Milestone 字段调整 + +Milestone 的 `status` 使用新的枚举集合,替代当前代码库中的旧枚举。 + +### 2.1 目标枚举 + +- `open` +- `freeze` +- `undergoing` +- `completed` +- `closed` + +### 2.2 替代当前代码中的旧枚举 + +当前代码里 milestone status 旧枚举为: +- `open` +- `pending` +- `deferred` +- `progressing` +- `closed` + +后续应整体替换为新的 milestone 状态集合,不再使用: +- `pending` +- `deferred` +- `progressing` + +另增字段: +- `started_at` + - milestone 进入 `undergoing` 时自动记录开始时间 + +默认: +- 新建 milestone 时,`status = open` + +--- + +## 3. Milestone 状态定义 + +### 3.1 open + +含义: +- milestone 刚创建,仍可接收 feature 范围内的内容 + +页面按钮: +- `freeze` +- `close` + +约束: +- milestone 下的 `task / support / meeting` 在此阶段都只能处于锁定状态,不允许进入真正开始执行的状态 +- 当前可认为它们只能停留在 `pending` +- milestone 基本信息仍可编辑 + +--- + +### 3.2 freeze + +含义: +- milestone 进入冻结,需求范围不再继续扩张,准备开始执行 + +进入条件: +- 该 milestone **有且仅有一个** `maintenance task` +- 且这个 maintenance task 的 `subtype = release` +- 如果不满足,不能进入 `freeze` + +页面按钮: +- `start` +- `close` + +约束: +- freeze 之后不再允许做范围调整 +- 如果 freeze 之后才发现要调整范围,只能放到未来的 milestone,不提供 `unfreeze` +- milestone **不能再新增** `subtype = feature` 的 `story task` +- 已存在的 `feature story task` 不能再编辑核心字段 + - 例如:`title`、`description`、`owner` 等 +- 但允许继续做附属操作 + - 例如:新增 comment +- milestone 下的 `task / support / meeting` 仍保持锁定,不允许进入执行中状态 +- 当前可认为仍只能停留在 `pending` + +--- + +### 3.3 undergoing + +含义: +- milestone 正式开始执行 + +进入方式: +- 用户点击 `start` + +进入前检查: +- milestone 自身的前置依赖必须全部完成 +- 其前置依赖中涉及的 milestone / task / 其他依赖对象,如果尚未完成,则不能开始 + +进入后效果: +- milestone `status = undergoing` +- 自动记录 `started_at` +- detail 页面只显示:`close` +- milestone 下的 `task / support / meeting` 才允许进入可执行状态 +- 也就是说,只有在 milestone 为 `undergoing` 之后,这些对象才可以从 `pending` 往后推进 + +--- + +### 3.4 completed + +含义: +- milestone 正常完成 + +进入方式: +- 当该 milestone 下**唯一的** `release maintenance task` 完成后,milestone 自动进入 `completed` + +约束: +- `completed` 是终态 +- milestone 一旦进入 `completed`,状态不再允许变更 + +备注: +- “task 完成”所对应的 task 状态枚举,后续补充 + +--- + +### 3.5 closed + +含义: +- 废弃该 milestone,而不是正常完成 + +适用场景: +- milestone 不再继续推进 +- 即使已经开始,也可以选择废弃 + +页面入口: +- `open` / `freeze` / `undergoing` 状态下都可以通过 `close` 进入 `closed` + +进入 `closed` 后的处理原则: +- 如果 milestone 尚未开始,则直接废弃 +- 如果 milestone 已经开始: + - 已完成的 `feature task` 可以被废弃 + - 或者移交到其他 `open` 状态的 milestone +- 所有依赖该 milestone 的对象,都需要修改依赖关系,避免继续依赖一个已废弃 milestone + +约束: +- `closed` 视为终态 +- 当前不考虑 reopen 原 milestone +- milestone 页面不提供状态动作按钮 + +--- + +## 4. Milestone 页面按钮草案 + +所有按钮都按权限可见。 + +对于需要前置校验的按钮,推荐交互: +- 按钮可见但禁用 +- 并给出提示,说明当前为什么不能点击 + +### 4.1 各状态按钮 + +- `open` + - `freeze` + - `close` + +- `freeze` + - `start` + - `close` + +- `undergoing` + - `close` + +- `completed` + - 不显示状态动作按钮 + +- `closed` + - 不显示状态动作按钮 + +### 4.2 按钮触发条件 + +#### `freeze` +- 仅 `open` 状态显示 +- 点击前检查: + - 当前 milestone 下**有且仅有一个** `maintenance/release task` +- 不满足时不可点击,并提示原因 + +#### `start` +- 仅 `freeze` 状态显示 +- 点击前检查: + - 当前 milestone 的前置依赖全部完成 +- 满足后: + - milestone 进入 `undergoing` + - 自动记录 `started_at` + +#### `close` +- `open` / `freeze` / `undergoing` 状态显示 +- 需要有对应权限 +- 如果 milestone 已经是 `undergoing`,建议弹确认说明: + - 已完成的 feature task 需要废弃或移交 + - 依赖该 milestone 的对象需要调整依赖 + +### 4.3 Milestone 动作权限 + +将以下三类动作设计为**独立权限**,由 role 控制: +- `freeze milestone` +- `start milestone` +- `close milestone` + +规则: +- 拥有对应权限的 role 才可以执行对应动作 +- 三者互相独立,不合并 + +### 4.4 特别说明 + +- milestone 不提供 `unfreeze` +- 范围调整只能发生在 `freeze` 之前 +- 如果 freeze 之后才发现需要调整范围,只能放到未来的 milestone +- milestone 不提供手动 `complete` 按钮 + - `completed` 由唯一的 `release maintenance task` 完成后自动触发 + +--- + +## 5. 依赖规则 + +### 4.1 开始前依赖检查 + +以下对象在进入“开始/执行中”状态前,都要检查依赖是否已完成: +- milestone +- task +- support +- meeting +- 其他有依赖关系的对象 + +规则: +- 如果任一前置依赖未完成,则当前对象不能开始 +- 不需要在依赖完成时批量自动推进下游状态 +- 只在“尝试开始”这一刻做校验即可 + +### 4.2 milestone close 对依赖的影响 + +当 milestone 被 `closed`: +- 所有依赖它的对象都必须调整依赖关系 +- 调整后,这些对象未来在开始时仍然通过“开始前依赖检查”判断是否可开始 + +也就是说: +- **不做统一的自动状态更新** +- **只要求依赖配置保持合法** + +--- + +## 5. Task 创建限制 + +以下两类 task **不能**通过通用的 `create task` 页面直接创建: + +- `feature story task` +- `release maintenance task` + +设计意图: +- `feature story task` 应来自 `propose -> accept` +- `release maintenance task` 应通过更受控的 milestone/release 流程创建 + +--- + +## 6. Propose 新模型 + +新增 `propose` 表。 + +### 6.1 propose 与 project 的关系 + +- `propose` 通过外键连接到 `project` + +### 6.2 propose code + +格式: + +```text +{proj_code}:P{i:05x} +``` + +说明: +- `i` 由每个 `project` 独立维护递增序号 +- 即每个 project 都有自己的 propose 计数器 + +### 6.3 propose 状态枚举 + +当前采用以下状态: +- `open` +- `accepted` +- `rejected` + +明确: +- propose **不需要** `closed` + +### 6.4 propose 基础字段(当前已明确部分) + +至少需要: +- `project_id` +- `propose_code` +- `title` +- `description` +- `status` +- `created_by_id` +- `created_at` +- `updated_at` + +### 6.5 feat_task_id 字段 + +新增字段: +- `feat_task_id` +- 类型:`string` +- 可选 + +规则: +- 在 accept 生成 `feature story task` 后自动填写 +- 不允许手动修改 +- 前端页面应显示该字段 + +--- + +## 7. Propose 页面草案 + +### 7.1 页面按钮 + +前端页面按钮规则采用当前草案: + +- `open` + - `accept` + - `reject` + +- `accepted` + - 不显示状态动作按钮 + - 可显示跳转入口:`View Generated Task` + +- `rejected` + - `reopen` + +所有按钮都按权限可见。 + +### 7.2 页面显示字段 + +前端 detail 页面至少应显示: +- `propose_code` +- `title` +- `description` +- `status` +- `feat_task_id` +- `created_by_id` +- `created_at` +- `updated_at` + +--- + +## 8. Propose 状态流转与动作规则 + +### 8.1 允许的状态流转 + +- `open -> accepted` +- `open -> rejected` +- `rejected -> open` + +当前不考虑: +- `accepted -> open` +- `accepted -> rejected` +- 任何 `closed` 相关流转(因为 propose 不设 `closed`) + +### 8.2 accept + +前置条件: +- propose 当前必须是 `open` +- 操作者有 `accept propose` 权限 +- 用户必须从下拉框中选择一个目标 milestone +- 目标 milestone 必须属于同一个 project +- 目标 milestone 必须是 `open` + +accept 后效果: +- propose.status = `accepted` +- 自动在所选 milestone 下创建一个 `feature story task` +- 新 task 的创建者与 propose 创建者保持一致 +- 新 task 默认状态建议为 `pending` +- 自动填写 `feat_task_id` + +字段继承: +- `title` +- `description` + +### 8.3 reject + +前置条件: +- propose 当前必须是 `open` +- 操作者有 `reject propose` 权限 + +reject 后效果: +- propose.status = `rejected` +- 建议要求填写 reject comment / reason,便于追踪原因 + +### 8.4 reopen + +前置条件: +- propose 当前必须是 `rejected` +- 操作者有 `reopen rejected propose` 权限 + +reopen 后效果: +- 不创建新 propose +- 复用当前 propose +- propose.status 回到 `open` +- 系统需要保留一条 reopen 记录 + +--- + +## 9. Propose 权限与编辑规则 + +### 9.1 独立权限 + +建议将以下动作设计为独立权限: +- `accept propose` +- `reject propose` +- `reopen rejected propose` + +### 9.2 编辑规则 + +- `open` + - creator 可编辑 `title`、`description` + - admin 可编辑 + - 有特定管理权限的 role 可编辑 +- `accepted` + - 主体不可编辑 +- `rejected` + - 主体不可编辑 + +备注: +- 主体不可编辑不代表不能追加 comment / activity 记录 + +--- + +## 10. 当前推荐的业务流程 + +### 10.1 feature 进入 milestone + +推荐流程: +1. 用户先在 project 下创建 `propose` +2. 负责人在 propose detail 页面审阅 +3. 若接受,则选择目标 milestone 并 `accept` +4. 系统自动生成 `feature story task` +5. milestone 处于 `open` 时可继续接纳 feature story +6. milestone 进入 `freeze` 后,不再接受新的 feature story +7. milestone 满足条件后 `start`,进入 `undergoing` +8. milestone 下唯一 `release maintenance task` 完成后,milestone 自动 `completed` + +### 10.2 废弃 milestone + +1. 用户点击 `close` +2. milestone 进入 `closed` +3. 处理已完成 feature task: + - 废弃 + - 或移交到其他 `open` milestone +4. 修正所有依赖该 milestone 的对象依赖配置 + +--- + +## 11. Task 状态枚举调整 + +Task 的 `status` 也采用新的枚举集合。 + +### 11.1 目标枚举 + +- `open` +- `pending` +- `undergoing` +- `completed` +- `closed` + +### 11.2 替代当前代码中的旧枚举 + +当前代码里 task status 旧枚举为: +- `open` +- `pending` +- `progressing` +- `closed` + +后续应整体替换为新的 task 状态集合,不再使用: +- `progressing` + +并新增: +- `undergoing` +- `completed` + +### 11.3 Task 状态语义 + +- `pending` + - 还不能开始,通常是被 milestone 状态或依赖锁住 +- `open` + - 可以开始,但尚未真正开工 +- `undergoing` + - 正在处理 +- `completed` + - 正常完成 +- `closed` + - 废弃 / 取消,不是正常完成 + +### 11.4 当前认可的 Task 基础状态流转 + +允许的基础流转: +- `pending -> open` +- `open -> undergoing` +- `undergoing -> completed` +- `pending -> closed` +- `open -> closed` +- `undergoing -> closed` + +### 11.5 各状态流转条件 + +#### `pending -> open` +条件: +- 所有前置依赖都已满足 + +#### `open -> undergoing` +条件: +- `assignee` 必须非空 +- 且只有当前 assignee 可以执行这个开始动作 + +#### `undergoing -> completed` +条件: +- 不额外要求前置条件 +- 但 assignee 需要留下 comment + +### 11.6 Reopen 方向 + +补充决定: +- reopen 时**不创建新 task** +- 直接复用当前 task +- 但系统需要保留一条 reopen 记录 + +当前已确认: +- reopen 允许从 `closed` 与 `completed` 触发 +- reopen 后统一回到 `open` +- reopen 记录采用 comment、activity log 还是专门字段/表,后续再定 + +当前理解: +- `completed` 与 `closed` 原本被视为终态 +- 但 task 将引入受控的 reopen 机制,使其可以返回 `open` + +### 11.7 Task 状态动作权限 + +将以下三类动作设计为**独立权限**,由 role 控制: +- `close task` +- `reopen closed task` +- `reopen completed task` + +规则: +- 拥有对应权限的 role 才可以执行对应动作 +- `reopen closed task` 与 `reopen completed task` 分开控制,不合并为一个权限 + +补充: +- `undergoing -> completed` 不走 role 权限控制 +- `complete` 只有当前 assignee 可以执行 + +### 11.8 Task 页面按钮可见性 + +所有按钮都按权限可见。 + +当前确认的按钮规则: + +- 除 `closed` 与 `completed` 外,task 页面始终显示 `close` 按钮 + - 也就是: + - `pending` 有 `close` + - `open` 有 `close` + - `undergoing` 有 `close` +- `closed` 与 `completed` 状态显示 `reopen` 按钮 +- `pending` 状态显示 `open` 按钮 +- `open` 状态显示 `start` 按钮 +- `undergoing` 状态显示 `finish` 按钮 + +可理解为: +- `pending`:`open` + `close` +- `open`:`start` + `close` +- `undergoing`:`finish` + `close` +- `completed`:`reopen` +- `closed`:`reopen` + +其中: +- `finish` 对应 `undergoing -> completed` +- `start` 对应 `open -> undergoing` +- `open` 按钮对应 `pending -> open` + +### 11.9 与 Milestone 状态的联动约束 + +- 当 milestone 为 `open` / `freeze` 时: + - task 原则上只能处于 `pending` 或 `closed` + - 不应进入 `open / undergoing / completed` +- 当 milestone 为 `undergoing` 时: + - task 才允许从 `pending` 往执行状态推进 +- 当 milestone 为 `completed` / `closed` 时: + - milestone 下 task 原则上不再继续推进 + +### 11.10 新建 Task 的默认状态 + +当前倾向: +- 新建 task 默认 `pending` + +原因: +- 与 milestone `open / freeze` 阶段的“锁定”语义一致 +- 避免 task 一创建就被误认为可执行 + +### 11.11 Assignee 字段与编辑权限规则 + +Task 需要一个字段: +- `assignee` +- 类型:`string` +- 可选 +- 含义:用户 id + +当前业务规则: + +#### open 状态 +- 当 `assignee = null` 时: + - 任何人都可以编辑 +- 当 `assignee != null` 时: + - 仅当前 assignee 和 admin 可以编辑 + +#### undergoing 以及之后的状态 +- `undergoing` +- `completed` +- `closed` + +以上状态一律不可编辑。 + +备注: +- 这里的“不能编辑”指 task 主体内容不可编辑 +- `undergoing -> completed` 已明确要求 assignee 留下 comment,因此 comment / 记录性动作不等同于主体编辑 +- 其他 comment 等附属动作的权限边界,后续可继续细化 + +--- + +## 12. Task 类型清理 + +当前又发现一个需要清理的点: +- 现在创建 task 时,存在一个 `task_type = task` 的类型 +- 且它当前只有一个 subtype:`defect` + +这在语义上比较别扭,也和其它类型存在重叠/混淆。 + +### 12.1 当前决定 + +后续应移除这个类型: +- 移除 `task_type = task` + +### 12.2 当前代码确认结果 + +这个问题在当前代码中已经确认存在,而且前后端都写死了: + +前端: +- `HarborForge.Frontend/src/components/CreateTaskModal.tsx` +- `HarborForge.Frontend/src/pages/CreateTaskPage.tsx` +- 两处都定义了: + - `task` + - 其 subtype 只有:`defect` +- 当前创建表单默认类型也是 `task` + +后端: +- `HarborForge.Backend/app/api/routers/tasks.py` +- `TASK_SUBTYPE_MAP` 中明确存在: + - `'task': {'defect'}` + +补充: +- 前端类型定义里也仍包含 `task` +- 后端 schema / enum 里也仍包含 `task` + +### 12.3 本轮只记录,不改代码 + +当前先把它记录为需求与清理项: +- 暂不实现 +- 暂不修改代码 +- 后续统一与 task type / subtype 体系一起调整 + +--- + +## 13. 待补充事项 + +这些点后续需要进一步定死: + +### 13.1 feature task 在 freeze 后的可编辑字段白名单/黑名单 +- 当前只明确:核心字段不可编辑,comment 仍可新增 +- 后续要更具体列出:哪些能改,哪些不能改 + +### 13.2 release maintenance task 的创建入口 +- 当前只明确它不能走通用 create task +- 但具体从哪里创建、谁能创建、何时创建,还需要补 + +### 13.3 close 时的迁移细节 +- 已完成 feature task 移交到其他 `open milestone` 时,是否保留原历史链接 +- task code 是否需要变化 +- 依赖链迁移是否允许批量操作 + +--- + +## 14. 一句话总结 + +这套方案的本质是: + +- 用 `propose` 控制 feature 进入 milestone 的入口 +- 用 `milestone status` 控制范围冻结与执行节奏 +- 用 `release maintenance task` 作为 milestone 完成的关键闸门 +- 用“开始前依赖检查”替代大规模自动状态联动 + +这样业务会更可控,也更容易避免流程混乱。