后端开发规范SKILL


name: "springboot-mybatis-plus-standards"

description: "SpringBoot + MyBatis-Plus 后端开发规范,约束大模型生成符合团队标准的 Java 后端代码。当用户要求编写 SpringBoot 后端代码、生成 CRUD 接口、创建实体/Service/Controller 或任何涉及后端开发任务时必须遵循此规范。"

SpringBoot + MyBatis-Plus 后端开发规范

本规范用于约束所有由大模型生成的 SpringBoot + MyBatis-Plus 后端代码,确保代码风格统一、结构清晰、易于维护。


一、项目结构规范

复制代码
com.example.project
├── common/                    # 公共模块
│   ├── config/               # 全局配置类
│   ├── exception/            # 全局异常处理
│   ├── result/               # 统一响应体
│   └── utils/                # 工具类
├── module/                    # 业务模块(按领域拆分)
│   ├── user/                 # 用户模块
│   │   ├── controller/       # 控制器
│   │   ├── service/          # 服务接口
│   │   │   └── impl/         # 服务实现
│   │   ├── mapper/           # 数据访问层
│   │   ├── entity/           # 数据库实体(PO)
│   │   └── model/            # DTO/VO/Query
│   └── order/                # 订单模块(同上结构)
└── Application.java          # 启动类

强制规则:

  • 必须按业务模块分包,禁止按层分包(如所有 controller 放一个包)。
  • 每个模块内部结构:controller / service + impl / mapper / entity / model
  • 公共代码放在 common 包下,不允许跨模块直接引用 entity。
  • 启动类必须放在根包下,使用 @SpringBootApplication

二、命名规范

类型 命名规则 示例
实体类(PO) XxxEntityXxx UserEntity / User
数据传输对象 XxxDTO UserSaveDTOUserUpdateDTO
视图对象 XxxVO UserVOUserDetailVO
查询对象 XxxQueryXxxPageQuery UserPageQuery
Mapper 接口 XxxMapper UserMapper
Service 接口 XxxService UserService
Service 实现 XxxServiceImpl UserServiceImpl
Controller XxxController UserController
配置类 XxxConfig MyBatisPlusConfig
工具类 XxxUtilXxxUtils DateUtil
枚举类 XxxEnum UserStatusEnum

方法命名:

  • Controller 方法名与操作语义一致:list() / detail() / save() / update() / delete()
  • Service 方法名与业务语义一致,禁止使用 addData() / doSomething() 等模糊命名
  • Mapper 自定义方法名遵循 MyBatis-Plus 风格:selectXxxByCondition() / updateXxxById()
  • 布尔查询方法以 is / has / exists 开头

三、实体类规范(Entity)

java 复制代码
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_user")  // 必须用 @TableName
public class UserEntity {

    @TableId(type = IdType.AUTO)  // 主键必须明确策略
    private Long id;

    /** 用户名 */
    private String username;

    /** 密码(不返回给前端) */
    @TableField(select = false)   // 敏感字段查询时不返回
    private String password;

    /** 逻辑删除标记(0-未删除 1-已删除) */
    @TableLogic                      // 逻辑删除必须加
    private Integer deleted;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;

    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
}

强制规则:

  • 使用 Lombok @Data,禁止手写 getter/setter。
  • 表名必须通过 @TableName 显式指定,表名格式为 t_xxx(如 t_user)。
  • 主键使用 @TableId 显式声明主键策略:IdType.AUTO(自增)或 IdType.ASSIGN_ID(雪花)。
  • 数据库必须存在 create_timeupdate_time 字段 ,实体中对应 LocalDateTime,配合自动填充。
  • 涉及删除操作必须使用逻辑删除 ,字段 deleted(0=未删除,1=已删除),加 @TableLogic
  • 密码等敏感字段使用 @TableField(select = false),防止查询时泄露。
  • 禁止在 Entity 中使用 @JsonFormat 等序列化注解,那是 VO 的职责。

四、DTO / VO / Query 规范

DTO(数据传输对象):仅用于接收请求参数

java 复制代码
@Data
public class UserSaveDTO {

    @NotBlank(message = "用户名不能为空")
    @Size(min = 3, max = 20, message = "用户名长度3-20位")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 6, max = 20, message = "密码长度6-20位")
    private String password;
}

VO(视图对象):仅用于返回数据给前端

java 复制代码
@Data
public class UserVO {

    private Long id;
    private String username;

    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createTime;
}

