# 02 — 后端规则 > **适用**:所有后端 AI 开发任务。 --- ## 分层不变式 ``` Controller → 参数校验 + 调 Service + 返回统一格式 禁止:写业务逻辑、直接调 Mapper、处理事务 Service → 业务逻辑 + 事务管理 禁止:处理 HTTP 请求/响应、直接操作 Request/Response Mapper → 数据访问(MyBatis-Plus BaseMapper) 禁止:写业务逻辑、调用其他 Mapper Entity → 数据映射(@TableName 对应表名) 禁止:写业务方法、放非持久化字段(放 DTO/VO) ``` ### 包结构 ``` com.ghb.base/ ├── system/ # 系统模块(不改) │ ├── entity/ SysUser, SysRole, SysMenu... │ ├── controller/ │ └── service/ └── business/ # 业务模块 ← 你的代码放这里 ├── entity/ # 继承 JeecgBoot 的 BaseEntity ├── controller/ # 继承 JeecgController ├── service/ # impl/ 继承 ServiceImpl ├── mapper/ # 继承 BaseMapper │ └── xml/ # MyBatis XML(复杂查询才需要) └── vo/ # 请求/响应 DTO (*VO, *Request, *ImportRow) ``` --- ## 命名规则 | 元素 | 规则 | 示例(正确 → 错误) | |------|------|---------------------| | **类名** | 大驼峰,名词 | `TaskController` `OrderService` `UserMapper` | | **方法名** | 小驼峰,动词开头 | `getById()` `saveTask()` `listByStatus()` | | **变量** | 小驼峰,有意义 | `taskList` `userId` → ❌ `list` `id` `data` | | **包名** | 全小写,点分隔 | `com.xxx.server.controller` | | **Entity** | 大驼峰,对应表名 | 表 `task_apply` → 类 `TaskApply` | | **DTO** | 大驼峰,后缀 DTO/VO/Request | `TaskCreateRequest` `UserListVO` | | **常量** | 全大写,下划线分隔 | `MAX_RETRY_COUNT` `DEFAULT_PAGE_SIZE` | | **SQL 表名** | 小写,下划线分隔 | `task_apply` `user_profile` | | **SQL 字段** | 小写,下划线分隔 | `create_time` `task_id` `del_flag` | | **URL 路径** | 小写,短横线或斜杠 | `/app/task/list` → ❌ `/app/Task/GetList` | | **配置文件** | kebab-case 或小写 | `application-prod.yml` | ### 长度控制 类名、方法名、表名不要过长。超过 3 个单词或 30 个字符时,用通用缩写。 **前缀也要缩**:模块前缀用一个单词,别堆多个词。 | 完整 | 缩写 | JeecgBoot 实际案例 | |------|------|-------------------| | Department | Dept | `SysDepartRolePermission` → `SysDeptRolePerm` | | Permission | Perm | 同上 | | Announcement | Notice | `SysAnnouncementSend` → `SysNoticeSend` | | Enterprise | Ent | `WechatEnterprise` → `WxEnt` | | Message | Msg | `SysMessageTemplate` → `SysMsgTemplate` | ```java // ❌ JeecgBoot 原版 — 太长 SysDepartRolePermissionServiceImpl // 35 字符 SysAnnouncementSendServiceImpl // 30 字符 ThirdAppWechatEnterpriseServiceImpl // 35 字符 // ✅ 缩写后 SysDeptRolePermServiceImpl // 26 字符 SysNoticeSendServiceImpl // 25 字符 ThirdAppWxEntServiceImpl // 27 字符 ``` **原则**:优先用全称,超长再缩写。缩写必须一眼能认出含义,不自造别人看不懂的缩写。 --- ## 注释规则 | 位置 | 要求 | 示例 | |------|------|------| | **类/接口** | 必须有 Javadoc,说明职责 | `/** 任务管理 Controller,处理任务的增删改查 */` | | **公共方法** | 必须有 Javadoc,说明入参/返回/异常 | `/** @param taskId 任务ID @return 任务详情 @throws 无 */` | | **复杂逻辑** | 行内注释说明"为什么这么做" | `// 先查缓存再查库,避免击穿` | | **常量** | 必须有注释说明含义 | `/** 最大重试次数 */` | | **TODO/FIXME** | 必须有负责人和日期 | `// TODO(yaoshuli 2026-06) 后续改为配置项` | | **禁止** | ❌ 注释写"做了什么"(代码本身已说明) | `// 查询任务列表` ← 删掉 | | **禁止** | ❌ 注释掉的旧代码 | 直接删,Git 有历史 | | **禁止** | ❌ 无意义注释 | `// 定义一个变量` `// 返回结果` | | **禁止** | ❌ Controller 方法加 Javadoc 说"这是xxx接口" | 写业务含义,不写技术废话 | ### 注释示例 ```java /** 任务服务,负责任务的创建、分配、状态流转 */ @Service public class TaskServiceImpl implements TaskService { /** 单次批量操作上限 */ private static final int BATCH_LIMIT = 100; /** * 根据状态和截止时间查询待处理任务。 * 先查 Redis 缓存,未命中再查 MySQL。 * * @param status 任务状态,不能为 null * @param deadline 截止时间,只查此时间之前的任务 * @return 任务列表,可能为空 */ @Override public List listPending(String status, LocalDateTime deadline) { // 缓存 key: pending:{status} String cacheKey = "pending:" + status; List cached = cacheService.get(cacheKey); if (cached != null) return cached; // 查库(这里用复合索引 idx_status_deadline) List tasks = taskMapper.selectByStatusBefore(status, deadline); cacheService.set(cacheKey, tasks, 300); return tasks; } } ``` ## 新增功能标准步骤 ``` 1. 建表 SQL → [项目数据库脚本目录] 2. Entity → 表映射 3. Mapper → 继承 BaseMapper 4. Service 接口+实现 → 业务逻辑 5. Controller → 接口暴露 6. 编译验证 → 构建通过 ``` --- ## CRUD 模板(填空式) ```java // ===== 分页列表 ===== @GetMapping("/list") public Result> list(@RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) { Page p = new Page<>(page, size); // [填空] 构建查询条件 return Result.ok(service.page(p, wrapper)); } // ===== 详情 ===== @GetMapping("/{id}") public Result detail(@PathVariable Long id) { Xxx entity = service.getById(id); if (entity == null) return Result.error("[资源]不存在"); return Result.ok(entity); } // ===== 新增 ===== @PostMapping public Result create(@RequestBody @Valid XxxDto dto) { // [填空] DTO → Entity 转换 service.save(entity); return Result.ok(); } // ===== 更新 ===== @PutMapping("/{id}") public Result update(@PathVariable Long id, @RequestBody @Valid XxxDto dto) { Xxx entity = service.getById(id); if (entity == null) return Result.error("[资源]不存在"); // [填空] 字段更新 service.updateById(entity); return Result.ok(); } ``` --- ## 禁止清单 | 类别 | ❌ 禁止 | |------|--------| | **分层** | Controller 里写 if/else 业务判断 | | **分层** | Controller 直接调 Mapper/Repository | | **分层** | Entity 里写业务方法 | | **数据** | SQL/查询条件字符串拼接 | | **数据** | 查不到不判空,直接返回可能 NPE | | **数据** | `selectOne` / `getOne` 不做唯一性约束 | | **输入** | 入参不校验(该用 `@Valid` 的地方手撸 if) | | **输出** | 返回裸对象、裸字符串、自造格式 | | **值** | 代码里硬编码魔法数字、魔法字符串 | | **事务** | 该加事务的地方漏加 | --- ## 检查表 每完成一个后端任务,逐项自检: - [ ] 编译通过(`mvn compile` / `go build` / 对应构建命令) - [ ] 所有返回值是统一格式,无裸返回 - [ ] 分页接口格式正确(`list/total/pages/current/size`) - [ ] Controller 方法体不超过 20 行(超过说明写了业务逻辑) - [ ] 查不到/null 路径有明确的 `fail` 返回 - [ ] 改了接口 → 已 grep 同步所有前端调用方 - [ ] 新增/改字段 → 已更新数据库脚本 - [ ] 无禁止清单中的违规项