feat(slidev): update 3 4

This commit is contained in:
smart
2026-07-03 00:26:47 +08:00
parent 2fd8fc1f66
commit 6baa4cd10e
47 changed files with 1185 additions and 382 deletions
@@ -1,16 +1,19 @@
# 工单 RBAC 权限矩阵
# <模块名称> RBAC 权限矩阵
| 功能点 | 管理员 | 工单负责人 | 研发处理人 | 测试验证人 | 只读观察者 | 权限字符建议 | 备注 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 查看列表 | 允许 | 条件允许 | 条件允许 | 条件允许 | 条件允许 | work:order:list | 按数据范围 |
| 查看详情 | 允许 | 条件允许 | 条件允许 | 条件允许 | 条件允许 | work:order:query | 仅可见范围 |
| 新建工单 | 允许 | 允许 | 允许 | 允许 | 拒绝 | work:order:add | |
| 编辑工单 | 允许 | 条件允许 | 条件允许 | 拒绝 | 拒绝 | work:order:edit | 状态限制 |
| 指派负责人 | 允许 | 条件允许 | 拒绝 | 拒绝 | 拒绝 | work:order:assign | |
| 状态流转 | 允许 | 条件允许 | 条件允许 | 条件允许 | 拒绝 | work:order:transition | 按状态 |
| 关闭工单 | 允许 | 条件允许 | 拒绝 | 条件允许 | 拒绝 | work:order:close | 测试验证后 |
| 删除工单 | 允许 | 拒绝 | 拒绝 | 拒绝 | 拒绝 | work:order:delete | 高危操作 |
| 查看审计日志 | 允许 | 条件允许 | 条件允许 | 条件允许 | 拒绝 | work:order:audit | |
| 查看看板 | 允许 | 允许 | 条件允许 | 条件允许 | 条件允许 | work:order:dashboard | 按数据范围 |
| 导出数据 | 允许 | 条件允许 | 拒绝 | 拒绝 | 拒绝 | work:order:export | |
## 使用说明
将列名中的 `<角色A>`、`<角色B>`、`<角色C>` 替换为目标项目真实角色。权限字符只能写成建议,除非源码、菜单配置或后端注解已经证明。
| 功能点 | <管理员角色> | <业务负责人角色> | <处理人角色> | <审核人角色> | <只读角色> | 权限字符建议 | 数据范围 | 来源证据 | 备注 |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 查看列表 | 允许 | 条件允许 | 条件允许 | 条件允许 | 条件允许 | `<module>:<entity>:list` | 按授权范围 | | |
| 查看详情 | 允许 | 条件允许 | 条件允许 | 条件允许 | 条件允许 | `<module>:<entity>:query` | 仅可见数据 | | |
| 新建记录 | 允许 | 允许 | 条件允许 | 条件允许 | 拒绝 | `<module>:<entity>:add` | 通常无数据范围 | | |
| 编辑记录 | 允许 | 条件允许 | 条件允许 | 拒绝 | 拒绝 | `<module>:<entity>:edit` | 创建人/负责人/授权范围 | | 需结合状态 |
| 分配/转交 | 允许 | 条件允许 | 拒绝 | 拒绝 | 拒绝 | `<module>:<entity>:assign` | 管理范围 | | 高风险操作 |
| 状态流转 | 允许 | 条件允许 | 条件允许 | 条件允许 | 拒绝 | `<module>:<entity>:transition` | 当前数据范围 | | 需结合状态表 |
| 关闭/归档 | 允许 | 条件允许 | 拒绝 | 条件允许 | 拒绝 | `<module>:<entity>:close` | 当前数据范围 | | 终态动作 |
| 删除记录 | 条件允许 | 拒绝 | 拒绝 | 拒绝 | 拒绝 | `<module>:<entity>:delete` | 管理范围 | | 高危操作 |
| 查看审计日志 | 允许 | 条件允许 | 条件允许 | 条件允许 | 拒绝 | `<module>:<entity>:audit` | 当前数据范围 | | |
| 查看统计看板 | 允许 | 条件允许 | 条件允许 | 条件允许 | 条件允许 | `<module>:<entity>:dashboard` | 按角色过滤 | | |
| 导出数据 | 条件允许 | 条件允许 | 拒绝 | 拒绝 | 拒绝 | `<module>:<entity>:export` | 管理范围或授权范围 | | 需脱敏 |
@@ -1,18 +1,20 @@
# RBAC 权限矩阵生成 Prompt
```text
请基于 JeecgBoot 工单模块设计 RBAC 权限矩阵。
请基于目标业务模块设计 RBAC 权限矩阵。
输入材料:
- 工单模块需求:<粘贴需求>
- 角色列表:管理员、工单负责人、研发处理人、测试验证人、只读观察者
- 功能点:查看列表、查看详情、新建、编辑、指派、状态流转、关闭、删除、查看审计、查看看板、导出
- 项目依据:<粘贴权限、路由、菜单相关路径>
- 模块名称:<填写>
- 业务对象:<填写,例如订单、客户、申请、任务>
- 需求说明或用户故事:<粘贴>
- 角色列表:<粘贴目标项目真实角色>
- 功能点:<粘贴页面、按钮、接口、数据动作>
- 项目依据:<粘贴权限、路由、菜单、接口、后端校验相关路径>
输出要求:
1. 先列事实依据:JeecgBoot 权限、菜单、动态路由相关路径。
1. 先列事实依据:权限、菜单、动态路由、接口、后端校验相关路径。
2. 输出 RBAC 权限矩阵:角色 x 功能点,允许、拒绝、条件允许。
3. 输出权限字符建议,必须标注为建议。
3. 输出权限字符建议,并标注哪些是事实、哪些是建议。
4. 输出页面级、按钮级、接口级、数据范围四层控制。
5. 输出不确定点:哪些权限必须业务方确认。
6. 输出验收方式:如何证明权限确实生效。
@@ -21,5 +23,5 @@
- 不要直接改代码。
- 不要把前端按钮隐藏当作安全边界。
- 所有结论必须区分事实、推断、建议。
- 不要把示例模块、示例角色或示例权限字符当成目标项目事实。
```
@@ -1,18 +1,19 @@
# 工单审计日志 SOP
# <模块名称> 审计日志 SOP
## 记录原则
所有会改变工单关键业务状态、负责人、可见性或结论的动作都必须记录审计日志。
所有会改变目标业务对象关键状态、负责人/处理人、可见性、金额、结论或对外结果的动作都必须记录审计日志。
## 审计字段
| 字段 | 含义 | 示例 |
| --- | --- | --- |
| ticket_id | 工单 ID | WO-001 |
| target_type | 业务对象类型 | order / customer / request |
| target_id | 业务对象 ID | 100001 |
| actor_id | 操作人 ID | 1001 |
| actor_name | 操作人名称 | zhangsan |
| action | 操作类型 | ASSIGN |
| field_name | 变更字段 | assignee |
| action | 操作类型 | ASSIGN / APPROVE / CLOSE |
| field_name | 变更字段 | status / owner_id / amount |
| old_value | 旧值 | lisi |
| new_value | 新值 | wangwu |
| source | 来源 | web |
@@ -23,12 +24,13 @@
| 动作 | 写入时机 | oldValue | newValue | 验收 |
| --- | --- | --- | --- | --- |
| 创建工单 | 创建成功后 | 空 | 工单摘要 | 审计列表可见 |
| 创建记录 | 创建成功后 | 空 | 业务摘要 | 审计列表可见 |
| 编辑字段 | 保存成功后 | 修改前字段 | 修改后字段 | 字段前后值正确 |
| 指派负责人 | 指派成功后 | 原负责人 | 新负责人 | 审计记录负责人变化 |
| 分配/转交 | 分配成功后 | 原处理人 | 新处理人 | 审计记录处理人变化 |
| 状态流转 | 状态变更成功后 | 原状态 | 新状态 | 审计记录状态变化 |
| 评论新增 | 评论保存后 | 空 | 评论摘要 | 审计记录评论动作 |
| 关闭工单 | 关闭成功后 | 原状态 | 已关闭 | 审计记录关闭时间 |
| 结论/审核 | 结论保存成功后 | 原结论 | 新结论 | 审计记录结论变化 |
| 评论/备注新增 | 保存成功后 | 空 | 摘要或脱敏内容 | 审计记录评论动作 |
| 关闭/归档 | 操作成功后 | 原状态 | 目标终态 | 审计记录关闭或归档时间 |
## 验收方式
@@ -37,4 +39,3 @@
3. 敏感字段已脱敏。
4. 操作失败不写成功审计。
5. 多步写入失败时审计和业务数据保持一致。
@@ -2,12 +2,11 @@
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 创建记录 | 创建工单后能查询审计记录 | |
| 创建记录 | 创建业务记录后能查询审计记录 | |
| 字段变更 | 编辑关键字段记录 oldValue/newValue | |
| 指派记录 | 指派负责人记录原负责人和新负责人 | |
| 分配记录 | 分配或转交记录原处理人和新处理人 | |
| 状态记录 | 状态流转记录原状态和新状态 | |
| 操作人 | actor_id 和 actor_name 可追溯 | |
| 来源 | source 或 request_id 可追溯 | |
| 脱敏 | 敏感字段不明文进入审计 | |
| 失败处理 | 操作失败不写成功审计 | |
@@ -3,11 +3,10 @@
| 异常态 | 必须验证 | 结果 |
| --- | --- | --- |
| 空列表 | 页面显示空状态,不报错 | |
| 详情不存在 | 提示工单不存在,页面不崩溃 | |
| 详情不存在 | 提示记录不存在,页面不崩溃 | |
| 权限不足 | 前端提示或隐藏,后端拒绝 | |
| 非法状态 | 返回非法流转,状态不变 | |
| 重复提交 | 只成功一次,只写一条有效审计 | |
| 参数缺失 | 返回参数错误,业务数据不变 | |
| 并发修改 | 拒绝覆盖或提示刷新 | |
| 看板无数据 | 图表显示空态,不显示假数据 | |
@@ -1,13 +1,13 @@
# 工单异常态清单
| 场景 | 触发条件 | 前端表现 | 后端响应 | 验收 |
| --- | --- | --- | --- | --- |
| 权限不足 | 无权限操作 | 按钮不可见或提示无权限 | 403 或业务拒绝 | 不完成操作 |
| 工单不存在 | 传入错误 ID | 显示不存在 | 404 或业务错误 | 页面不崩溃 |
| 非法状态流转 | closed -> processing | 提示非法流转 | 状态不变 | 数据状态不变 |
| 重复提交 | 连续点击提交 | 按钮 loading/禁用 | 只处理一次 | 只生成一条有效审计 |
| 参数缺失 | 驳回缺少原因 | 表单校验提示 | 参数错误 | 不改变状态 |
| 并发修改 | 数据版本过期 | 提示刷新 | 拒绝覆盖 | 旧数据不覆盖新数据 |
| 看板无数据 | 无匹配记录 | 空状态 | 返回空集合 | 页面不报错 |
| 审计日志为空 | 无审计记录 | 空状态 | 返回空集合 | 页面不报错 |
# <模块名称> 异常态清单
| 场景 | 触发条件 | 前端表现 | 后端响应 | 验收 | 来源证据 |
| --- | --- | --- | --- | --- | --- |
| 权限不足 | 无权限操作敏感功能 | 按钮不可见或提示无权限 | 403 或业务拒绝 | 不完成操作 | |
| 记录不存在 | 传入错误 ID | 显示不存在 | 404 或业务错误 | 页面不崩溃 | |
| 非法状态流转 | `<状态A> -> <状态C>` 不在状态表 | 提示非法流转 | 状态不变 | 数据状态不变 | |
| 重复提交 | 连续点击提交或重复请求 | 按钮 loading/禁用 | 只处理一次 | 只生成一条有效审计 | |
| 参数缺失 | 必填原因、金额、范围或结论缺失 | 表单校验提示 | 参数错误 | 不改变业务数据 | |
| 并发修改 | 数据版本过期 | 提示刷新 | 拒绝覆盖或合并冲突 | 旧数据不覆盖新数据 | |
| 统计无数据 | 无匹配记录 | 空状态 | 返回 0 或空集合 | 页面不显示假数据 | |
| 审计日志为空 | 无审计记录 | 空状态 | 返回空集合 | 页面不报错 | |
| 下游服务失败 | 外部接口超时或失败 | 错误提示且可重试 | 失败响应可追踪 | 不写成功审计 | |
@@ -1,24 +1,31 @@
# 异常态补齐 Prompt
```text
请基于 JeecgBoot 工单模块,列出状态流转、指派、删除、查询详情、审计日志、看板相关的异常态和防御性逻辑。
请基于目标业务模块,列出状态流转、分配/转交、删除/作废、查询详情、审计日志、统计看板相关的异常态和防御性逻辑。
输入材料:
- 模块名称:<填写>
- 业务对象:<填写>
- 角色与权限:<粘贴>
- 状态流转表:<粘贴>
- 接口契约或页面说明:<粘贴>
输出表格:
场景 | 触发条件 | 前端表现 | 后端响应 | 用户提示 | 测试点 | 验收
必须覆盖:
- 权限不足
- 工单不存在
- 记录不存在
- 非法状态流转
- 重复提交
- 参数缺失
- 并发修改
- 看板无数据
- 统计无数据
- 审计日志为空
- 下游服务失败
约束:
- 先列异常态,不要直接改代码。
- 给出每个异常态的验收步骤。
- 标注事实、推断、建议和不确定点。
```
@@ -2,11 +2,10 @@
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 菜单权限 | 工单菜单只对授权角色可见 | |
| 按钮权限 | 新增、编辑、删除、指派、状态按钮按权限显示 | |
| 菜单权限 | 目标模块菜单只对授权角色可见 | |
| 按钮权限 | 新增、编辑、删除、分配、状态按钮按权限显示 | |
| 接口权限 | 无权限账号直接请求敏感接口失败 | |
| 数据权限 | 普通用户只能看到授权范围内工单 | |
| 数据权限 | 普通用户只能看到授权范围内数据 | |
| 权限字符 | 前端按钮、菜单配置、后端接口命名一致 | |
| 审计 | 权限敏感操作成功后有审计记录 | |
| 反例验证 | 无权限账号不能绕过前端执行删除/指派 | |
| 反例验证 | 无权限账号不能绕过前端执行删除/分配 | |
@@ -1,12 +1,11 @@
# 工单权限控制方案
# <模块名称> 权限控制方案
| 操作 | 前端入口 | 权限字符 | 后端接口 | 后端校验 | 数据权限 | 验收 |
| --- | --- | --- | --- | --- | --- | --- |
| 新增工单 | 新增按钮 | work:order:add | /workOrder/add | 权限注解 | 无 | 无权限账号看不到按钮且直接请求失败 |
| 编辑工单 | 编辑按钮 | work:order:edit | /workOrder/edit | 权限注解 + 状态校验 | 负责人/创建人范围 | 越权账号返回 403 或业务拒绝 |
| 指派负责人 | 指派按钮 | work:order:assign | /workOrder/assign | 权限注解 | 管理员/负责人范围 | 普通研发无法指派他人 |
| 状态流转 | 状态按钮 | work:order:transition | /workOrder/transition | 权限注解 + 状态机校验 | 相关工单范围 | 非法流转失败 |
| 删除工单 | 删除按钮 | work:order:delete | /workOrder/delete | 权限注解 | 管理员范围 | 非管理员返回 403 |
| 查看审计 | 审计页签 | work:order:audit | /workOrder/audit/list | 权限注解 | 相关工单范围 | 无权限不可见且接口拒绝 |
| 看板 | 看板菜单 | work:order:dashboard | /workOrder/dashboard | 权限注解 | 数据范围过滤 | 不同角色数据范围不同 |
| 新增记录 | 新增按钮 | `<module>:<entity>:add` | `<POST /api/entity>` | 权限注解或统一鉴权 | 通常无数据范围 | 无权限账号看不到按钮且直接请求失败 |
| 编辑记录 | 编辑按钮 | `<module>:<entity>:edit` | `<PUT /api/entity/{id}>` | 权限校验 + 状态校验 | 创建人/负责人/授权范围 | 越权账号返回 403 或业务拒绝 |
| 分配/转交 | 分配按钮 | `<module>:<entity>:assign` | `<POST /api/entity/{id}/assign>` | 权限校验 | 管理范围或当前授权范围 | 未授权角色无法分配给他人 |
| 状态流转 | 状态按钮 | `<module>:<entity>:transition` | `<POST /api/entity/{id}/transition>` | 权限校验 + 状态机校验 | 当前授权范围 | 非法流转失败且状态不变 |
| 删除/作废 | 删除或作废按钮 | `<module>:<entity>:delete` | `<DELETE /api/entity/{id}>` | 权限校验 + 风险校验 | 管理范围 | 非授权角色返回 403 或业务拒绝 |
| 查看审计 | 审计页签 | `<module>:<entity>:audit` | `<GET /api/entity/{id}/audit>` | 权限校验 | 当前授权范围 | 无权限不可见且接口拒绝 |
| 统计看板 | 看板菜单 | `<module>:<entity>:dashboard` | `<GET /api/entity/dashboard>` | 权限校验 | 数据范围过滤 | 不同角色返回授权范围内统计 |
@@ -1,9 +1,11 @@
# 权限控制方案生成 Prompt
```text
请基于工单模块 RBAC 权限矩阵,生成 JeecgBoot 权限控制方案。
请基于目标业务模块 RBAC 权限矩阵,生成权限控制方案。
输入材料:
- 模块名称:<填写>
- 业务对象:<填写>
- RBAC 权限矩阵:<粘贴>
- 接口契约表:<粘贴>
- 当前项目权限相关路径:<粘贴>
@@ -20,5 +22,5 @@
- 不要直接改代码。
- 后端接口权限必须单独说明。
- 每个敏感操作必须有验收方式。
- 权限字符、接口路径和角色名称必须来自输入材料;没有依据时标注为建议。
```
@@ -6,7 +6,6 @@
| 合法流转 | 状态流转表中的合法路径可执行 | |
| 非法流转 | 非法路径被拒绝且状态不变 | |
| 角色限制 | 不同角色只能执行被授权动作 | |
| 前置条件 | 驳回原因、关闭条件、负责人等校验完整 | |
| 前置条件 | 驳回原因、关闭条件、处理人、审批意见等校验完整 | |
| 审计日志 | 每次成功流转写审计 | |
| 重复提交 | 重复点击不会产生重复有效流转 | |
@@ -1,22 +1,21 @@
# 工单状态流转表
# <模块名称> 状态流转表
| 当前状态 | 动作 | 目标状态 | 允许角色 | 前置条件 | 后置动作 | 验收 |
| --- | --- | --- | --- | --- | --- | --- |
| 新建 | 指派 | 新建 | 管理员/负责人 | 工单未关闭 | 写审计日志 | 指派人变化且状态不变 |
| 新建 | 开始处理 | 处理中 | 研发处理人/负责人 | 已指派处理人 | 写审计日志 | 状态变为处理中 |
| 处理中 | 提交验证 | 待验证 | 研发处理人/负责人 | 处理说明不为空 | 写审计日志 | 状态变为待验证 |
| 待验证 | 验证通过 | 已关闭 | 测试验证人/负责人 | 验证结果通过 | 写审计日志和关闭时间 | 状态变为已关闭 |
| 待验证 | 验证驳回 | 已驳回 | 测试验证人/负责人 | 驳回原因不为空 | 写审计日志 | 状态变为已驳回 |
| 已驳回 | 重新处理 | 处理中 | 研发处理人/负责人 | 驳回原因已读 | 写审计日志 | 状态变为处理中 |
| 已关闭 | 重新打开 | 新建 | 管理员 | 重开原因不为空 | 写审计日志 | 状态变为新建 |
| 当前状态 | 动作 | 目标状态 | 允许角色 | 前置条件 | 后置动作 | 验收 | 来源证据 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 草稿/新建 | 提交 | 待处理/待审核 | 创建人/提交人 | 必填字段完整 | 写审计日志 | 状态变为目标状态 | |
| 待处理/待审核 | 分配/领取 | 处理中 | 管理员/负责人/处理人 | 处理人存在且有权限 | 写审计日志 | 处理人变化且状态正确 | |
| 处理中 | 提交结果 | 待确认/待复核 | 处理人/负责人 | 处理说明或结果不为空 | 写审计日志 | 状态变为待确认 | |
| 待确认/待复核 | 通过 | 已完成/已关闭 | 审核人/负责人 | 审核结论通过 | 写审计日志和完成时间 | 状态进入终态 | |
| 待确认/待复核 | 驳回 | 已驳回/待修改 | 审核人/负责人 | 驳回原因不为空 | 写审计日志 | 状态变为驳回态 | |
| 已驳回/待修改 | 重新提交 | 待处理/待审核 | 创建人/处理人 | 修改说明不为空 | 写审计日志 | 回到处理链路 | |
| 已完成/已关闭 | 重新打开 | 草稿/新建/处理中 | 管理员/负责人 | 重开原因不为空 | 写审计日志 | 状态按规则回退 | |
## 非法流转清单
| 当前状态 | 尝试动作 | 问题 | 系统响应 | 验收 |
| --- | --- | --- | --- | --- |
| 已关闭 | 开始处理 | 关闭后不能直接处理 | 返回非法流转 | 状态不变 |
| 新建 | 验证通过 | 未处理不能验证 | 返回非法流转 | 状态不变 |
| 待验证 | 提交验证 | 重复提交验证 | 返回非法流转 | 只保留原状态 |
| 处理中 | 验证通过 | 未进入待验证 | 返回非法流转 | 状态不变 |
| 已驳回 | 关闭 | 驳回后不能直接关闭 | 返回非法流转 | 状态不变 |
| 已完成/已关闭 | 直接处理 | 终态不能直接进入处理中 | 返回非法流转 | 状态不变 |
| 草稿/新建 | 直接通过 | 未提交或未处理不能通过 | 返回非法流转 | 状态不变 |
| 待确认/待复核 | 重复提交结果 | 重复动作不应再次生效 | 返回非法流转或幂等结果 | 只保留原状态 |
| 处理中 | 直接通过 | 未进入确认/复核节点 | 返回非法流转 | 状态不变 |
| 已驳回/待修改 | 直接关闭 | 驳回后不能绕过修改或确认 | 返回非法流转 | 状态不变 |
@@ -1,23 +1,22 @@
# 状态流转设计 Prompt
```text
请为 JeecgBoot 工单模块设计状态流转规则。
请为目标业务模块设计状态流转规则。
状态候选:
- 新建
输入材料:
- 模块名称:<填写>
- 业务对象:<填写>
- 角色列表:<粘贴>
- 当前需求或已有状态枚举:<粘贴>
- 关键动作:<粘贴>
可参考但必须按项目替换的状态类型:
- 草稿/新建
- 待处理/待审核
- 处理中
- 待验证
- 已关闭
- 已驳回
动作候选:
- 指派
- 开始处理
- 提交验证
- 验证通过
- 验证驳回
- 关闭
- 重新打开
- 待确认/待复核
- 已完成/已关闭
- 已驳回/待修改
输出要求:
1. 状态流转表:当前状态、动作、目标状态、允许角色、前置条件、后置动作、验收。
@@ -28,7 +27,7 @@
约束:
- 先出状态表,不要直接写 if else。
- 已关闭、越权、缺少说明、重复提交必须覆盖。
- 终态、越权、缺少说明、重复提交必须覆盖。
- 每条规则必须有验收方式。
- 没有需求依据的状态或动作必须标注为建议或待确认。
```
@@ -1,13 +1,12 @@
# 工单看板指标字典
# <模块名称> 看板指标字典
| 指标 | 含义 | 口径 | 数据来源 | 展示方式 | 权限 | 验收 |
| --- | --- | --- | --- | --- | --- | --- |
| 工单总数 | 当前筛选范围内工单数量 | count(ticket_id) | 工单表 | 指标卡 | 看板权限 | 与列表同条件数量一致 |
| 未关闭工单数 | 状态不是已关闭的数量 | status != closed | 工单表 | 指标卡 | 看板权限 | 过滤状态验证 |
| 平均关闭周期 | 从创建到关闭平均耗时 | avg(close_time - create_time) | 工单表 | 指标卡/趋势 | 看板权限 | 抽样计算一致 |
| 按优先级分布 | 高中低优先级数量 | group by priority | 工单表/字典 | 饼图/柱状图 | 看板权限 | 分组总和等于总数 |
| 按类型分布 | 缺陷/需求/支持数量 | group by type | 工单表/字典 | 柱状图 | 看板权限 | 分组总和等于总数 |
| 缺陷趋势 | 按日期统计缺陷工单 | type=bug group by date | 工单表 | 折线图 | 看板权限 | 日期范围正确 |
| 逾期工单数 | 超过 SLA 未关闭数量 | due_time < now and status != closed | 工单表 | 指标卡 | 看板权限 | SLA 口径明确 |
| 人员处理负载 | 按负责人统计未关闭数量 | group by assignee | 工单表 | 排行榜 | 管理员/负责人 | 数据范围正确 |
| 记录总数 | 当前筛选范围内业务记录数量 | count(target_id) | 主业务表 | 指标卡 | 看板权限 | 与列表同条件数量一致 |
| 未完成数量 | 状态不在终态集合内的数量 | status not in `<terminal_statuses>` | 主业务表 | 指标卡 | 看板权限 | 过滤状态验证 |
| 平均完成周期 | 从开始时间到完成时间的平均耗时 | avg(completed_at - started_at) | 主业务表 | 指标卡/趋势 | 看板权限 | 抽样计算一致 |
| 按状态分布 | 各状态记录数量 | group by status | 主业务表/状态字典 | 饼图/柱状图 | 看板权限 | 分组总和等于总数 |
| 按类型分布 | 各业务类型数量 | group by type | 主业务表/字典 | 柱状图 | 看板权限 | 分组总和等于总数 |
| 趋势指标 | 按日期统计新增、完成或失败数量 | group by date_field | 主业务表 | 折线图 | 看板权限 | 日期范围正确 |
| 逾期数量 | 超过 SLA 且未完成的数量 | due_at < now and status not in `<terminal_statuses>` | 主业务表 | 指标卡 | 看板权限 | SLA 口径明确 |
| 人员处理负载 | 按负责人/处理人统计未完成数量 | group by owner_id / assignee_id | 主业务表 | 排行榜 | 管理员/负责人 | 数据范围正确 |
@@ -1,16 +1,23 @@
# 看板指标设计 Prompt
```text
请为 JeecgBoot 工单模块设计研发效能看板指标。
请为目标业务模块设计业务看板指标。
指标方向:
- 工单总数
- 未关闭工单数
- 平均关闭周期
- 按优先级分布
输入材料:
- 模块名称:<填写>
- 业务对象:<填写>
- 角色与数据范围:<粘贴>
- 状态枚举与时间字段:<粘贴>
- 列表筛选条件:<粘贴>
指标方向可参考:
- 记录总数
- 未完成数量
- 平均完成周期
- 按状态分布
- 按类型分布
- 缺陷趋势
- 逾期工单数
- 趋势指标
- 逾期数量
- 人员处理负载
输出要求:
@@ -24,5 +31,5 @@
- 每个指标必须可计算。
- 不要只写页面 UI。
- 指标口径不清必须写入不确定点。
- 不要把示例指标名直接当成目标项目事实。
```
@@ -1,9 +1,11 @@
# 看板接口验收 Prompt
```text
请为 JeecgBoot 工单看板统计接口生成验收记录。
请为目标业务模块看板统计接口生成验收记录。
输入材料:
- 模块名称:<填写>
- 业务对象:<填写>
- 看板指标字典:<粘贴>
- 看板接口契约:<粘贴>
- 权限矩阵:<粘贴>
@@ -23,5 +25,5 @@
约束:
- 不要只说“接口正常”。
- 每个指标至少有一个验收动作。
- 每个验收动作都要说明账号/角色、请求参数、预期、实际和证据。
```
@@ -2,11 +2,10 @@
| 验收项 | 请求条件 | 通过标准 | 结果 |
| --- | --- | --- | --- |
| 默认查询 | 管理员默认条件 | 返回完整指标 | |
| 默认查询 | 管理员或授权角色默认条件 | 返回完整指标 | |
| 时间过滤 | 指定 start/end | 只统计时间范围内数据 | |
| 类型过滤 | 指定工单类型 | 只统计对应类型 | |
| 类型过滤 | 指定业务类型 | 只统计对应类型 | |
| 空数据 | 无匹配条件 | 返回 0 或空集合 | |
| 无权限 | 未授权账号 | 返回 403 或业务拒绝 | |
| 数据范围 | 普通角色 | 只返回授权范围 | |
| 指标一致性 | 固定样例数据 | 与手工计算一致 | |
@@ -1,11 +1,10 @@
# 工单看板接口验收记录
# <模块名称> 看板接口验收记录
| 验收项 | 账号/条件 | 请求参数 | 预期 | 实际 | 证据 | 结论 |
| --- | --- | --- | --- | --- | --- | --- |
| 工单总数 | 管理员 | 默认筛选 | 返回总数 | | | |
| 空数据 | 管理员 + 无匹配条件 | 不存在的日期范围 | 返回空集合或 0 | | | |
| 无权限 | 只读外角色 | 默认筛选 | 返回 403 或业务拒绝 | | | |
| 时间过滤 | 管理员 | start/end | 只统计范围内数据 | | | |
| 数据范围 | 普通研发 | 默认筛选 | 只返回本人相关数据 | | | |
| 指标口径 | 管理员 | 固定样例数据 | 与手工计算一致 | | | |
| 记录总数 | 管理员或授权角色 | 默认筛选 | 返回总数 | | | |
| 空数据 | 授权角色 + 无匹配条件 | 不存在的日期范围或筛选条件 | 返回空集合或 0 | | | |
| 无权限 | 未授权角色 | 默认筛选 | 返回 403 或业务拒绝 | | | |
| 时间过滤 | 授权角色 | start/end | 只统计范围内数据 | | | |
| 数据范围 | 普通业务角色 | 默认筛选 | 只返回本人或授权范围内数据 | | | |
| 指标口径 | 授权角色 | 固定样例数据 | 与手工计算一致 | | | |
@@ -95,17 +95,6 @@ Good wording:
本项目未发现 `AGENTS.md`、`CLAUDE.md`、`.trae/rules` 或 `README.md` 中的明确 AI 协作规则。以下约束来自配置文件和源码模式;未能由仓库证明的内容已标记为待确认。
```
## JeecgBoot Example
For the training case only, JeecgBoot evidence can generate rules such as:
- `硬约束`: backend uses `Result<T>` response wrapper, from backend `CLAUDE.md`.
- `硬约束`: frontend uses `defHttp` and backend menu permission mode, from frontend `CLAUDE.md`.
- `硬约束`: backend modification blocks require `update-begin` / `update-end`, from backend `CLAUDE.md`.
- `观察性约束`: business features should follow existing controller/service/mapper layering after verifying representative modules.
Keep these examples out of generic outputs unless the target project is JeecgBoot or explicitly similar.
## Quality Gates
Before claiming completion:
@@ -122,4 +111,4 @@ Before claiming completion:
- Do not turn general best practices into project rules without evidence.
- Do not merge business constraints, technical constraints, and delivery constraints into one vague list.
- Do not omit verification steps; rules are useful only if a learner or team can check them.
- Do not copy JeecgBoot-specific constraints into unrelated projects.
- Do not copy one project's specific constraints into unrelated projects.
@@ -31,7 +31,7 @@
## 可复用性
- [ ] JeecgBoot 等项目特有规则没有写进通用规则。
- [ ] 某个项目特有规则没有写进通用规则。
- [ ] 每条规则都标注了“可直接复用 / 需重新扫描 / 不可迁移”。
- [ ] 换项目时必须重新扫描的内容已经列出。
- [ ] 可以从本清单反推出生成步骤。
@@ -0,0 +1,133 @@
---
name: project-knowledge-builder
description: Use when a user asks “项目知识库”, “看项目”, “整理项目”, or otherwise wants to inspect a software project deeply, build/refresh a project knowledge base, create onboarding docs, architecture maps, domain glossaries, ADRs, generated API/config/database indexes, or RAG-ready repository documentation.
---
# Project Knowledge Builder
## Overview
Build a source-grounded project knowledge base that helps humans and AI agents understand, extend, troubleshoot, and index a codebase. Treat the knowledge base as a maintained system artifact, not a README rewrite.
## Quick Use
Users should not need to remember commands, script paths, or `$project-knowledge-builder`.
The shortest trigger is enough:
- `项目知识库`
Treat the current repository as the target and `docs/project-knowledge/` as the output path unless the user says otherwise. Run the bundled inventory script yourself and write the docs. Only show manual commands when the user explicitly asks for command-line usage.
## Language And Audience Defaults
If the user writes in Chinese or asks for Chinese, write human-facing narrative docs in Chinese by default: README, context, architecture, runtime, backend, frontend, data, auth/permissions, extension, troubleshooting, RAG guidance and ADRs. Keep source paths, class names, commands, API paths, config keys, table names, permission codes and metadata enum values unchanged.
Treat beginners as a primary audience. A useful knowledge base should let a new maintainer answer four questions without extra coaching:
1. What is this project and where are the main modules?
2. How do I run it locally or identify the active runtime mode?
3. How do I find the backend endpoint, frontend page, database table and permission code for a feature?
4. What must change when adding a normal feature, especially menu/button permissions?
Do not make generated indexes overly friendly at the cost of precision. Keep generated files machine-refreshable; make narrative docs readable.
## Core Rule
Every claim must be traceable to the repository or to an explicitly marked inference. Prefer source paths, commands, generated inventories, and unresolved-question lists over confident prose.
## Workflow
1. **Set scope.** Confirm or assume the output location. Default to `docs/project-knowledge/`. If the user mentions RAG, Feishu/Lark, Dify, Notion, onboarding, architecture review, or API docs, tailor the output contract.
2. **Read local instructions first.** Check root docs plus `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, `.codex/`, `.agents/`, package manifests, build files, env examples, Docker files, and module READMEs.
3. **Generate an inventory.** Run `scripts/project_inventory.py` from this skill against the repo. Use its output as a factual base, then inspect important files manually.
4. **Map contexts.** Identify bounded contexts, runtime modes, major modules, owners implied by paths, external systems, and data stores. Use `CONTEXT.md` and `CONTEXT-MAP.md` style docs.
5. **Extract generated indexes.** Produce machine-refreshable docs where possible: endpoints, frontend routes/views, config keys, env variables, database tables, permission codes, scheduled jobs, queues, and extension points.
6. **Write human guides.** Produce onboarding, architecture, local run/deploy, data model, auth/permission, extension, troubleshooting, and glossary docs. Match the user's language for narrative docs; for Chinese users, use Chinese section titles such as `已观察来源`, `核心事实`, `操作影响`, and `未知与风险`.
7. **Create ADRs for real decisions.** Record important architecture decisions already embodied in the code. Do not invent future decisions unless marked as proposed.
8. **Add RAG metadata guidance.** Include chunking, source-path metadata, stale-data policy, and exclusion rules.
9. **Verify.** Re-run the inventory, scan docs for placeholders, broken relative links, stale paths, and unsupported claims. Report what was verified and what remains uncertain.
## Output Contract
Read `references/output-contract.md` when creating or upgrading a full knowledge base.
Minimum useful set:
```text
docs/project-knowledge/
├── README.md
├── CONTEXT.md
├── CONTEXT-MAP.md
├── 01-architecture.md
├── 02-runtime.md
├── 03-backend.md
├── 04-frontend.md
├── 05-data-and-migrations.md
├── 06-auth-permissions.md
├── 07-extension-guide.md
├── 08-troubleshooting.md
├── 09-rag-indexing.md
├── adr/
│ └── 0001-current-runtime-architecture.md
└── generated/
├── project-inventory.md
├── backend-endpoints.md
├── frontend-routes.md
├── config-reference.md
├── database-dictionary.md
└── permission-codes.md
```
Scale down only if the repo is small or the user requests a lighter artifact.
## Source-Grounding Standards
- Cite source paths inline for non-obvious claims.
- Distinguish **Observed** from **Inferred** and **Unknown**.
- Do not summarize generated code, dependencies, or configs from memory. Read them.
- If external docs are needed for unstable facts, browse primary sources and link them.
- Preserve project-specific conventions. If a repo requires trace comments, ADR format, or issue IDs, document and follow that.
## Cross-Platform Compatibility
Read `references/compatibility.md` when the user asks about macOS, Windows, Linux, CI, portability, or installing/running the skill across machines. The bundled inventory script is intended to run on Python 3.9+ with only the standard library.
## Inventory Script
Use this only for manual refreshes or when the user asks for the command. Run from the target repo:
macOS/Linux:
```sh
python3 /path/to/skill/scripts/project_inventory.py --root . --out docs/project-knowledge/generated
```
Windows PowerShell:
```powershell
py -3 C:\path\to\skill\scripts\project_inventory.py --root . --out docs\project-knowledge\generated
```
Useful options:
```sh
python3 /path/to/skill/scripts/project_inventory.py --root . --out ./kb-inventory --json
python3 /path/to/skill/scripts/project_inventory.py --root . --out docs/project-knowledge/generated --max-files 20000
```
The script is a starting point, not a replacement for reading core files. Inspect representative files before writing narrative docs.
## Quality Gates
Before final response:
- List generated docs with a platform-appropriate command, such as `find docs/project-knowledge -type f | sort` on macOS/Linux or `Get-ChildItem docs/project-knowledge -Recurse -File` on PowerShell.
- `rg -n "TODO|TBD|待补|unknown|your/path|foo|bar" docs/project-knowledge`
- Re-run the inventory script after edits.
- Check that top-level README links resolve.
- If the user is Chinese, scan narrative docs for English-only structural headings such as `Observed Sources`, `Core Facts`, `Operational Implications`, `Unknowns/Risks`, `Architecture`, `Runtime`, `Backend`, `Frontend`, `Troubleshooting`, and translate them unless they are code, metadata, or generated output.
- Check newcomer usefulness: README gives a reading order; runtime doc names local/Docker/cloud modes; auth doc explains menu/button permission flow; extension doc explains backend CRUD, frontend page and permission changes.
- `git status --short` to report changed files.
If the user asked for “deep” or “落地”, do not stop at a single overview. Deliver generated indexes plus human guides.
@@ -0,0 +1,4 @@
interface:
display_name: "项目知识库"
short_description: "深度整理代码仓库知识库"
default_prompt: "Use $project-knowledge-builder 为当前项目生成 docs/project-knowledge 项目知识库。"
@@ -0,0 +1,65 @@
# Cross-Platform Compatibility
Use this reference when running or installing the skill on macOS, Windows, Linux, or CI.
## Compatibility Summary
| Area | macOS | Linux | Windows |
| --- | --- | --- | --- |
| `SKILL.md` workflow | Supported | Supported | Supported |
| `project_inventory.py` | Supported with Python 3.9+ | Supported with Python 3.9+ | Supported with Python 3.9+ |
| Python dependencies | Standard library only | Standard library only | Standard library only |
| Path handling | `pathlib` | `pathlib` | `pathlib` |
| Shell dependency | None in script | None in script | None in script |
| Generated Markdown paths | POSIX-style relative paths | POSIX-style relative paths | POSIX-style relative paths |
The script intentionally emits repository-relative paths with `/` separators so generated Markdown is stable across platforms and friendlier for URLs, GitHub, and RAG metadata.
## Python Invocation
macOS and Linux usually have one of these:
```sh
python3 skills/project-knowledge-builder/scripts/project_inventory.py --root . --out docs/project-knowledge/generated
python skills/project-knowledge-builder/scripts/project_inventory.py --root . --out docs/project-knowledge/generated
```
Windows PowerShell usually uses:
```powershell
py -3 skills\project-knowledge-builder\scripts\project_inventory.py --root . --out docs\project-knowledge\generated
python skills\project-knowledge-builder\scripts\project_inventory.py --root . --out docs\project-knowledge\generated
```
CI should prefer an explicit Python:
```sh
python -m py_compile skills/project-knowledge-builder/scripts/project_inventory.py
python skills/project-knowledge-builder/scripts/project_inventory.py --root . --out docs/project-knowledge/generated --json
```
## Command Substitutions
| Task | macOS/Linux | Windows PowerShell |
| --- | --- | --- |
| List docs | `find docs/project-knowledge -type f \| sort` | `Get-ChildItem docs/project-knowledge -Recurse -File \| Sort-Object FullName` |
| Search placeholders | `rg -n "TODO|TBD" docs/project-knowledge` | `rg -n "TODO|TBD" docs/project-knowledge` |
| Show git status | `git status --short` | `git status --short` |
| Remove temp output | `rm -rf ./kb-inventory` | `Remove-Item .\kb-inventory -Recurse -Force` |
`rg` is preferred where available. If `rg` is not installed, use the platform search available in the environment.
## Avoid In Skill Examples
- Hard-coded `/tmp` paths when the command should work on Windows.
- POSIX-only command chains such as `cmd1 && cmd2` in user-facing instructions when a PowerShell equivalent is needed.
- Shell glob assumptions that behave differently between zsh, bash, PowerShell, and cmd.
- Python code that depends on executable shebang behavior; Windows users should call `py -3 script.py`.
## Known Limits
- Java endpoint extraction is regex-based and intentionally conservative. It does not fully resolve constants, composed annotations, or inherited controller mappings.
- SQL table extraction looks for `CREATE TABLE`; tables created dynamically or through migrations in another DSL need manual documentation.
- Frontend route extraction scans route-like literals and view files; runtime-generated route data from APIs still requires backend menu/permission data or database export.
- Very large repositories may need `--max-files` tuning or additional ignore directories.
@@ -0,0 +1,142 @@
# Project Knowledge Builder Output Contract
Use this reference when producing a full codebase knowledge base.
## Language Policy
Match the user's language for human-facing narrative docs. If the user writes in Chinese or asks for Chinese, write README, context, architecture, runtime, backend, frontend, data, auth/permissions, extension, troubleshooting, RAG guidance and ADRs in Chinese.
Keep technical anchors unchanged: source paths, commands, class names, method names, API paths, config keys, table names, permission codes, JSON keys and metadata enum values.
Generated indexes may remain technical and source-like. Do not hand-translate generated values if it reduces refreshability.
## Document Roles
| File | Purpose |
| --- | --- |
| `README.md` | Entry point, reading order, high-value links, freshness note. For Chinese output, title and usage guidance should be Chinese. |
| `CONTEXT.md` | Domain language, business capabilities, user roles, core nouns, invariants. |
| `CONTEXT-MAP.md` | Bounded contexts, module ownership, upstream/downstream dependencies, runtime boundaries. |
| `01-architecture.md` | System shape, module graph, request/data/control flows, extension seams. |
| `02-runtime.md` | Local run, build, env vars, Docker, deployment modes, process topology. Must identify local, Docker standalone and cloud modes when present. |
| `03-backend.md` | Backend modules, entrypoints, API conventions, service/mapper/entity patterns. |
| `04-frontend.md` | Frontend bootstrap, routing, state, API client, component/page conventions. |
| `05-data-and-migrations.md` | Databases, migrations, schema sources, seed data, table families. |
| `06-auth-permissions.md` | Authentication, authorization, token/session flow, permission-code lifecycle. Must explain menu/button permission flow when the repo has backend-driven menus. |
| `07-extension-guide.md` | How to add a feature end-to-end, with source-grounded examples. Must cover backend CRUD, frontend page and permission/menu data changes when applicable. |
| `08-troubleshooting.md` | Symptom-driven runbook with commands, files, and likely causes. |
| `09-rag-indexing.md` | Chunking, metadata, exclusions, refresh cadence, stale-data policy. |
| `adr/*.md` | Architecture decisions observed in the current code. |
| `generated/*.md` | Data-derived indexes that can be regenerated. |
## Required Sections
Each narrative doc should include:
- **Observed Sources:** concrete files or commands used.
- **Core Facts:** concise source-backed statements.
- **Operational Implications:** what developers must do differently because of those facts.
- **Unknowns/Risks:** gaps that need runtime access, DB export, product owner input, or external docs.
For Chinese output, use equivalent section labels:
- **已观察来源**
- **核心事实**
- **操作影响**
- **未知与风险**
## Newcomer Usefulness Gate
Before considering the knowledge base useful for handoff, verify that a new maintainer can answer:
1. What is the product/project, and which directories matter first?
2. How do I start or identify the active runtime mode?
3. Where are backend endpoints, frontend routes/pages, config, tables and permission codes indexed?
4. How do menu, button and backend permission checks connect?
5. When adding a normal feature, which backend, frontend, database and permission artifacts usually change?
If the answer is not obvious from README plus the narrative docs, add or revise human-facing guidance before finishing. Do not solve this by making generated indexes longer.
## Generated Indexes
### `project-inventory.md`
Include:
- repository roots
- build/package manifests
- primary languages
- major directories
- scripts
- Docker/compose files
- env files
- test files
### `backend-endpoints.md`
Include route method, path, class/file, approximate line, auth annotations, and notes. Do not invent full route paths when class-level and method-level mappings require Java expression resolution; show observed fragments and mark composition limitations.
### `frontend-routes.md`
Include static router files, dynamic route loaders, `src/views` index, route guard files, and external route package loaders.
### `config-reference.md`
Include env files, YAML/properties files, relevant keys, default values, and risk notes. Redact secrets.
### `database-dictionary.md`
Include SQL files, migration files, table names, table families, and whether the table is initialized, migrated, or inferred. Avoid loading huge SQL into prose; summarize and link.
### `permission-codes.md`
Include backend annotations, frontend permission helpers/usages, menu/permission SQL occurrences, and auth lifecycle notes.
## ADR Template
```markdown
# ADR NNNN: Title
Date: YYYY-MM-DD
Status: Observed
## Context
Source-backed facts that show the decision exists.
## Decision
The architecture decision embodied by the current code.
## Consequences
Benefits, tradeoffs, operational constraints, and migration risks.
## Sources
- `path/to/file`
```
## RAG Metadata
Recommended metadata per chunk:
```json
{
"project": "name",
"snapshot_date": "YYYY-MM-DD",
"source_path": "path",
"doc_type": "architecture|runtime|generated|adr|runbook",
"domain": "backend|frontend|data|auth|deploy",
"freshness": "manual|generated",
"confidence": "observed|inferred|unknown"
}
```
## Anti-Patterns
- A long essay without generated indexes.
- “Best practice” advice unrelated to the repo.
- Copying README marketing text as architecture documentation.
- Claiming an endpoint, table, permission, or runtime mode exists without a source path.
- Hiding unknowns instead of listing them.
@@ -0,0 +1,313 @@
#!/usr/bin/env python3
"""Generate a source-grounded project inventory for knowledge-base work."""
from __future__ import annotations
import argparse
import json
import os
import re
from collections import Counter, defaultdict
from pathlib import Path
from typing import Iterable
IGNORE_DIRS = {
".git",
".idea",
".vscode",
"node_modules",
"dist",
"build",
"target",
".gradle",
".mvn/wrapper",
"coverage",
".next",
".nuxt",
"__pycache__",
}
MANIFEST_NAMES = {
"package.json",
"pnpm-lock.yaml",
"yarn.lock",
"package-lock.json",
"pom.xml",
"build.gradle",
"settings.gradle",
"Cargo.toml",
"pyproject.toml",
"requirements.txt",
"go.mod",
"composer.json",
"Gemfile",
}
CONFIG_PATTERNS = (
re.compile(r".*\.env(\..*)?$"),
re.compile(r".*application[-\w]*\.(yml|yaml|properties)$"),
re.compile(r".*\.(ya?ml|toml|ini|properties|conf)$"),
)
HTTP_MAPPING_RE = re.compile(
r"@(RequestMapping|GetMapping|PostMapping|PutMapping|DeleteMapping|PatchMapping)\s*(?:\((.*?)\))?"
)
PERMISSION_RE = re.compile(r"@(?:RequiresPermissions|RequiresRoles)\s*\((.*?)\)")
TABLE_NAME_RE = re.compile(
r"\bCREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?`?([A-Za-z0-9_.$-]+)`?",
re.IGNORECASE,
)
VUE_ROUTE_RE = re.compile(r"\b(path|name|component)\s*:\s*['\"]([^'\"]+)['\"]")
def is_ignored(path: Path, root: Path) -> bool:
rel_parts = path.relative_to(root).parts
for i in range(len(rel_parts)):
joined = "/".join(rel_parts[: i + 1])
if joined in IGNORE_DIRS or rel_parts[i] in IGNORE_DIRS:
return True
return False
def iter_files(root: Path, max_files: int) -> Iterable[Path]:
count = 0
for dirpath, dirnames, filenames in os.walk(root):
current = Path(dirpath)
dirnames[:] = [d for d in dirnames if not is_ignored(current / d, root)]
for name in filenames:
path = current / name
if is_ignored(path, root):
continue
count += 1
if count > max_files:
return
yield path
def rel(path: Path, root: Path) -> str:
return path.relative_to(root).as_posix()
def read_text(path: Path, limit: int = 400_000) -> str:
try:
data = path.read_bytes()
except OSError:
return ""
if len(data) > limit:
data = data[:limit]
try:
return data.decode("utf-8")
except UnicodeDecodeError:
try:
return data.decode("utf-8", errors="ignore")
except Exception:
return ""
def detect_config(path: Path) -> bool:
name = path.name
return any(pattern.fullmatch(name) for pattern in CONFIG_PATTERNS)
def extract_mapping_value(raw: str | None) -> str:
if not raw:
return ""
value_match = re.search(r"['\"]([^'\"]+)['\"]", raw)
if value_match:
return value_match.group(1)
return raw.strip().replace("\n", " ")[:120]
def scan(root: Path, max_files: int) -> dict:
files = list(iter_files(root, max_files))
ext_counts = Counter(path.suffix.lower() or "[none]" for path in files)
manifests = []
configs = []
docker = []
tests = []
java_endpoints = []
permission_codes = []
vue_routes = []
views = []
sql_tables = []
major_dirs = defaultdict(int)
for path in files:
r = rel(path, root)
parts = Path(r).parts
if parts:
major_dirs[parts[0]] += 1
if path.name in MANIFEST_NAMES:
manifests.append(r)
lower = path.name.lower()
if lower.startswith("dockerfile") or "docker-compose" in lower:
docker.append(r)
if detect_config(path):
configs.append(r)
if re.search(r"(^|/)(test|tests|__tests__)(/|$)", r) or re.search(r"\.(test|spec)\.[A-Za-z0-9]+$", r):
tests.append(r)
if "/src/views/" in r and path.suffix.lower() in {".vue", ".tsx", ".ts"}:
views.append(r)
suffix = path.suffix.lower()
if suffix not in {".java", ".ts", ".tsx", ".vue", ".js", ".jsx", ".sql", ".yml", ".yaml", ".properties"}:
continue
text = read_text(path)
if not text:
continue
if suffix == ".java":
lines = text.splitlines()
class_mapping = ""
class_name = ""
pending_annotations: list[tuple[int, str, str]] = []
for idx, line in enumerate(lines, start=1):
class_match = re.search(r"\bclass\s+([A-Za-z0-9_]+)", line)
if class_match:
class_name = class_match.group(1)
for match in HTTP_MAPPING_RE.finditer(line):
value = extract_mapping_value(match.group(2))
annotation = match.group(1)
pending_annotations.append((idx, annotation, value))
if annotation == "RequestMapping" and not class_mapping and ("class " not in line):
class_mapping = value
if re.search(r"\b(public|private|protected)\b.*\(", line) and pending_annotations:
method = line.strip()[:160]
for ann_line, annotation, value in pending_annotations:
java_endpoints.append(
{
"file": r,
"line": ann_line,
"class": class_name,
"annotation": annotation,
"class_mapping": class_mapping,
"mapping": value,
"method_signature": method,
}
)
pending_annotations = []
for perm in PERMISSION_RE.finditer(line):
permission_codes.append({"file": r, "line": idx, "value": perm.group(1).strip()})
if suffix in {".ts", ".tsx", ".vue", ".js", ".jsx"}:
for idx, line in enumerate(text.splitlines(), start=1):
if "path:" in line or "component:" in line or "name:" in line:
for match in VUE_ROUTE_RE.finditer(line):
vue_routes.append({"file": r, "line": idx, "key": match.group(1), "value": match.group(2)})
if suffix == ".sql":
for match in TABLE_NAME_RE.finditer(text):
sql_tables.append({"file": r, "table": match.group(1)})
return {
"root": str(root),
"file_count": len(files),
"extensions": dict(ext_counts.most_common(40)),
"major_dirs": dict(sorted(major_dirs.items(), key=lambda item: (-item[1], item[0]))),
"manifests": sorted(manifests),
"configs": sorted(configs),
"docker": sorted(docker),
"tests": sorted(tests),
"views": sorted(views),
"java_endpoints": java_endpoints,
"permission_codes": permission_codes,
"vue_routes": vue_routes,
"sql_tables": sql_tables,
}
def write_md(data: dict, out_dir: Path) -> None:
out_dir.mkdir(parents=True, exist_ok=True)
def bullet(items: list[str], limit: int | None = None) -> str:
selected = items if limit is None else items[:limit]
if not selected:
return "- None found\n"
body = "".join(f"- `{item}`\n" for item in selected)
if limit is not None and len(items) > limit:
body += f"- ... {len(items) - limit} more\n"
return body
inventory = [
"# Project Inventory\n\n",
f"Root: `{data['root']}`\n\n",
f"Files scanned: {data['file_count']}\n\n",
"## Major Directories\n\n",
]
inventory.extend(f"- `{name}`: {count} files\n" for name, count in data["major_dirs"].items())
inventory.extend(["\n## Extensions\n\n"])
inventory.extend(f"- `{name}`: {count}\n" for name, count in data["extensions"].items())
inventory.extend(["\n## Manifests\n\n", bullet(data["manifests"])])
inventory.extend(["\n## Config Files\n\n", bullet(data["configs"], 200)])
inventory.extend(["\n## Docker Files\n\n", bullet(data["docker"])])
inventory.extend(["\n## Test Files\n\n", bullet(data["tests"], 200)])
(out_dir / "project-inventory.md").write_text("".join(inventory), encoding="utf-8")
endpoints = ["# Backend Endpoints\n\n"]
if data["java_endpoints"]:
endpoints.append("| File | Line | Class | Annotation | Class Mapping | Mapping | Method |\n")
endpoints.append("| --- | ---: | --- | --- | --- | --- | --- |\n")
for item in data["java_endpoints"]:
method_signature = item["method_signature"].replace("|", "\\|")
endpoints.append(
f"| `{item['file']}` | {item['line']} | `{item['class']}` | `{item['annotation']}` | "
f"`{item['class_mapping']}` | `{item['mapping']}` | `{method_signature}` |\n"
)
else:
endpoints.append("No Java endpoint annotations found.\n")
(out_dir / "backend-endpoints.md").write_text("".join(endpoints), encoding="utf-8")
routes = ["# Frontend Routes And Views\n\n", "## Views\n\n", bullet(data["views"], 400), "\n## Route-like Literals\n\n"]
if data["vue_routes"]:
routes.append("| File | Line | Key | Value |\n| --- | ---: | --- | --- |\n")
for item in data["vue_routes"][:1000]:
routes.append(f"| `{item['file']}` | {item['line']} | `{item['key']}` | `{item['value']}` |\n")
if len(data["vue_routes"]) > 1000:
routes.append(f"\nTruncated: {len(data['vue_routes']) - 1000} more route-like literals.\n")
else:
routes.append("No route-like literals found.\n")
(out_dir / "frontend-routes.md").write_text("".join(routes), encoding="utf-8")
configs = ["# Config Reference\n\n", bullet(data["configs"], 500)]
(out_dir / "config-reference.md").write_text("".join(configs), encoding="utf-8")
tables = ["# Database Dictionary\n\n"]
if data["sql_tables"]:
tables.append("| Table | Source |\n| --- | --- |\n")
for item in sorted(data["sql_tables"], key=lambda x: (x["table"], x["file"])):
tables.append(f"| `{item['table']}` | `{item['file']}` |\n")
else:
tables.append("No SQL CREATE TABLE statements found in scanned files.\n")
(out_dir / "database-dictionary.md").write_text("".join(tables), encoding="utf-8")
perms = ["# Permission Codes\n\n"]
if data["permission_codes"]:
perms.append("| File | Line | Value |\n| --- | ---: | --- |\n")
for item in data["permission_codes"]:
permission_value = item["value"].replace("|", "\\|")
perms.append(f"| `{item['file']}` | {item['line']} | `{permission_value}` |\n")
else:
perms.append("No permission annotations found.\n")
(out_dir / "permission-codes.md").write_text("".join(perms), encoding="utf-8")
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", default=".", help="Repository root to scan")
parser.add_argument("--out", required=True, help="Output directory for generated Markdown")
parser.add_argument("--json", action="store_true", help="Also write inventory.json")
parser.add_argument("--max-files", type=int, default=30000, help="Maximum files to scan")
args = parser.parse_args()
root = Path(args.root).resolve()
out_dir = Path(args.out).resolve()
data = scan(root, args.max_files)
write_md(data, out_dir)
if args.json:
(out_dir / "inventory.json").write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"Wrote project inventory to {out_dir}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
@@ -0,0 +1,59 @@
# 业务交付验收清单
本清单用于判断复杂业务模块是否达到可验收状态,适用于后台管理、审批流、运营系统、CRM、低代码二开和数据看板等项目。
## 项目理解
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 能说明模块边界 | 说明模块负责什么、不负责什么 | |
| 能定位关键目录 | 能指出前端页面、API 层、后端入口、服务层和数据层位置 | |
| 能区分事实和推断 | 对没有源码依据的业务规则标注为推断或待确认 | |
| 能记录不确定点 | 不确定内容没有被包装成确定事实 | |
## 需求与接口
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 用户故事完整 | 覆盖主要角色、关键动作和限制条件 | |
| 非目标范围明确 | 明确本次不做的能力、数据迁移、生产部署或外部集成 | |
| 接口契约完整 | 覆盖列表、详情、新增、编辑、分配、状态、审计和看板 | |
| 失败场景明确 | 每个关键接口至少有权限、参数、状态或数据不存在失败场景 | |
| 验收方式明确 | 每个关键需求都有可执行检查方法 | |
## 权限与状态
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 权限矩阵完整 | 覆盖页面级、按钮级、接口级和数据范围 | |
| 权限字符合理 | 命名统一,并标注为建议或源码事实 | |
| 后端权限未缺失 | 没有用前端按钮隐藏替代后端接口控制 | |
| 状态流转表完整 | 覆盖初始态、处理中、确认/审核态、终态和驳回态 | |
| 非法流转明确 | 列出关键非法流转、拒绝原因和验收方式 | |
## 审计、异常态与看板
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 审计操作覆盖关键动作 | 创建、编辑、分配、状态流转、关闭/归档等关键动作被覆盖 | |
| 审计字段可追溯 | 包含 actor、action、targetId、oldValue、newValue、source | |
| 敏感信息处理 | oldValue/newValue 有脱敏说明 | |
| 异常态可测试 | 权限不足、重复提交、非法状态、数据不存在、并发修改均覆盖 | |
| 看板指标可计算 | 每个指标有口径、维度、过滤条件和数据来源建议 | |
## Review 与交付
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| Review 有证据 | 问题能回到路径、表格、接口、角色或业务规则 | |
| 风险按严重程度排序 | 高风险问题优先展示 | |
| 建议可行动 | 每条建议有修复方向和验收方式 | |
| 交付物齐全 | 设计表、Review 报告、验证记录和未闭环事项完整 | |
| 能沉淀资产 | 高频问题能转成规则、检查清单、Prompt、反例或 Skill | |
## 判定原则
- 优先看证据,不优先看文字是否漂亮。
- 优先看权限、状态、审计、异常态,不只看 CRUD。
- 优先看不确定点是否被诚实标出。
- 优先看结论能否被角色账号、接口请求、样例数据或浏览器操作验证。
@@ -2,12 +2,11 @@
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 创建记录 | 创建工单后能查询审计记录 | |
| 创建记录 | 创建业务记录后能查询审计记录 | |
| 字段变更 | 编辑关键字段记录 oldValue/newValue | |
| 指派记录 | 指派负责人记录原负责人和新负责人 | |
| 分配记录 | 分配或转交记录原处理人和新处理人 | |
| 状态记录 | 状态流转记录原状态和新状态 | |
| 操作人 | actor_id 和 actor_name 可追溯 | |
| 来源 | source 或 request_id 可追溯 | |
| 脱敏 | 敏感字段不明文进入审计 | |
| 失败处理 | 操作失败不写成功审计 | |
@@ -3,11 +3,10 @@
| 异常态 | 必须验证 | 结果 |
| --- | --- | --- |
| 空列表 | 页面显示空状态,不报错 | |
| 详情不存在 | 提示工单不存在,页面不崩溃 | |
| 详情不存在 | 提示记录不存在,页面不崩溃 | |
| 权限不足 | 前端提示或隐藏,后端拒绝 | |
| 非法状态 | 返回非法流转,状态不变 | |
| 重复提交 | 只成功一次,只写一条有效审计 | |
| 参数缺失 | 返回参数错误,业务数据不变 | |
| 并发修改 | 拒绝覆盖或提示刷新 | |
| 看板无数据 | 图表显示空态,不显示假数据 | |
@@ -2,11 +2,10 @@
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 菜单权限 | 工单菜单只对授权角色可见 | |
| 按钮权限 | 新增、编辑、删除、指派、状态按钮按权限显示 | |
| 菜单权限 | 目标模块菜单只对授权角色可见 | |
| 按钮权限 | 新增、编辑、删除、分配、状态按钮按权限显示 | |
| 接口权限 | 无权限账号直接请求敏感接口失败 | |
| 数据权限 | 普通用户只能看到授权范围内工单 | |
| 数据权限 | 普通用户只能看到授权范围内数据 | |
| 权限字符 | 前端按钮、菜单配置、后端接口命名一致 | |
| 审计 | 权限敏感操作成功后有审计记录 | |
| 反例验证 | 无权限账号不能绕过前端执行删除/指派 | |
| 反例验证 | 无权限账号不能绕过前端执行删除/分配 | |
@@ -6,7 +6,6 @@
| 合法流转 | 状态流转表中的合法路径可执行 | |
| 非法流转 | 非法路径被拒绝且状态不变 | |
| 角色限制 | 不同角色只能执行被授权动作 | |
| 前置条件 | 驳回原因、关闭条件、负责人等校验完整 | |
| 前置条件 | 驳回原因、关闭条件、处理人、审批意见等校验完整 | |
| 审计日志 | 每次成功流转写审计 | |
| 重复提交 | 重复点击不会产生重复有效流转 | |
@@ -2,11 +2,10 @@
| 验收项 | 请求条件 | 通过标准 | 结果 |
| --- | --- | --- | --- |
| 默认查询 | 管理员默认条件 | 返回完整指标 | |
| 默认查询 | 管理员或授权角色默认条件 | 返回完整指标 | |
| 时间过滤 | 指定 start/end | 只统计时间范围内数据 | |
| 类型过滤 | 指定工单类型 | 只统计对应类型 | |
| 类型过滤 | 指定业务类型 | 只统计对应类型 | |
| 空数据 | 无匹配条件 | 返回 0 或空集合 | |
| 无权限 | 未授权账号 | 返回 403 或业务拒绝 | |
| 数据范围 | 普通角色 | 只返回授权范围 | |
| 指标一致性 | 固定样例数据 | 与手工计算一致 | |
@@ -1,9 +1,11 @@
# 阶段 Review Prompt
```text
请对 JeecgBoot 工单模块阶段交付物做 Review。
请对目标业务模块阶段交付物做 Review。
输入材料:
- 模块名称:<填写>
- 业务对象:<填写>
- RBAC 权限矩阵
- 权限控制方案
- 状态流转表
@@ -26,5 +28,5 @@
约束:
- 不要只说整体不错。
- 不要直接改代码。
- 不要把示例模块、示例角色或示例接口当成目标项目事实。
```
@@ -15,7 +15,7 @@
| 等级 | 模块 | 问题 | 触发规则 | 风险 | 修复建议 | 验收 |
| --- | --- | --- | --- | --- | --- | --- |
| P1 | 权限 | 删除接口只隐藏按钮,后端缺少权限校验 | 前端按钮不是安全边界 | 可绕过前端删除工单 | 后端补权限校验 | 无权限账号请求返回 403 |
| P1 | 权限 | 敏感操作只做前端按钮隐藏,后端缺少权限校验 | 前端按钮不是安全边界 | 可绕过前端直接调用接口完成高风险操作 | 后端补权限校验和数据范围校验 | 无权限账号直接请求返回 403 或业务拒绝 |
## 不确定点
@@ -25,4 +25,3 @@
## 结论
通过 / 有条件通过 / 不通过
@@ -1,71 +0,0 @@
# 验收清单
本清单用于判断学员是否真正掌握 JeecgBoot 工单案例,而不是只复制了 TRAE 输出。建议讲师在小组展示时逐项打分。
## 项目理解
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 能说明 JeecgBoot 前后端目录 | 至少指出 `jeecg-boot`、`jeecgboot-vue3`、`docs/project-knowledge` 的用途 | |
| 能定位后端入口 | 能指出 `JeecgSystemApplication.java` 和后端启动模块 | |
| 能定位前端入口 | 能指出 `src/main.ts`、路由、Pinia 和 API 请求层 | |
| 能区分事实和推断 | 对没有源码依据的业务规则标注为推断或待确认 | |
| 能记录不确定点 | 不确定内容没有被包装成确定事实 | |
## 需求与接口
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 用户故事完整 | 覆盖管理员、负责人、研发、测试、只读用户 | |
| 非目标范围明确 | 明确课堂不做完整编码、生产部署、真实数据迁移 | |
| 接口契约完整 | 覆盖列表、详情、新增、编辑、指派、状态、审计、看板 | |
| 失败场景明确 | 每个关键接口至少有权限、参数、状态或数据不存在失败场景 | |
| 验收方式明确 | 每个关键需求都有可执行检查方法 | |
## 权限与状态
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 权限矩阵完整 | 覆盖页面级、按钮级、接口级和数据范围 | |
| 权限字符合理 | 命名统一,并标注为建议而非源码事实 | |
| 后端权限未缺失 | 没有用前端按钮隐藏替代后端接口控制 | |
| 状态流转表完整 | 覆盖新建、处理中、待验证、已关闭、已驳回 | |
| 非法流转明确 | 至少列出 5 条非法流转和拒绝原因 | |
## 审计、异常态与看板
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| 审计操作覆盖关键动作 | 创建、编辑、指派、状态流转、关闭、评论至少覆盖 5 类 | |
| 审计字段可追溯 | 包含 actor、action、targetId、oldValue、newValue、source | |
| 敏感信息处理 | oldValue/newValue 有脱敏说明 | |
| 异常态可测试 | 权限不足、重复提交、非法状态、数据不存在、并发修改均覆盖 | |
| 看板指标可计算 | 每个指标有口径、维度、过滤条件和数据来源建议 | |
## Review 与交付
| 检查项 | 通过标准 | 结果 |
| --- | --- | --- |
| Review 有证据 | 问题能回到路径、表格或业务规则 | |
| 风险按严重程度排序 | 高风险问题优先展示 | |
| 建议可行动 | 每条建议有修复方向和验收方式 | |
| 交付物齐全 | [worksheets.md](./worksheets.md) 中最终交付记录完整 | |
| 能沉淀资产 | 能说明哪些内容进入 [assets-index.md](./assets-index.md) 的资产库 | |
## 评分建议
| 等级 | 分数 | 描述 |
| --- | ---: | --- |
| A | 90-100 | 依据充分,设计完整,风险清楚,验收可执行 |
| B | 75-89 | 主链路完整,少量不确定点或验收细节不足 |
| C | 60-74 | 有基本产出,但权限、状态、审计或异常态明显薄弱 |
| D | 0-59 | 泛泛总结项目,缺少源码依据或无法验收 |
## 讲师判定原则
- 优先看证据,不优先看文字是否漂亮。
- 优先看权限、状态、审计、异常态,不只看 CRUD。
- 优先看不确定点是否被诚实标出。
- 优先看能否复用到团队资产库。
- 对没有依据的自信结论扣分。
@@ -2,7 +2,6 @@
| Review 编号 | 问题 | 是否成立 | 修改文件 | 修改说明 | 验证结果 | 资产沉淀 |
| --- | --- | --- | --- | --- | --- | --- |
| R01 | 删除接口缺少后端权限校验 | 是 | WorkOrderController | 增加权限校验 | 无权限请求被拒绝 | 权限检查清单 |
| R02 | closed 可重新处理 | 是 | WorkOrderService | 补非法流转校验 | closed -> processing 失败 | 状态流转检查清单 |
| R01 | 敏感操作接口缺少后端权限校验 | 是 | `<Controller/API handler>` | 增加权限校验和数据范围校验 | 无权限请求被拒绝 | 权限检查清单 |
| R02 | 终态记录可被直接改回处理中 | 是 | `<Service/Domain logic>` | 补非法流转校验 | terminal -> processing 失败 | 状态流转检查清单 |
| R03 | 看板关闭周期口径不清 | 是 | 指标字典/看板接口 | 明确 create_time 到 close_time | 指标说明已更新 | 看板指标口径清单 |
@@ -2,31 +2,30 @@
## 场景
工单指派负责人成功,但没有记录审计日志。
业务记录分配处理人成功,但没有记录审计日志。
## 问题表现
| 操作 | 结果 |
| --- | --- |
| 张三将工单指派给李四 | 工单负责人变更成功 |
| 查询审计日志 | 没有负责人变更记录 |
| 张三将记录分配给李四 | 处理人变更成功 |
| 查询审计日志 | 没有处理人变更记录 |
## 风险
无法追溯负责人变化,出现延误或争议时无法定位责任。
无法追溯处理人变化,出现延误或争议时无法定位责任。
## 修复要求
1. 指派成功后写审计日志。
2. 记录原负责人、新负责人、操作人、时间、来源。
3. 指派失败不写成功审计。
1. 分配成功后写审计日志。
2. 记录原处理人、新处理人、操作人、时间、来源。
3. 分配失败不写成功审计。
4. 多步写入需要事务一致性。
## 验收
| 操作 | 预期 |
| --- | --- |
| 指派成功 | 审计列表出现 ASSIGN 记录 |
| 指派失败 | 不出现成功审计 |
| 查询详情 | 审计记录可在工单详情查看 |
| 分配成功 | 审计列表出现 ASSIGN 或同等业务动作记录 |
| 分配失败 | 不出现成功审计 |
| 查询详情 | 审计记录可在详情页查看 |
@@ -2,19 +2,19 @@
## 场景
工单删除按钮在前端按权限隐藏,但后端删除接口没有权限校验。
敏感删除或作废按钮在前端按权限隐藏,但后端接口没有权限校验。
## 问题代码表现
| 层级 | 表现 |
| --- | --- |
| 前端 | 普通研发账号看不到删除按钮 |
| 后端 | 直接请求 `/workOrder/delete` 仍可删除 |
| 前端 | 普通业务账号看不到删除按钮 |
| 后端 | 直接请求删除或作废接口仍可成功 |
| 审计 | 删除记录可能缺失 |
## 风险
攻击者或误操作用户可以绕过前端直接调用接口删除工单。
攻击者或误操作用户可以绕过前端直接调用接口删除或作废业务数据。
## 修复要求
@@ -27,7 +27,6 @@
| 账号 | 操作 | 预期 |
| --- | --- | --- |
| 管理员 | 删除允许删除的工单 | 成功并写审计 |
| 普通研发 | 直接请求删除接口 | 失败 |
| 只读用户 | 前端访问删除按钮 | 不可见 |
| 管理员或授权角色 | 删除允许删除的数据 | 成功并写审计 |
| 普通业务角色 | 直接请求删除接口 | 失败 |
| 只读角色 | 前端访问删除按钮 | 不可见 |
@@ -2,22 +2,22 @@
## 场景
看板展示“平均关闭周期”,但没有说明从哪个时间到哪个时间。
看板展示“平均完成周期”,但没有说明从哪个时间到哪个时间。
## 问题表现
| 指标 | 当前说明 |
| --- | --- |
| 平均关闭周期 | 最近工单平均处理时间 |
| 平均完成周期 | 最近记录平均处理时间 |
## 风险
不同人理解不同:可能是创建到关闭,也可能是开始处理到关闭,导致管理判断失真。
不同人理解不同:可能是创建到完成,也可能是开始处理到完成,导致管理判断失真。
## 修复要求
1. 明确定义为 `close_time - create_time` 或其他确定口径。
2. 明确是否排除未关闭工单。
1. 明确定义为 `completed_at - created_at`、`closed_at - created_at` 或其他确定口径。
2. 明确是否排除未完成记录。
3. 明确时间范围过滤字段。
4. 用样例数据手工校验。
@@ -25,7 +25,6 @@
| 样例 | 预期 |
| --- | --- |
| 工单 A 创建 1 日、关闭 3 日 | 周期 2 天 |
| 未关闭工单 | 不计入平均关闭周期 |
| 记录 A 创建 1 日、完成 3 日 | 周期 2 天 |
| 未完成记录 | 不计入平均完成周期 |
| 过滤 7 月 | 只统计 7 月范围内符合口径的数据 |
@@ -2,7 +2,7 @@
## 场景
已关闭工单可以直接被改回处理中。
已完成或已关闭的业务记录可以直接被改回处理中。
## 问题表现
@@ -12,12 +12,12 @@
## 风险
已关闭工单被重新打开但没有重开原因、没有审计、没有负责人确认,导致统计和责任链失真。
终态记录被重新打开但没有重开原因、没有审计、没有负责人确认,导致统计和责任链失真。
## 修复要求
1. Service 层集中校验状态流转。
2. 已关闭工单只能通过“重新打开”动作变为新建。
2. 已完成或已关闭记录只能通过“重新打开”动作回到指定状态。
3. 重新打开必须填写原因。
4. 所有成功流转写审计日志。
@@ -28,4 +28,3 @@
| closed -> processing | 失败,状态不变 |
| closed -> reopen -> new | 成功,必须有原因 |
| 重开成功 | 审计记录 oldValue=closed, newValue=new |