Files
cn-rdms/CLAUDE.md
hongawen d669d53a80 feat(project): 添加项目进度自动计算功能
- 在 ProjectMapper 中新增 updateProgressRateById 方法,支持单独更新项目进度
- 在 ProjectService 中新增 recalcProgress 接口,用于重新计算项目进度
- 实现 ProjectServiceImpl 的进度重计算逻辑,通过根任务平均进度更新项目进度
- 新增 ProjectTaskMapper 的 selectRootTaskAvgProgressByProjectId 查询方法
- 在任务创建、更新、删除、状态变更等操作后触发项目进度重计算
- 添加进度归一化处理,确保数值精度为两位小数
- 更新 CLAUDE.md 文档,加强技术风险判断要求
2026-05-25 14:17:37 +08:00

176 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# CLAUDE.md
本文件为 `C:\code\gitea\rdms\cn-rdms` 仓库下所有 Agent含 Claude Code的常驻工作指引`AGENTS.md` 引用本文件。
## 工作方式
- 默认先给执行方案:目标、涉及模块、改动点、验证方式;不擅自动手。
- 用户只要分析或评审时,停在分析层;不要顺手开工。
- 描述仓库现状以**当前**代码、配置、文档可验证的事实为准;不要拿历史实现、过渡方案或已废弃模型解释当前状态。
- **输出极简**:先给结论、改动点、必要风险;用自然语言给判断和影响面,少贴代码片段;涉及代码用 `file_path:line_number` 引用;用户追问再展开。
- **下定论需要充足证据**。疑似 bug 时先判断是否稳定复现:跑了很久没动过的功能**首次**报错优先怀疑运行时状态污染devtools / IDE 热替换、ApplicationContext 残留、缓存、Redis / DB 连接、JVM 静态字段被旧 context 设过等),**不要凭单次堆栈就断言代码 bug更不要直接甩修改方案**。先给"可能原因 + 最便宜的取证步骤"(多数场景是**冷重启 JVM**),用户确认能稳定复现,再讨论代码层面的修复。同款写法在仓库其它位置存在并不能反推"也是 bug",长期能跑的代码突然失效 ≠ 代码本身错。
- **技术风险判断(性能 / N+1 / 索引缺失 / 架构缺陷 / 并发安全 / 内存泄漏 等)与"bug 判断"同等严格**:未读到实现层不下结论。不要凭 subagent 摘要、字段名、注释或印象"顺嘴提一句风险/瓶颈/可能问题"——那也是下结论,**且杀伤力更大**:用户会基于"风险提示"决定要不要立项整改。如果当前上下文没核实到实现,就明说"这部分未核实,需要打开 X 文件确认",不要把猜测包装成"风险提示"塞出去。已识别教训:执行进度查询答完"已批量聚合无 N+1"后又凭印象抛"列表 N+1 风险",被追问才收回。
## 本机环境
- JDK必须使用 `JDK 17`,路径 `C:\Program Files\Java\jdk-17`。不要使用 JDK 8 / 11 / 其他版本。
- Maven`C:\software\apache-maven-3.8.9`,命令优先用完整路径 `C:\software\apache-maven-3.8.9\bin\mvn.cmd`。不要假设 `mvn` 在 PATH。
- 执行任何 Maven / java 命令前,先确认当前 shell 的 `JAVA_HOME` 指向 JDK 17`java -version` 输出 17否则在该命令上下文中显式切换。
## 仓库结构
多模块 Maven 单仓库Java 17Spring Boot 3.5.9,根模块打包 `pom`
顶层模块:
1. `rdms-system` — 系统域(用户/组织/岗位/菜单/角色/权限)
2. `rdms-project` — RDMS 核心交付域(项目集/项目/产品/需求/任务/工单/执行)
3. `rdms-framework` — 共享框架与内部 starter
4. `rdms-gateway` — Spring Cloud Gateway 网关
每个业务模块按 `xxx-api` + `xxx-boot` 拆分:
- `*-api`:对外 RPC/Feign 接口、DTO、错误码、枚举、常量
- `*-boot`启动类、controller、service、dal、convert、api 实现、模块级 framework 配置
主包/启动类:
- `rdms-system-boot``com.njcn.rdms.module.system.SystemServerApplication`
- `rdms-project-boot``com.njcn.rdms.module.project.ProjectServerApplication`
- `rdms-gateway``com.njcn.rdms.gateway.GatewayServerApplication`
`rdms-framework` 子模块:`rdms-common` 及一组 `rdms-spring-boot-starter-*``env``web``rpc``security``mybatis``redis``mq``websocket``excel``protection``test``biz-ip`)。
## 模块演进判断
新增能力时**先判断落点**
- 落在现有 `rdms-system` 子域 → 沿用 `controller/admin|app``service``dal/dataobject``dal/mysql``convert` 的现有结构,跨模块暴露时在 `rdms-system-api` 补 API/DTO/错误码/枚举。
- 落在现有 `rdms-project` 子域 → 同理,跨模块走 `rdms-project-api`
- 已具备独立服务边界(如未来的 `rdms-workflow`)→ 新建 `rdms-xxx` + `rdms-xxx-api` + `rdms-xxx-boot`,根 `pom.xml` 加聚合,包路径 / `spring.application.name` / `ApiConstants` / `RpcConstants` / `rdms.info.base-package` 保持一致。
不要:
- 把后续业务长期堆进 `rdms-system`
- 为新增子域引入一套平行的 `application/domain/infrastructure/adapter` 分层。
- 让外部模块直接依赖 `*-boot` 的 service 或 mapper必须走 `*-api`)。
## 分层职责
| 层 | 职责 | 红线 |
|---|---|---|
| `rdms-framework` | 基础能力 | 不承载业务语义;除非框架级缺陷或全局基础设施,不要把业务判断塞进来 |
| `rdms-gateway` | 入口、令牌校验、登录用户透传、路由、横切 | 不要在这里承载组织/成员/负责人/项目/产品/工作流状态/数据可见性 |
| Controller | HTTP 暴露、参数校验、权限注解、结果封装 | 不要编排复杂业务流程,不要直接操作多个 mapper`ReqVO`/`RespVO`,不要直接暴露 DO |
| Service | 业务规则、事务、缓存、领域编排 | 已有领域优先扩展,不要为"整齐"平移;不要把规则散到 controller / mapper / util |
| DAL | DO + Mapper | Mapper 继承 `BaseMapperX<T>`;查询优先 `LambdaQueryWrapperX` 与默认方法封装;非必要不回退 XML不承担领域校验 |
| Convert | 已有 `convert` 风格继续沿用,简单场景直接 `BeanUtils` | 不要强推全员 MapStruct也不要反过来把已有 convert 全删 |
## 认证与跨模块调用
- 默认沿用 OAuth2 / Token / `LoginUser` / `login-user` 透传主链。**不要**另造 ThreadLocal / Session / 自定义 header。
- 业务逻辑落 `*-boot`;可复用契约落 `*-api`;可复用框架能力落 `rdms-framework`。跨模块/跨服务必须通过 `*-api` 定义契约,**不要直接依赖别人的 `*-boot`**;改跨模块 API 时,`*-boot` 实现与对应 `*-api` 契约同步更新。
### 鉴权:必须按"全域 / 对象域"分通道挂
系统有**两条互不交叉**的权限通道,挂错通道 = 永远 403。新增/修改接口前必须先判断它属于哪一域:
| 通道 | 适用场景 | 注解 / 实现 | 角色与菜单 |
|---|---|---|---|
| **全域 global** | 传统 RBAC 顶层菜单与"项目管理界面"——选择对象**之前**的所有动作(建项目、列项目、菜单/角色/用户管理等) | Controller 上 `@PreAuthorize("@ss.hasPermission('xxx')")`,由 `PermissionServiceImpl.hasAnyPermissions` 处理 | `system_role.scope_type='global'` + `system_menu.scope_type='global'` |
| **对象域 object** | 用户**已选择某个对象**(如某个项目/产品)后,对象内部的一切操作(任务、执行、工时、协办人、需求、成员维护等) | Service 上 `@CheckObjectPermission(objectType=..., objectId="#xxxId", permission="...")`,由 `ObjectPermissionAspect``ObjectPermissionService.checkPermission` 处理 | `system_role.scope_type='object'` + `system_menu.scope_type='object'` + `object_type` |
红线:
- **对象内接口绝不能挂 `@PreAuthorize("@ss.hasPermission(...)")`**。该注解走的链路在 `PermissionServiceImpl` 里强制按 GLOBAL 取角色line 343-347+ 强制按 GLOBAL 查菜单line 92-94对象域角色与对象域菜单都进不来即使授权配置完全正确也必然 403。
- **对象域权限校验必须落在 Service 层 `@CheckObjectPermission`**,原因:路径里 `objectId` 通常以 `#projectId`/`#productId` 等 SpEL 解析Controller 的参数校验前置阶段不便复用;与同模块(`ProjectMemberServiceImpl` / `ProjectExecutionServiceImpl` / `ProjectExecutionAssigneeServiceImpl` / `ProjectTaskServiceImpl`)保持一致。
- **同一接口不要两条通道叠加**。要么全域,要么对象域;叠加只会让对象域用户被全域那条卡死。
- 列表/详情这类对象内**读路径**目前未挂 `@CheckObjectPermission`(属已识别负债,台账 TD-001新增读接口暂沿用现状即可不要顺手改造等独立立项。
判定口诀:**URL 里有 `{projectId}` / `{productId}` 等对象 ID → 对象域;没有 → 全域**。
## 接口语义(HTTP 动词)
本仓库 update 类接口默认按 RESTful 标准用 HTTP 动词区分语义,前后端必须按下表对齐,避免"前端没传字段"和"前端想清空"在后端无法区分的歧义。
| 动词 | 语义 | 字段处理规则 |
|---|---|---|
| **PUT** | 全资源替换 | 前端必须把表单完整状态回传(读到的非必填字段也要原样回传)。后端按字段值落库:**有值=更新,`null`=清空**。DO 字段加 `@TableField(updateStrategy = FieldStrategy.ALWAYS)` 跳过全局 `NOT_NULL``null` 真的写库 |
| **PATCH** | 部分字段更新 | **本仓库暂不引入 PATCH 接口**。如果有"只改一两个字段"的需求,用专门的子动作接口(参考 `assignees/{id}/inactive``status` 这种语义化路径),不要在 update 接口里靠旁路标记(如 `clearXxx: true`)模拟 PATCH |
| **DELETE** | 资源删除 | 软删走全局 `deleted` 列,不需要参数 body |
红线:
- **不要在 update 类接口的 ReqVO 里加 `clearXxx: Boolean` 这种旁路标记**来模拟 PATCH —— 等于承认接口是"伪 PATCH",会让所有非必填字段都需要类似标记,长期污染 API 设计。需要部分更新就拆子动作接口。
- 新增 update 接口时,必须在 API 文档对应章节明示"PUT 全字段回传"约定;DO 上对允许 null 的字段补 `FieldStrategy.ALWAYS` 注解,并加注释说明语义来源(指向本节)。
- 历史接口若是稀疏 PATCH 风格(传 null = 不动),保留现状但不要拓展;遇到清空诉求时按 PUT 方向重构。
## 数据与 SQL
- 新表 DO 复用现有 `BaseDO` / 审计字段风格,不要再引一套审计基类(除非该表本身明确不需要逻辑删除)。
- **不要假设运行时自动数据库迁移**:依赖新表/新字段/新索引时,必须同步补 SQL 脚本与文档。
- SQL 放在目标模块 `src/main/resources/sql/...`,可审阅、可单独执行。
- 缓存/日志/审计变更优先沿用既有机制,不要绕开登录上下文与审计字段填充。
### 种子 SQL纯 SQL INSERT 雪花 ID 表)
`system_dict_type` / `system_dict_data` / `system_menu` 等历史表 id 由 MyBatis-Plus 雪花算法在 Java 层生成DDL 无 `AUTO_INCREMENT`。纯 SQL 路径(字典种子、菜单种子等)写 INSERT 必须显式提供 id否则 MySQL 报 `1364 - Field 'id' doesn't have a default value`
- **id 取值**`SET @new_id = (SELECT IFNULL(MAX(id), 0) + 1 FROM xxx_table);` 然后 INSERT `SELECT @new_id, ...`。雪花 ID 单调递增,`MAX+1` 落在已用区间之后,不会与未来 Java 生成的新雪花 ID 冲突。
- **多条连续 INSERT**:每条 INSERT **前重新取** `MAX+1`——不要用 `base+1 / +2 / +3` 一次性算多个。配合 `NOT EXISTS` 守卫,部分已存在场景(半路重跑)才不会出现两条共用一个 id。
- **collation 1267 陷阱**:仓库历史表 collation 不统一(如 `system_dict_data``utf8mb4_unicode_ci`,新表 `rdms_task_worklog``utf8mb4_0900_ai_ci`)。**不要**用 `SET @t = 'xxx'` 存字符串再 `WHERE col = @t`——用户变量自带连接级 collation与列 collation 撞会报 `1267 Illegal mix of collations (utf8mb4_unicode_ci,IMPLICIT) and (utf8mb4_0900_ai_ci,IMPLICIT) for operation '='`。**对策**直接展开成字面值MySQL 字面值会按列 collation 隐式解析,不冲突。
样板参考:`docs/sql/rdms_task_worklog.sql:47-50`(菜单种子)+ `docs/sql/rdms_worklog_difficulty_seed.sql`(字典种子)。
## 注释与编码
- 关键字段/分支/约束/非直观实现补**简洁中文**注释;中文写入必须 UTF-8不要用"改成英文"规避乱码。
- superpowers 产出的功能文档(设计/实施/联调默认中文落地代码标识、文件路径、接口路径、SQL、命令保持原样不意译。
## 文档输出格式
- 新写文档默认输出 **HTML 格式**(便于浏览器直接打开、自带样式)。
- 例外:`docs/superpowers/` 下保持 markdown工作流约定
- 历史已有的 markdown 文档不强制迁移;只有新写的按 HTML。
## 工作规则(执行前对照)
1. 优先做有边界的模块内改动,避免跨模块扩散。
2. **不要修改** `application-local.yaml` / `application-dev.yaml`,除非任务本身就是环境配置调整;改前先查 git 状态。
3. 新增共享能力优先扩展现有 `rdms-spring-boot-starter-*`,不要在业务服务里重复堆配置。
4. **未经用户明确同意,不执行任何 `mvn`、启动命令、脚本等会实际运行项目的命令。**
## Git 操作纪律
### 默认不引导分支管理(**首要**
用户在本仓库长期固定在 `main` 上工作。开发流程中:
- **不要主动建议建 feature 分支**`git checkout -b feat/xxx``git switch -c ...`)。
- **不要把"先切到 xxx 分支再操作"作为方案前置步骤**。
- 一切围绕 `main` 展开:直接在 `main` 上改、`main` 上提交、`main` 上推。
- 例外:用户明确要求建分支、或涉及多人协作 / PR 评审 / 大规模重构(此时仍只是"提一句作为可选",不强推)。
**理由**:这次差点丢用户 3 天工作的事故,根因就是分支管理本身——某次操作意外把文件名当成分支名(建出 `用户行动清单.md` 分支),后续"切回 main + `git branch -D` 删怪分支"流程里就把未推送 commit `8bad989` 干掉了。**少走分支 = 少埋雷**。
### 破坏性 git 命令必须先核实
任何**会丢工作**的 git 命令——`branch -D``reset --hard``clean -fd``push --force` / `--force-with-lease``checkout` / `switch` 带未提交改动、`rebase` 在已推送分支上、直接动 `.git/` 内部文件(`refs/``HEAD``packed-refs`)——**给出建议前必须先核实**,不得凭"看起来安全"就甩命令:
1. **目标 ref 上是否有未推送 / 未合并 commit**:让用户跑 `git log --oneline -5 <ref>``git log <主线>..<ref>` 把输出贴回来。
2. **工作区是否干净**`git status`
3. **先挂救生圈**:建议用 `git tag backup-xxx <sha>` 锁定当前 SHA**再执行**破坏性命令。
4. **明示翻车回滚路径**:例如"如果不对,`git reset --hard backup-xxx` 即可回到此处"。
## 验证默认动作
先定义验证方式,再做修改。默认静态验证:
- 调用链是否闭环、是否符合模块边界
- 配置项 / 接口契约 / 权限标识 / 路由 / 资源注册前后是否一致
- 改动是否控制在最小集合
- 文档 / SQL / 配置 / 接口说明是否需要同步更新
如果改动涉及 Spring 配置、序列化、安全、路由、RPC 契约、MyBatis 行为或跨模块 API必须**显式区分**哪些是已静态检查、哪些尚未实际运行验证。
## 给后续我自己的提醒
- 仓库可能有未提交的本地改动,不要顺手覆盖与当前任务无关的编辑。
- `docs/` 是当前工作上下文的一部分,不是归档;架构级修改前先查阅 [`docs/README.md`](./docs/README.md)。
-`pom.xml` 统一版本与依赖;版本调整改根 pom不要散落到子模块。
- 推荐使用 `Glob` / `Grep` / `Read` 等专用工具,避免用 Bash 做文件搜索/读取/编辑。