7.8 KiB
7.8 KiB
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 |
// ❌ 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接口" | 写业务含义,不写技术废话 |
注释示例
/** 任务服务,负责任务的创建、分配、状态流转 */
@Service
public class TaskServiceImpl implements TaskService {
/** 单次批量操作上限 */
private static final int BATCH_LIMIT = 100;
/**
* 根据状态和截止时间查询待处理任务。
* 先查 Redis 缓存,未命中再查 MySQL。
*
* @param status 任务状态,不能为 null
* @param deadline 截止时间,只查此时间之前的任务
* @return 任务列表,可能为空
*/
@Override
public List<Task> listPending(String status, LocalDateTime deadline) {
// 缓存 key: pending:{status}
String cacheKey = "pending:" + status;
List<Task> cached = cacheService.get(cacheKey);
if (cached != null) return cached;
// 查库(这里用复合索引 idx_status_deadline)
List<Task> 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 模板(填空式)
// ===== 分页列表 =====
@GetMapping("/list")
public Result<IPage<Xxx>> list(@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
Page<Xxx> p = new Page<>(page, size);
// [填空] 构建查询条件
return Result.ok(service.page(p, wrapper));
}
// ===== 详情 =====
@GetMapping("/{id}")
public Result<Xxx> 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 同步所有前端调用方
- 新增/改字段 → 已更新数据库脚本
- 无禁止清单中的违规项