后端开发规范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()

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

相关推荐
人月神话-Lee2 小时前
【图像处理】颜色空间——RGB之外的世界
图像处理·人工智能·ios·ai编程·swift·rgb·颜色空间
ANnianStriver2 小时前
PetLumina 05 — App 端 UI 效果应用
java·ui·ai编程
peterfei3 小时前
ai-agent-scan v1.0.0:基于 MCP 协议的开源 SAST 安全扫描器
安全·ai编程
leeyi3 小时前
Eino 的数据是怎么建模的:Message、ToolCall、流式管道
agent·ai编程
于慨3 小时前
cursor的agents window报错崩溃求解
ai编程
秋天的一阵风3 小时前
AGENTS.md:你的AI代码助手,需要一份"项目说明书"
前端·后端·ai编程
鲁子狄3 小时前
lrnev:让 AI 协作开发「有记忆、可追溯」的项目治理引擎 | 零模型依赖,文件即真相
人工智能·笔记·gpt·ai·ai编程
ANnianStriver4 小时前
PetLumina 09 — 全局日期格式化与通知详情完善
ai·ai编程·路由·日期格式化
恋猫de小郭4 小时前
不需要数学基础,也能理解 LLM 的运作原理
人工智能·aigc·ai编程