Skip to content

开发规范 ​

TIP

规定后端日常开发中的编码风格、分层写法、异常处理、事务管理等约定,适用于 Spring Boot 3 + MyBatis-Plus + Guava 平台 技术栈。

设计原则 ​

原则说明
分层清晰Controller → Service → Repository,禁止跨层调用
接口与实现分离Service 定义接口,impl/ 包下提供实现
统一返回所有接口返回 RestResult 或 IPage,禁止直接返回 PO
异常统一业务异常通过 BizException 抛出,全局处理器统一处理
安全优先参数校验、防注入、敏感信息脱敏

技术栈约定 ​

类别选型说明
框架Spring Boot 3.5.14Java 25,Spring 生态
ORMMyBatis-Plus 3.5.16继承 DBMapper<T>
缓存Redis 8.8.0 + Caffeine二级缓存架构
消息队列RabbitMQ 4.3.2Spring AMQP
注册/配置Nacos 2025.0.0.0服务注册 + 配置中心
限流Sentinel 2025.0.0.0URL 限流、熔断
API 文档Springdoc OpenAPI 2.8.8Knife4j 4.5.0
工具库Hutool 5.8.46字符串、日期、集合工具
日志SLF4J + Logback占位符形式

代码风格 ​

通用 ​

  • 缩进 4 空格,UTF-8 编码
  • 语句末尾加分号
  • 单行长度建议不超过 120 字符
  • 使用 Lombok 简化 POJO 代码(@Data、@Slf4j)

类结构顺序 ​

java
@Tag(name = "admin-sysuser")
@RestController
@RequestMapping("/sysuser/")
@Slf4j
public class SysUserController {

    // 1. 常量(static final)
    private static final String MODULE_NAME = "用户管理";

    // 2. 依赖注入(@Resource / @Autowired)
    @Resource
    private SysUserService sysUserService;

    // 3. 公开接口
    @PostMapping("/findUsers")
    public IPage<Map<String, Object>> findUsers(@RequestBody String params) { }

    // 4. 私有方法
    private void validateParams(Map<String, Object> param) { }
}

Controller 规范 ​

基本结构 ​

java
@Tag(name = "admin-sysuser")
@RestController
@RequestMapping("/sysuser/")
@Slf4j
public class SysUserController {

    @Resource
    SysUserService sysUserService;

    @Operation(summary = "查询->全部用户信息", description = "查询全部用户信息")
    @PostMapping(value = "/findUsers")
    public IPage<Map<String, Object>> findUsers(
            @Parameter(description = "查询条件参数", required = true)
            @RequestBody String params) {
        Page<Map<String, Object>> page = new Page<>();
        String conditions = MyUtil.getConditionsWhere(params, page);
        return sysUserService.findUsers(page, conditions, deptId);
    }
}

接口约定 ​

约定说明
路径风格RESTful,模块前缀 + 操作(/sysuser/findUsers)
请求格式JSON(@RequestBody)
分页参数通过 JSON 内部 pageNo / pageSize 传递
返回格式IPage<Map<String, Object>>(列表)或 Map<String, Object>(单笔)
参数校验使用 AssertMyUtil.notNull() / notEmpty() 校验

禁止事项 ​

  • 禁止 Controller 中写业务逻辑(仅做参数接收、校验、调用 Service)
  • 禁止 Controller 直接调用 Mapper
  • 禁止 返回 PO 实体(应返回 DTO 或 Map)

Service 规范 ​

接口定义 ​

java
public interface SysUserService {
    IPage<Map<String, Object>> findUsers(Page<Map<String, Object>> page, String qc, String deptId);
    Map<String, Object> saveUser(Map<String, Object> param);
    Map<String, Object> updateUser(Long id, Map<String, Object> param);
}

实现类 ​

java
@Service
@Slf4j
public class SysUserServiceImpl implements SysUserService {

    @Resource
    private SysUserMapper sysUserMapper;

    @Override
    public IPage<Map<String, Object>> findUsers(Page<Map<String, Object>> page, String conditions, String deptId) {
        // 业务逻辑
    }
}

事务管理 ​

  • 单表操作无需显式事务
  • 多表/多服务调用使用 @Transactional:
