docs: 删除工单需求规格文档并更新开发规范

- 删除了工单需求规格说明文档 2026-05-22-ticket-design.md
- 在安全注解 CheckObjectPermission 中新增 accessible 参数配置
- 更新 CLAUDE.md 开发规范文档,补充 MySQL 客户端使用说明
- 优化错误码常量中的错误消息格式,使用中文状态和操作名称
- 修复权限拒绝提示消息,提供更友好的用户提示
- 更新开发规范关于演示库同步补丁和文档输出格式的要求
This commit is contained in:
2026-06-04 18:46:41 +08:00
parent f23f1930e9
commit f13286aaff
50 changed files with 2072 additions and 450 deletions

View File

@@ -16,6 +16,7 @@
- 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`](./docs/agent/MySQL客户端连库手册.md)。
## 仓库结构
@@ -48,7 +49,6 @@
不要:
- 把后续业务长期堆进 `rdms-system`
- 为新增子域引入一套平行的 `application/domain/infrastructure/adapter` 分层。
- 让外部模块直接依赖 `*-boot` 的 service 或 mapper必须走 `*-api`)。
## 分层职责
@@ -64,7 +64,7 @@
## 认证与跨模块调用
- 默认沿用 OAuth2 / Token / `LoginUser` / `login-user` 透传主链。**不要**另造 ThreadLocal / Session / 自定义 header。
- 业务逻辑落 `*-boot`;可复用契约落 `*-api`;可复用框架能力落 `rdms-framework`。跨模块/跨服务必须通过 `*-api` 定义契约,**不要直接依赖别人 `*-boot`**;改跨模块 API 时,`*-boot` 实现与对应 `*-api` 契约同步更新。
- 业务逻辑落 `*-boot`;可复用契约落 `*-api`;可复用框架能力落 `rdms-framework`。跨模块/跨服务必须通过 `*-api` 定义契约,**不要直接依赖别人 `*-boot` 的 service 或 mapper必须走 `*-api`**;改跨模块 API 时,`*-boot` 实现与对应 `*-api` 契约同步更新。
### 鉴权:必须按"全域 / 对象域"分通道挂
@@ -80,7 +80,7 @@
- **对象内接口绝不能挂 `@PreAuthorize("@ss.hasPermission(...)")`**。该注解走的链路在 `PermissionServiceImpl` 里强制按 GLOBAL 取角色line 343-347+ 强制按 GLOBAL 查菜单line 92-94对象域角色与对象域菜单都进不来即使授权配置完全正确也必然 403。
- **对象域权限校验必须落在 Service 层 `@CheckObjectPermission`**,原因:路径里 `objectId` 通常以 `#projectId`/`#productId` 等 SpEL 解析Controller 的参数校验前置阶段不便复用;与同模块(`ProjectMemberServiceImpl` / `ProjectExecutionServiceImpl` / `ProjectExecutionAssigneeServiceImpl` / `ProjectTaskServiceImpl`)保持一致。
- **同一接口不要两条通道叠加**。要么全域,要么对象域;叠加只会让对象域用户被全域那条卡死。
- 对象内**读路径**(列表 / 详情 / 状态看板 / 聚合)已统一在 **Service 层**挂 `@CheckObjectPermission(objectType=PROJECT, permission=...PERMISSION_QUERY)`——查询同样扫库耗资源,必须按对象域鉴权(原台账 TD-001 所述"读路径未挂"已不成立)。**Controller 方法层一律不挂权限注解**,对象域鉴权全部落 Service新增读接口照此在 Service 层挂对象域权限不要只在 Controller 留空更不要误判"Controller 没注解 = 无鉴权"。
- 对象内**读路径**(列表 / 详情 / 状态看板 / 聚合)同样要`@CheckObjectPermission(objectType=PROJECT, permission=...PERMISSION_QUERY)`——查询同样扫库耗资源,必须按对象域鉴权。**Controller 方法层一律不挂权限注解**新增读接口照此在 Service 层挂对象域权限不要只在 Controller 留空更不要误判"Controller 没注解 = 无鉴权"。
判定口诀:**URL 里有 `{projectId}` / `{productId}` 等对象 ID → 对象域;没有 → 全域**。
@@ -100,8 +100,32 @@
- 新增 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`](./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/...`,可审阅、可单独执行。
@@ -109,13 +133,13 @@
### 种子 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`
`system_dict_type` / `system_dict_data` / `system_menu` 等历史表 id 由 MyBatis-Plus 雪花算法在 Java 层生成DDL 无 `AUTO_INCREMENT`。纯 SQL 路径(字典种子、菜单种子、演示库补丁)写 INSERT 必须遵守三条红线,否则报错或撞库:
- **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 隐式解析,不冲突
- **必须显式给 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_task_worklog.sql:47-50`(菜单种子)+ `docs/sql/rdms_worklog_difficulty_seed.sql`(字典种子)
详细写法、示例与样板(`docs/sql/rdms_project_query_permission.sql`)见 [`docs/agent/种子SQL写法规范.md`](./docs/agent/种子SQL写法规范.md)
## 注释与编码
@@ -125,9 +149,29 @@
## 文档输出格式
- 新写文档默认输出 **HTML 格式**(便于浏览器直接打开、自带样式)。
- 例外:`docs/superpowers/` 下保持 markdown(工作流约定)。
- 例外:`docs/superpowers/``docs/agent/` 下保持 **markdown**——前者是工作流约定,后者是专给 Agent 看的操作手册Agent 读 md 更直接,不需要浏览器样式)。
- 历史已有的 markdown 文档不强制迁移;只有新写的按 HTML。
### 产出落点(每次生成前对照,避免散乱)
**红线:绝不把生成物放在仓库根或 `docs/` 根目录**——任何文档 / SQL / 草稿必须落到 `docs/` 下对应子目录(完整目录见 [`docs/README.md`](./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 的文件表。
## 工作规则(执行前对照)
1. 优先做有边界的模块内改动,避免跨模块扩散。