Query(查询对象):用于分页/条件查询

java 复制代码
@Data
@EqualsAndHashCode(callSuper = true)
public class UserPageQuery extends PageQuery {

    /** 用户名模糊搜索 */
    private String username;

    /** 状态筛选 */
    private Integer status;
}

强制规则:

  • DTO 参数校验使用 Jakarta Validation 注解(@NotBlank@NotNull@Size 等),message 必须明确。
  • Controller 接收参数统一使用 DTO,禁止直接使用 Entity 接收请求参数。
  • Controller 返回数据统一使用 VO,禁止直接返回 Entity 给前端。
  • 分页查询对象统一继承 PageQuery 基类(见下文)。
  • DTO/VO 之间的转换使用 MapStruct 或在 Service 层手动赋值,禁止在 Controller 调用 Entity 的 setter。

五、Mapper 层规范

java 复制代码
@Mapper
public interface UserMapper extends BaseMapper<UserEntity> {
    // MyBatis-Plus 的 BaseMapper 提供基础 CRUD,一般无需写 XML

    /**
     * 自定义分页查询(多表关联时使用)
     */
    IPage<UserVO> selectPageByCondition(Page<UserEntity> page, @Param("query") UserPageQuery query);
}

强制规则:

  • Mapper 接口必须标注 @Mapper,继承 BaseMapper<Entity>
  • 简单 CRUD 直接使用 MyBatis-Plus 内置方法,禁止重复定义。
  • 仅在多表关联、复杂聚合查询时编写自定义 SQL,SQL 写在 XML 或注解中。
  • 分页查询返回 IPage<VO> 而非 IPage<Entity>,直接用 VO 接收结果。
  • 禁止在 Mapper 层做业务逻辑处理,仅负责数据访问。
  • 禁止使用 ${} 拼接 SQL 变量,一律使用 #{} 防止 SQL 注入。

六、Service 层规范

接口:

java 复制代码
public interface UserService extends IService<UserEntity> {
    IPage<UserVO> pageUser(UserPageQuery query);
    void saveUser(UserSaveDTO dto);
    void updateUser(UserUpdateDTO dto);
}

实现:

java 复制代码
@Service
@RequiredArgsConstructor  // 构造器注入
public class UserServiceImpl extends ServiceImpl<UserMapper, UserEntity> implements UserService {

    private final UserMapper userMapper;

    @Override
    public IPage<UserVO> pageUser(UserPageQuery query) {
        Page<UserEntity> page = query.toPage();
        return userMapper.selectPageByCondition(page, query);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveUser(UserSaveDTO dto) {

        // 1. 业务校验
        if (lambdaQuery().eq(UserEntity::getUsername, dto.getUsername()).exists()) {
            throw new BusinessException("用户名已存在");
        }

        // 2. DTO → Entity
        UserEntity user = new UserEntity();
        user.setUsername(dto.getUsername());
        user.setPassword(BCrypt.hashpw(dto.getPassword(), BCrypt.gensalt()));

        // 3. 入库
        save(user);
    }
}

强制规则:

  • Service 接口继承 IService<Entity>,实现类继承 ServiceImpl<Mapper, Entity>
  • 使用 @RequiredArgsConstructor + final 字段实现构造器注入,禁止使用 @Autowired 字段注入。
  • 增删改操作必须加 @Transactional(rollbackFor = Exception.class)
  • 业务校验在 Service 层完成,Controller 仅负责参数校验和路由。
  • 条件查询优先使用 LambdaQueryWrapper,禁止字符串硬编码字段名:
java 复制代码
// 正确
lambdaQuery().eq(UserEntity::getUsername, username).list()

// 错误
new QueryWrapper<UserEntity>().eq("username", username).list()
  • 批量操作使用 MyBatis-Plus 的 saveBatch() / updateBatchById(),设置合理 batchSize(建议 500)。
  • Entity 之间的转换在 Service 层进行,禁止将 Entity 传递到 Controller 层。

七、Controller 层规范

全部接口统一使用 POST 请求,仅文件下载等流式响应接口可使用 GET。

java 复制代码
@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
@Validated
public class UserController {

    private final UserService userService;

    @PostMapping("/list")
    public Result<IPage<UserVO>> list(@Valid @RequestBody UserPageQuery query) {
        return Result.success(userService.pageUser(query));
    }

    @PostMapping("/detail")
    public Result<UserVO> getById(@Valid @RequestBody IdDTO dto) {
        return Result.success(userService.getByUserId(dto.getId()));
    }

