- 删除了工单需求规格说明文档 2026-05-22-ticket-design.md - 在安全注解 CheckObjectPermission 中新增 accessible 参数配置 - 更新 CLAUDE.md 开发规范文档,补充 MySQL 客户端使用说明 - 优化错误码常量中的错误消息格式,使用中文状态和操作名称 - 修复权限拒绝提示消息,提供更友好的用户提示 - 更新开发规范关于演示库同步补丁和文档输出格式的要求
20 KiB
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;否则在该命令上下文中显式切换。 - MySQL 客户端:本机已装命令行客户端
C:\software\mysql-8.4.9-winx64\bin\mysql.exe,Agent 可直接连库跑 SQL(查数据、核对状态、验证库结构),无需另装工具。连接账密不写进本文件,以application-local.yaml的spring.datasource段为准(开发库192.168.1.22:13306/rdms_view);演示库是另一套,账密以用户当场给的为准。读随便跑;写库(DDL/DML)务必先确认是哪套库,开发库改动按本文件§数据与 SQL 出演示库补丁,绝不擅自改演示库。调用细节(PowerShell 必须逐参数加单引号否则 host 被拆、字符集、常用查询)见docs/agent/MySQL客户端连库手册.md。
仓库结构
多模块 Maven 单仓库,Java 17,Spring Boot 3.5.9,根模块打包 pom。
顶层模块:
rdms-system— 系统域(用户/组织/岗位/菜单/角色/权限)rdms-project— RDMS 核心交付域(项目集/项目/产品/需求/任务/工单/执行)rdms-framework— 共享框架与内部 starterrdms-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.SystemServerApplicationrdms-project-boot:com.njcn.rdms.module.project.ProjectServerApplicationrdms-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分层。
分层职责
| 层 | 职责 | 红线 |
|---|---|---|
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的 service 或 mapper(必须走*-api);改跨模块 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(objectType=PROJECT, permission=...PERMISSION_QUERY)——查询同样扫库耗资源,必须按对象域鉴权。Controller 方法层一律不挂权限注解,新增读接口照此在 Service 层挂对象域权限;不要只在 Controller 留空,更不要误判"Controller 没注解 = 无鉴权"。
判定口诀: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 方向重构。
用户可见错误文案(状态机动作 / 状态校验)
凡"状态机动作 / 状态校验失败"的业务异常,若 message 会被前端直接 toast 给用户:不要把动作 / 状态的内部 code 塞进 message(如"不支持动作【complete】")。给用户看中文展示名,技术诊断(原始 code / 入参 / 堆栈)由 infra_api_access_log 承载。
红线:
- service 注入
StatusActionTextResolver(rdms-project-boot · service/status),抛错前用actionName / statusName把 code 翻成中文名再填错误码占位;中文名权威源是rdms_object_status_transition.action_name/rdms_object_status_model.status_name,不在代码里硬编码 code→中文 映射。 - 错误码文案用「{}」占位中文名(正面样板
PERSONAL_ITEM_STATUS_ACTION_NOT_ALLOWED),不要把 code 写死进文案。 - 新对象类型只要在状态机两张表配好
status_name/action_name,resolver 自动生效,无需改代码。
完整规范、落地清单与已知缺口(加班申请 OvertimeApplicationServiceImpl 待补)见 docs/architecture/用户可见错误文案规范.html。来源 TD-012。
数据与 SQL
🔴 演示库同步补丁(上线前最高优先级,每次开发都要做)
项目已临近上线测试,开发库与演示库两套数据库并存:日常开发只改了开发库,演示库要靠 SQL 补丁手工同步。一旦补丁缺失或漏项,演示库就会因数据结构 / 字典 / 权限缺失导致功能异常。因此——
-
每次开发引起的任何数据库变动,都必须同步产出一个可直接运行的 SQL 补丁文件,交给用户拿到演示库执行。覆盖范围:表结构(DDL)、数据字典(
system_dict_type/system_dict_data)、菜单 / 角色 / 权限(system_menu及权限相关表)、新功能初始化数据、索引等——只要开发库动了,补丁就必须有。 -
落地位置与命名:
docs/sql/patches/,命名YYYY-MM-DD-功能名-NN.sql(每次变更一个独立文件;当天同一功能多次变更递增NN序号)。沿用docs/sql/**不主动提交 git 的惯例。 -
可直接执行:纯 SQL、自包含,不依赖 IDE / 迁移工具;用户复制即可在演示库一次跑通。
-
幂等可重复:演示库可能已有部分数据,补丁必须可重复执行不报错(
NOT EXISTS/IF NOT EXISTS守卫;雪花 id 表按下文「种子 SQL」规则显式取 id)。 -
只含增量:仅本次变动,不 dump 全库、不夹带无关数据。
-
补丁写法严格遵守下文「种子 SQL」小节(雪花 id 取值、collation 1267 陷阱)。
-
收尾必做:完成任何涉及 DB 的开发后,主动告诉用户「本次演示库补丁:
docs/sql/patches/xxx.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:用
MAX+1取(SET @new_id = (SELECT IFNULL(MAX(id),0)+1 FROM xxx);),多条连续 INSERT 每条前重新取,否则1364 - Field 'id' doesn't have a default value。 - 必须幂等:每条 INSERT 加
NOT EXISTS守卫,可重复执行不重插。 - 防 collation 1267:不要用用户变量存字符串再比较(
SET @t='xxx'后WHERE col=@t),仓库历史表 collation 不统一会撞1267 Illegal mix of collations;直接展开成字面值。
详细写法、示例与样板(docs/sql/rdms_project_query_permission.sql)见 docs/agent/种子SQL写法规范.md。
注释与编码
- 关键字段/分支/约束/非直观实现补简洁中文注释;中文写入必须 UTF-8,不要用"改成英文"规避乱码。
- superpowers 产出的功能文档(设计/实施/联调)默认中文落地;代码标识、文件路径、接口路径、SQL、命令保持原样不意译。
文档输出格式
- 新写文档默认输出 HTML 格式(便于浏览器直接打开、自带样式)。
- 例外:
docs/superpowers/与docs/agent/下保持 markdown——前者是工作流约定,后者是专给 Agent 看的操作手册(Agent 读 md 更直接,不需要浏览器样式)。 - 历史已有的 markdown 文档不强制迁移;只有新写的按 HTML。
产出落点(每次生成前对照,避免散乱)
红线:绝不把生成物放在仓库根或 docs/ 根目录——任何文档 / SQL / 草稿必须落到 docs/ 下对应子目录(完整目录见 docs/README.md 场景表)。历史教训:工作台 API 文档被丢到仓库根 + docs 根导致散乱。
| 产出类型 | 落点 |
|---|---|
| 业务功能文档(设计 / 前端 API / 实现说明 / 联调) | docs/domains/<域>/(域=system/product/project),平铺、命名 YYYY-MM-DD-功能-类型.html,不按功能建子目录 |
| superpowers 工作流产物(spec / plan) | docs/superpowers/specs/ plans/(保持 markdown) |
| 跨模块约束 / 架构规范 | docs/architecture/ |
| starter / 框架 API 说明 | docs/modules/ |
| 专给 Agent 看的操作手册(从 CLAUDE.md 抽出的长篇 how-to / 命令 / 陷阱) | docs/agent/(CLAUDE.md 留红线 + 指针) |
| 调研 / 对标 / 可行性 | docs/research/ |
| 负债台账 / 排查经验 | docs/debt/ |
| 演示库同步补丁 | docs/sql/patches/(见本文件§数据与 SQL) |
| SQL 草稿 | docs/sql/(最终迁回模块 src/main/resources/sql/) |
| 过程草稿 / 待确认清单 / "看一眼"稿 | docs/temp/,定稿后及时删 |
- 正文格式按上文「文档输出格式」(默认 HTML,
superpowers/与docs/agent/保持 md,历史 md 不强迁)。 - 落点对应的子目录有
README.md时,新增文件同步登记进该 README 的文件表。
工作规则(执行前对照)
- 优先做有边界的模块内改动,避免跨模块扩散。
- 不要修改
application-local.yaml/application-dev.yaml,除非任务本身就是环境配置调整;改前先查 git 状态。 - 新增共享能力优先扩展现有
rdms-spring-boot-starter-*,不要在业务服务里重复堆配置。 - 未经用户明确同意,不执行任何
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)——给出建议前必须先核实,不得凭"看起来安全"就甩命令:
- 目标 ref 上是否有未推送 / 未合并 commit:让用户跑
git log --oneline -5 <ref>或git log <主线>..<ref>把输出贴回来。 - 工作区是否干净:
git status。 - 先挂救生圈:建议用
git tag backup-xxx <sha>锁定当前 SHA,再执行破坏性命令。 - 明示翻车回滚路径:例如"如果不对,
git reset --hard backup-xxx即可回到此处"。
验证默认动作
先定义验证方式,再做修改。默认静态验证:
- 调用链是否闭环、是否符合模块边界
- 配置项 / 接口契约 / 权限标识 / 路由 / 资源注册前后是否一致
- 改动是否控制在最小集合
- 文档 / SQL / 配置 / 接口说明是否需要同步更新
如果改动涉及 Spring 配置、序列化、安全、路由、RPC 契约、MyBatis 行为或跨模块 API,必须显式区分哪些是已静态检查、哪些尚未实际运行验证。
给后续我自己的提醒
- 仓库可能有未提交的本地改动,不要顺手覆盖与当前任务无关的编辑。
docs/是当前工作上下文的一部分,不是归档;架构级修改前先查阅docs/README.md。- 根
pom.xml统一版本与依赖;版本调整改根 pom,不要散落到子模块。 - 推荐使用
Glob/Grep/Read等专用工具,避免用 Bash 做文件搜索/读取/编辑。