java
@Transactional(rollbackFor = Exception.class)
public void saveUserWithRoles(SysUserPO user, List<Long> roleIds) {
    sysUserMapper.insert(user);
    sysUserRoleMapper.batchInsert(user.getId(), roleIds);
}

Repository 规范 ​

Mapper 接口 ​

继承 DBMapper<T>(Guava 平台封装的 MyBatis-Plus BaseMapper):

java
@Repository
public interface SysUserMapper extends DBMapper<SysUserPO> {

    @SelectProvider(SysUserSqlProvider.class)
    IPage<Map<String, Object>> findUsers(Page<Map<String, Object>> page, String conditions);

    @SelectProvider(SysUserSqlProvider.class)
    Map<String, Object> selectUserInfoById(String userId);
}

SQL Provider ​

复杂动态 SQL 使用 Provider 类:

java
public class SysUserSqlProvider implements ProviderMethodResolver {

    public static String findUsers(Page<Map<String, Object>> page, String conditions) {
        StringBuffer sql = new StringBuffer();
        sql.append("SELECT u.id, u.account, u.user_name userName ");
        sql.append("FROM sys_user u ");
        sql.append("WHERE ").append(conditions);
        sql.append(" AND u.del_flag = 0");
        return sql.toString();
    }
}

分页查询 ​

统一使用 MyBatis-Plus Page + IPage:

java
@PostMapping("/findUsers")
public IPage<Map<String, Object>> findUsers(@RequestBody String params) {
    Page<Map<String, Object>> page = new Page<>();
    String conditions = MyUtil.getConditionsWhere(params, page);
    return sysUserService.findUsers(page, conditions, deptId);
}

数据模型规范 ​

持久化对象(PO) ​

所有 PO 继承 BasePO,自动填充审计字段:

java
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_user")
public class SysUserPO extends BasePO {
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @TableField("account")
    private String account;
}

BasePO 公共字段 ​

字段类型说明自动填充
idLong主键数据库自增
createByString创建人MetaObjectHandler
createTimeLocalDateTime创建时间MetaObjectHandler
lastUpdateByString最后更新人MetaObjectHandler
lastUpdateTimeLocalDateTime最后更新时间MetaObjectHandler
delFlagInteger删除标记(0=正常,1=删除)默认 0
statusInteger状态(0=启用,1=禁用)默认 0
versionInteger乐观锁版本号自动递增

数据传输对象(DTO) ​

服务间数据传输使用 DTO,继承 BaseDTO:

java
@Data
@EqualsAndHashCode(callSuper = true)
@Schema(name = "sys_user", description = "用户管理")
public class SysUserDTO extends BaseDTO {
    @Schema(description = "编号")
    private Long id;
}

软删除 ​

项目采用 SQL 级软删除(WHERE del_flag = 0),非 @TableLogic 注解方式。所有查询需在 SQL 中显式加上 del_flag = 0 条件。

乐观锁 ​

通过 version 字段实现乐观锁,更新时自动校验版本号。

异常处理 ​

统一异常处理器 ​