    @PostMapping("/save")
    public Result<Void> save(@Valid @RequestBody UserSaveDTO dto) {
        userService.saveUser(dto);
        return Result.success();
    }

    @PostMapping("/update")
    public Result<Void> update(@Valid @RequestBody UserUpdateDTO dto) {
        userService.updateUser(dto.getId(), dto);
        return Result.success();
    }

    @PostMapping("/delete")
    public Result<Void> delete(@Valid @RequestBody IdDTO dto) {
        userService.deleteUser(dto.getId());
        return Result.success();
    }
}

IdDTO 公共类(用于仅需传 ID 的接口):

java 复制代码
@Data
public class IdDTO {
    @NotNull(message = "ID不能为空")
    private Long id;
}

强制规则:

  • 所有接口统一使用 @PostMapping ,禁止使用 @GetMapping/@PutMapping/@DeleteMapping
  • 文件下载、导出 Excel 等流式响应接口例外,可使用 @GetMapping
  • 使用 @RestController,禁止使用 @Controller + @ResponseBody
  • 路径统一 /api/{module}s/{action},module 用复数形式,action 明确描述操作。
  • 所有返回值必须用 Result<T> 包裹,禁止直接返回实体或 Map。
  • 接收参数必须 @Valid + @RequestBody 校验,禁止使用 @PathVariable 传递业务参数。
  • Controller 类上加 @Validated
  • Controller 层禁止写业务逻辑,仅做参数接收、校验、调用 Service、返回结果。
  • 使用 @RequiredArgsConstructor 构造器注入。
  • 接口路径规范:
    • 分页列表:@PostMapping("/list")
    • 查询详情:@PostMapping("/detail")
    • 新增:@PostMapping("/save")
    • 修改:@PostMapping("/update")
    • 删除:@PostMapping("/delete")

八、统一响应体 Result<T>

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result<T> {

    private Integer code;
    private String message;
    private T data;

    public static <T> Result<T> success() {
        return new Result<>(200, "操作成功", null);
    }

    public static <T> Result<T> success(T data) {
        return new Result<>(200, "操作成功", data);
    }

    public static <T> Result<T> error(Integer code, String message) {
        return new Result<>(code, message, null);
    }

    public static <T> Result<T> error(String message) {
        return new Result<>(500, message, null);
    }
}
  • 所有接口返回 Result<T>,HTTP 状态码始终 200,业务状态码在 body 中区分。
  • 成功统一返回 code=200,业务异常通过 BusinessException + 全局异常处理器处理。
  • 禁止在 Controller 中 try-catch 包装返回;异常统一由 @RestControllerAdvice 处理。

九、全局异常处理

java 复制代码
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    /** 参数校验异常 */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<Void> handleValid(MethodArgumentNotValidException e) {
        String msg = e.getBindingResult().getFieldErrors().stream()
                .map(FieldError::getDefaultMessage)
                .collect(Collectors.joining("; "));
        return Result.error(400, msg);
    }

    /** 业务异常 */
    @ExceptionHandler(BusinessException.class)
    public Result<Void> handleBusiness(BusinessException e) {
        log.warn("业务异常: {}", e.getMessage());
        return Result.error(e.getCode(), e.getMessage());
    }

    /** 兜底异常 */
    @ExceptionHandler(Exception.class)
    public Result<Void> handleException(Exception e) {
        log.error("系统异常", e);
        return Result.error(500, "服务器内部错误");
    }
}
java 复制代码
// BusinessException
public class BusinessException extends RuntimeException {
    private Integer code = 500;

    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(Integer code, String message) {
        super(message);
        this.code = code;
    }

    public Integer getCode() { return code; }
}

十、分页查询规范

java 复制代码
@Data
public class PageQuery {

    @Min(value = 1, message = "页码最小为1")
    private Integer pageNum = 1;

    @Min(value = 1, message = "每页至少1条")
    @Max(value = 100, message = "每页最多100条")
    private Integer pageSize = 10;

    /** 转为 MyBatis-Plus Page 对象,关闭 count 总数时调用 toPage(false) */
    public <T> Page<T> toPage() {
        return new Page<>(pageNum, pageSize);
    }

    public <T> Page<T> toPage(boolean searchCount) {
        return new Page<>(pageNum, pageSize, searchCount);
    }
}

