# Java 后端开发规范 本项目 Java 后端开发规范,基于 Spring Boot + MyBatis-Plus 技术栈。 ## JDK 版本要求(重要) **必须使用 JDK 17** 进行编译和运行。 如果系统环境变量配置的是 JDK 1.8,请在编译前设置 `JAVA_HOME`: ```bash # Windows (Git Bash) - 根据实际安装路径调整 export JAVA_HOME="/f/Java/jdk-17" export PATH="$JAVA_HOME/bin:$PATH" # 编译项目 mvn clean compile -DskipTests # 或者在启动时指定 mvn spring-boot:run -Djava.home="/f/Java/jdk-17" ``` ## 核心原则 1. **OpenAPI 规范驱动** - 前后端通过接口规范对齐 2. **类型安全优先** - 强制类型校验 3. **约定大于配置** - 统一代码风格 4. **自动化优先** - 能自动化的绝不手动 5. **三层架构分离** - Controller、Service、Mapper 职责清晰 ## 技术栈 | 组件 | 技术选型 | 版本 | |------|---------|------| | 框架 | Spring Boot | 3.2+ | | 持久层 | MyBatis-Plus | 3.5+ | | 对象映射 | MapStruct | 1.5+ | | 数据库 | MySQL | 8.0+ | | 缓存 | Redis | - | | 安全 | Spring Security + JWT | - | | API 文档 | Knife4j | 4.x | ## 三层架构 | 层级 | 职责 | 数据接收 | 数据返回 | |------|------|---------|---------| | Controller | 接收请求、参数校验、返回响应 | DTO/Request | VO/Response | | Service | 处理业务逻辑、事务控制 | DTO/Entity | Entity | | Mapper | 数据库 CRUD 操作 | Entity/条件 | Entity | **核心规范:** - Service↔Mapper 之间只用 Entity,禁止 DTO/VO 转换 - 转换只在 Controller 层发生 - Service 继承 `IService` - 查询接口默认分页 ## 消除魔法值规范 **禁止在代码中使用魔法值,所有状态、类型、常量必须使用枚举定义** - 枚举类存放在 `enums` 包下 - 枚举包含 `code` 和 `desc` 字段 - 提供 `getCode()`、`getDesc()`、`valueOfCode()` 方法 - 数据库存储 `code`,代码中使用枚举 ## ORM 实体类规范 ### 表名命名 - `t_user_*` - 用户模块 - `t_sys_*` - 系统模块 - `t_biz_*` - 业务模块 - `t_auth_*` - 权限模块 ### 审计字段(必填) 所有表必须包含审计字段: | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT | 主键(自增) | | create_by | VARCHAR(50) | 创建人 | | create_time | DATETIME | 创建时间 | | update_by | VARCHAR(50) | 更新人 | | update_time | DATETIME | 更新时间 | | deleted | TINYINT | 逻辑删除(0-未删除,1-已删除) | - 审计字段通过 MyBatis-Plus `FieldFill` 自动填充 - 禁止手动设置审计字段 ## 日志规范 ### 日志语言 **所有日志必须使用中文** ### TraceId 链路追踪 - 使用 MDC 实现 TraceId 链路追踪 - AOP 切面在请求入口生成 TraceId - 日志格式包含 `[%X{traceId}]` ### 环境差异化配置 | 环境 | 日志策略 | |------|---------| | 开发/测试 | 全量记录(DEBUG 级别) | | 生产 | 精简记录(INFO/WARN 级别) | ### AOP 日志实现 - 拦截 Controller 层所有方法 - 请求日志:TraceId、请求方法、路径、IP、耗时 - 异常日志:TraceId、异常类型、消息、堆栈 ## 统一响应格式 ```java Result { code: Integer; // 状态码 message: String; // 消息 data: T; // 数据 timestamp: Long; // 时间戳 } ``` **错误码:** 200-成功、400-参数错误、401-未授权、403-无权限、404-不存在、500-系统错误 ## MapStruct 对象映射 - 使用 `@Mapper` 注解定义 Converter 接口 - Entity ↔ VO 转换在 Controller 层调用 - Service 层和 Mapper 层禁止 DTO/VO 转换 - 命名规范:`XxxConverter` 或 `XxxMapStruct` ## 工具类规范 - 工具函数集中管理,禁止在 Controller 层编写工具方法 - 工具类私有构造、静态方法、无状态、线程安全 - 命名规范:`XxxUtil`(通用)、`XxxHelper`(业务)、`XxxConverter`(转换) ## 多环境配置 | 配置项 | dev | test | prod | |--------|-----|------|------| | SQL 日志 | 开启 | 开启 | 关闭 | | Swagger | 开启 | 开启 | 关闭 | | Flyway Clean | 允许 | 禁止 | 禁止 | ## 快速参考 ### Redis Key 命名 - `auth:token:{token}` - 用户 Token - `user:info:{userId}` - 用户信息缓存 - `dict:{type}` - 数据字典 - `lock:{resource}:{id}` - 分布式锁 - `rate_limit:{key}` - 限流计数器 ## 数据库索引规范(重要) ### 唯一索引处理原则 **项目使用逻辑删除,唯一索引通过应用层控制,而非数据库唯一约束。** #### 背景问题 当表有逻辑删除字段(`deleted`)时,数据库唯一索引会导致: 1. 删除后无法重新添加相同数据 2. 错误信息不友好,直接返回数据库异常 #### 解决方案 | 层级 | 职责 | 实现方式 | |------|------|----------| | **数据库层** | 普通索引 | `KEY idx_xxx (xxx)` 而非 `UNIQUE KEY uk_xxx (xxx)` | | **应用层** | 重复校验 | Service 层查询 + 悲观锁 `FOR UPDATE` | | **异常处理** | 兜底处理 | 全局异常处理器捕获 `DuplicateKeyException` | #### 实现示例 **1. 数据库迁移(Flyway)** ```sql -- ❌ 错误:使用唯一索引 UNIQUE KEY `uk_username` (`username`) -- ✅ 正确:使用普通索引 KEY `idx_username` (`username`) ``` **2. Mapper 层(悲观锁查询)** ```java /** * 根据用户名查询用户(悲观锁,用于创建时防并发) */ @Select("SELECT * FROM t_user WHERE username = #{username} AND tenant_id = #{tenantId} AND deleted = 0 FOR UPDATE") User getUserByUsernameForUpdate(@Param("username") String username, @Param("tenantId") Long tenantId); ``` **3. Service 层(事务 + 校验)** ```java @Transactional(rollbackFor = Exception.class) public UserVO createUser(CreateUserDTO dto, Long tenantId, Long operatorId) { log.info("开始创建用户,用户名:{}", dto.getUsername()); // 使用悲观锁检查用户名是否已存在(防止并发) User existingUser = userMapper.getUserByUsernameForUpdate(dto.getUsername(), tenantId); if (existingUser != null) { throw new BusinessException("用户名已存在"); } // 插入用户 User user = convert(dto); userMapper.insert(user); return convertToVO(user); } ``` **4. 需要应用层校验的字段** 以下字段需要添加应用层重复校验: | 表 | 字段 | 说明 | |-----|------|------| | t_user | username, email, phone | 用户名、邮箱、手机号 | | t_role | code | 角色编码 | | t_permission | code | 权限编码 | | t_dict | code + tenant_id | 字典编码 | | t_tenant | code | 租户编码 | | t_sys_config | config_key | 配置键 | ### 关联表索引规范 对于多对多关联表,使用普通索引而非唯一索引: ```sql -- t_user_role: 用户角色关联表 KEY `idx_user_role` (`user_id`, `role_id`) -- t_role_permission: 角色权限关联表 KEY `idx_role_permission` (`role_id`, `permission_id`) ``` **注意**:关联表的重复数据控制在应用层业务逻辑中处理。 ### 核心环境变量 - `SPRING_PROFILES_ACTIVE` - 活跃环境 - `DB_HOST`、`DB_PASSWORD` - 数据库配置 - `REDIS_HOST`、`REDIS_PASSWORD` - Redis 配置 - `JWT_SECRET` - JWT 密钥 - `OSS_ACCESS_KEY_ID`、`OSS_ACCESS_KEY_SECRET` - OSS 配置