java
@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(BizException.class)
    public RestResult<?> handleBizException(BizException e) {
        return RestResult.fail(e.getCode(), e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public RestResult<?> handleException(Exception e) {
        log.error("服务器异常", e);
        return RestResult.fail(BizCode.SERVER_ERROR);
    }
}

业务异常码(BizCode) ​

通过 BizCode 枚举统一管理业务错误码:

java
// 通用
REQUEST_NULL(10001, "请求有错误")
SERVER_ERROR(10004, "服务器异常")

// 登录认证
AUTH_TOKEN_INVALID(23002, "token校验失败:token无效")
AUTH_TOKEN_TIMEOUT(23003, "token过期:请重新登录")

异常响应格式 ​

业务异常统一返回 HTTP 200,通过 body 区分成功/失败:

json
{
  "code": 23003,
  "status": "fail",
  "message": "token过期:请重新登录",
  "datas": null
}

参数校验 ​

使用 AssertMyUtil 进行参数校验:

java
AssertMyUtil.notNull(param, BizCode.VARIABLE_NOT_EMPTY, "param");
AssertMyUtil.notEmpty(userId, BizCode.METHOD_ARGS_NOTNULL, "userId");
AssertMyUtil.isNull(result, BizCode.VARIABLE_NOT_EMPTY, "result");

日志规范 ​

日志级别 ​

级别使用场景
DEBUG开发调试(仅 dev 环境输出)
INFO关键业务节点(登录、数据变更)
WARN可恢复的异常(缓存 miss、重试)
ERROR不可恢复异常(需人工介入)

日志格式 ​

java
// ✅ 推荐:占位符形式,避免字符串拼接
log.info("用户登录成功, userId={}, account={}", userId, account);

// ✅ 推荐:异常记录完整堆栈
log.error("查询用户失败, account={}", account, e);

// ❌ 不推荐:字符串拼接
log.info("用户登录成功, userId=" + userId);

操作日志 ​

使用 @MarkLog 注解记录操作日志到数据库:

java
@MarkLog(value = "新增用户-[{0}]", param = "account")
@PostMapping(value = "/saveUser")
public Map<String, Object> saveUser(@RequestBody Map<String, Object> param) {
    // ...
}

配置规范 ​

环境隔离 ​

Profile用途
dev本地开发,开启 DEBUG 日志、API 文档
uat预发布验证
prod生产运行,关闭 API 文档

Nacos 配置 ​

数据库、Redis 等敏感配置托管在 Nacos 远程配置中心:

yaml
spring:
  config:
    import:
      - optional:nacos:common.yml?group=guava&refreshEnabled=true
      - optional:nacos:admin.yml?group=guava&refreshEnabled=true

安全约定 ​

  • 禁止将密码、密钥写入源码或提交到仓库
  • 用户密码使用 PasswordEncoder 加密存储
  • 接口需 Token 校验(@CheckToken / @NoCheckToken)
  • 敏感信息(手机号、身份证)展示时脱敏
  • SQL 使用参数拼接时注意防注入

反例与正例 ​

分层违规 ​

java
// ❌ Controller 直接调用 Mapper
@PostMapping("/findUsers")
public IPage findUsers(@RequestBody String params) {
    return sysUserMapper.findUsers(page, conditions);  // 跳过 Service
}

// ✅ Controller → Service → Mapper
@PostMapping("/findUsers")
public IPage findUsers(@RequestBody String params) {
    return sysUserService.findUsers(page, conditions, deptId);
}

返回 PO 实体 ​

java
// ❌ 直接返回 PO,暴露数据库结构
@GetMapping("/getUser")
public SysUserPO getUser(@RequestParam Long id) {
    return sysUserMapper.selectById(id);
}

// ✅ 返回 DTO 或 Map
@GetMapping("/getUser")
public Map<String, Object> getUser(@RequestParam Long id) {
    return sysUserService.getUserInfoById(id);
}

字符串拼接日志 ​

java
// ❌ 字符串拼接
log.info("用户登录成功, userId=" + userId + ", account=" + account);

// ✅ 占位符形式
log.info("用户登录成功, userId={}, account={}", userId, account);

异常吞没 ​

java
// ❌ 捕获异常后无任何处理
try {
    sysUserService.saveUser(param);
} catch (Exception e) {
    // 空的
}

// ✅ 捕获异常后记录日志或抛出业务异常
try {
    sysUserService.saveUser(param);
} catch (Exception e) {
    log.error("保存用户失败, param={}", param, e);
    throw new BizException(BizCode.SERVER_ERROR);
}

Code Review 要点 ​

  • 目录与命名符合 目录规范 / 命名规范
  • 分层清晰,无跨层调用
  • 对外 API 有 Javadoc 和 OpenAPI 注解,见 注释规范
  • 参数校验完整(AssertMyUtil)
  • 异常处理规范,无吞没异常
  • 日志使用占位符形式
  • 无硬编码敏感信息
  • 事务使用正确(@Transactional)

开发 Checklist ​

功能开发完成、提交前确认:

  • 功能符合需求,边界情况已考虑(空数据、无权限)
  • 分层调用正确(Controller → Service → Repository)
  • 参数校验完整
  • 异常处理规范
  • 日志输出合理
  • 无遗留 TODO / 调试代码(或已关联任务)
  • API 文档注解完整

相关规范 ​