强制规则:

  • 所有分页查询参数类必须继承 PageQuery
  • 分页参数默认值:pageNum=1pageSize=10
  • 必须限制 pageSize 上限(≤100),防止全量导出拖垮数据库。
  • SQL 中使用 ORDER BY create_time DESC 确保分页数据一致性。

十一、MyBatis-Plus 配置

java 复制代码
@Configuration
public class MyBatisPlusConfig {

    /** 分页插件 */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }

    /** 自动填充创建时间/更新时间 */
    @Bean
    public MetaObjectHandler metaObjectHandler() {
        return new MetaObjectHandler() {
            @Override
            public void insertFill(MetaObject metaObject) {
                this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, LocalDateTime.now());
                this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
            }

            @Override
            public void updateFill(MetaObject metaObject) {
                this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
            }
        };
    }
}

强制规则:

  • 必须配置分页插件 PaginationInnerInterceptor,否则分页不生效。
  • 必须配置 MetaObjectHandler 自动填充 createTimeupdateTime
  • 逻辑删除配置在 application.yml 中:
yaml 复制代码
mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: deleted
      logic-delete-value: 1       # 已删除
      logic-not-delete-value: 0   # 未删除

十二、通用禁止项

  1. 禁止字段注入 :不允许 @Autowired 字段注入,统一构造器注入。
  2. 禁止魔法值 :状态、类型等固定值必须用枚举类,禁止代码中直接写 1/2/"A"/"B"
  3. 禁止 Entity 直连 Controller:Entity → DTO/VO 转换在 Service 层完成。
  4. 禁止在循环中操作数据库 :批量场景使用 MyBatis-Plus saveBatch / updateBatchById
  5. 禁止 try-catch 吞异常 :异常要么向上抛,要么记录日志后抛 BusinessException
  6. 禁止 SQL 字符串拼接 :防止 SQL 注入,占位符只用 #{}
  7. 禁止 SELECT *:自定义 SQL 必须明确列出需要的字段。
  8. 禁止 @RequestMapping 不指定 method :必须使用 @GetMapping / @PostMapping 等明确 HTTP 方法。

十三、依赖版本参考

xml 复制代码
<!-- Spring Boot 3.x + JDK 17+ -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.0</version>
</parent>

<properties>
    <java.version>17</java.version>
    <mybatis-plus.version>3.5.7</mybatis-plus.version>
    <mapstruct.version>1.5.5.Final</mapstruct.version>
</properties>

<!-- MyBatis-Plus -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>${mybatis-plus.version}</version>
</dependency>

<!-- Lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

<!-- Validation -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

十四、代码生成速查

场景 正确写法
条件查询 lambdaQuery().eq(Entity::getField, value).list()
分页查询 baseMapper.selectPage(page, wrapper)
单一条件更新 lambdaUpdate().eq(Entity::getId, id).set(Entity::getField, value).update()
批量插入 saveBatch(list, 500)
分组聚合 自定义 Mapper + XML
存在性检查 lambdaQuery().eq(Entity::getField, value).exists()
计数 lambdaQuery().eq(Entity::getField, value).count()

生成代码时必须逐条对照本规范检查,不符合的代码视为不合格。

相关推荐
子兮曰9 分钟前
OpenMontage 深度解剖:你的 AI 编程助手,其实是个视频工作室
前端·后端·ai编程
Hyyy1 小时前
Function Calling / Tool Use的原理和实现模式
前端·llm·ai编程
刘棕霆1 小时前
24—AI Skill 测评工作流工具箱化:为什么 regression 会自然出现
aigc·ai编程·测试
leeyi4 小时前
Callback 系统:给 Agent 管道装上“监听器“
aigc·agent·ai编程
Momo__4 小时前
MDN MCP Server——Mozilla 把 Web 文档接进 AI Agent,从此 LLM 不再瞎编 API
前端·ai编程·mcp
kyriewen6 小时前
折腾了半年 AI 编程工作流,最后发现效率瓶颈是桌上那块屏幕
前端·javascript·ai编程
猪猪拆迁队6 小时前
虚拟工厂仿真引擎的架构设计:让一条产线可编程、可观测、可干预
后端·ai编程
ZzT7 小时前
让 AI 少写一半代码:拆解爆火的 ponytail
ai编程·claude
Bigger7 小时前
从零搭建 AI 代码审查服务:一份前端也能看懂的 Python 学习笔记
前端·ci/cd·ai编程
我不是外星人8 小时前
我把 Claude Code 搬到网页!自研高颜值 Web 交互工作台
前端·ai编程·claude