后端开发规范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) XxxEntity 或 Xxx UserEntity / User
数据传输对象 XxxDTO UserSaveDTO、UserUpdateDTO
视图对象 XxxVO UserVO、UserDetailVO
查询对象 XxxQuery 或 XxxPageQuery UserPageQuery
Mapper 接口 XxxMapper UserMapper
Service 接口 XxxService UserService
Service 实现 XxxServiceImpl UserServiceImpl
Controller XxxController UserController
配置类 XxxConfig MyBatisPlusConfig
工具类 XxxUtil 或 XxxUtils 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_time、update_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=1,pageSize=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 自动填充 createTime 和 updateTime。
  • 逻辑删除配置在 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()

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

相关推荐
DO_Community1 小时前
AI Agent 运行环境难搭、空闲还收费?MicroVMs 正式公测
人工智能·agent·ai编程·agi
OpsEye2 小时前
企业 AI 管理新需求:不仅要接入模型,还要看懂每一次调用
javascript·ai编程
hoLzwEge2 小时前
AI签到「一劳永逸」
人工智能·程序员·ai编程
Rain的Java大神实战圈2 小时前
被问麻了!Function Calling、MCP、Skill 到底啥关系?一张图讲透三层架构,附 Java 生产落地全流程+30个坑
ai编程
文谦南宁AI普工2 小时前
写了个 PAP/PACT 授权面审计器:把 `poppy.json` 和 PACT 委托链的检查项跑成一条命令
aigc·ai编程
楚楚2512 小时前
2026最新5款AI编程助手基础版免费深度对比
ai编程
fthux3 小时前
编程语言:从机器指令到人类思维的演进史诗
ai编程
Rain的Java大神之路3 小时前
被问麻了!Function Calling、MCP、Skill 到底啥关系?一张图讲透三层架构,附 Java 生产落地全流程+30个坑
java·后端·ai编程
桃西西呀3 小时前
LangGraph4j之三:状态与通道
人工智能·llm·ai编程
桃西西呀3 小时前
LangGraph4j之二:图模型与节点边
人工智能·llm·ai编程