开发规范
本文档定义 youlai-boot 后端项目的开发约定,参考《阿里巴巴 Java 开发手册》及业界最佳实践。
命名规范
包命名
| 规则 | 示例 | 说明 |
|---|---|---|
| 全部小写 | com.youlai.boot.system | ✅ 正确 |
| 单词间无分隔 | com.youlai.boot.systemService | ❌ 错误 |
包结构规范:
com.youlai.boot
├── auth/ # 认证模块
├── common/ # 公共组件
├── config/ # 配置类
├── core/ # 核心组件
├── platform/ # 平台模块
├── security/ # 安全模块
└── system/ # 系统模块
├── controller/ # 控制层
├── service/ # 业务层
│ └── impl/ # 业务实现
├── mapper/ # 数据访问层
└── model/ # 数据模型
├── entity/ # 实体类
├── form/ # 表单对象
├── query/ # 查询对象
├── vo/ # 视图对象
├── dto/ # 数据传输对象
└── bo/ # 业务对象类命名
| 类型 | 规则 | 示例 |
|---|---|---|
| 控制器 | XxxController | UserController |
| 业务接口 | XxxService | UserService |
| 业务实现 | XxxServiceImpl | UserServiceImpl |
| 数据访问 | XxxMapper | UserMapper |
| 实体类 | Xxx | User |
| 表单对象 | XxxForm | UserForm |
| 查询对象 | XxxQuery | UserQuery |
| 视图对象 | XxxVO | UserPageVO |
| 数据传输 | XxxDTO | CurrentUserDTO |
| 业务对象 | XxxBO | RolePermsBO |
| 枚举类 | XxxEnum | DataScopeEnum |
| 常量类 | XxxConstants | SystemConstants |
| 配置类 | XxxConfig / XxxProperties | SecurityConfig |
| 异常类 | XxxException | BusinessException |
方法命名
查询方法
| 方法前缀 | 说明 | 示例 |
|---|---|---|
get | 单个对象查询 | getUserById()、getUserFormData() |
list | 列表查询 | listUsers()、listUserOptions() |
page / getPage | 分页查询 | getUserPage() |
count | 统计数量 | countUsers() |
exists | 存在性判断 | existsByUsername() |
java
// ✅ 推荐
User getUserById(Long id);
List<User> listUsers(UserQuery query);
IPage<UserPageVO> getUserPage(UserQuery query);
boolean existsByUsername(String username);
// ❌ 不推荐
User selectUser(Long id); // 应使用 get
List<User> queryUsers(); // 应使用 list写操作方法
| 方法前缀 | 说明 | 示例 |
|---|---|---|
save | 新增 | saveUser() |
update | 修改 | updateUser() |
delete | 删除 | deleteUser() |
batch | 批量操作 | batchInsert() |
java
// ✅ 推荐
boolean saveUser(UserForm form);
boolean updateUser(Long id, UserForm form);
boolean deleteUser(Long id);
boolean deleteUsers(String ids); // 批量删除
// ❌ 不推荐
boolean addUser(); // 应使用 save
boolean modifyUser(); // 应使用 update
boolean removeUser(); // 应使用 delete方法命名规范示例
java
public interface UserService {
// 查询方法
User getUserById(Long id); // 单个查询
UserForm getUserFormData(Long id); // 获取表单数据
IPage<UserPageVO> getUserPage(UserQuery q); // 分页查询
List<User> listUsers(); // 列表查询
List<Option<String>> listUserOptions(); // 下拉选项
boolean existsByUsername(String username); // 存在性判断
// 写操作方法
boolean saveUser(UserForm form); // 新增
boolean updateUser(Long id, UserForm form); // 修改
boolean deleteUsers(String ids); // 删除(批量)
boolean resetUserPassword(Long id, String pwd); // 特殊操作
}变量命名
| 类型 | 规则 | 示例 |
|---|---|---|
| 成员变量 | lowerCamelCase | private UserService userService; |
| 局部变量 | lowerCamelCase | User user = userService.getById(id); |
| 常量 | UPPER_SNAKE_CASE | public static final String DEFAULT_PASSWORD = "123456"; |
| 布尔变量 | is/has/can 前缀 | private boolean isRoot; |
分层架构规约
调用链路
各层职责
| 层级 | 职责 | 禁止事项 |
|---|---|---|
| Controller | 参数校验、调用 Service、组装返回结果 | ❌ 禁止直接调用 Mapper |
| Service | 业务逻辑、事务控制、数据组装 | ❌ 禁止跨业务调用 Mapper |
| Mapper | 数据库访问、SQL 编写 | ❌ 禁止包含业务逻辑 |
Service 调用规范
核心原则:Service 只能调用自己业务域的 Mapper,禁止跨业务调用。
java
// ✅ 正确:UserService 调用 UserMapper
@Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper; // 自己业务域
private final UserRoleService userRoleService; // 通过 Service 交互
public User getUserById(Long id) {
return userMapper.selectById(id);
}
}
// ❌ 错误:UserService 直接调用 RoleMapper
@Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
private final RoleMapper roleMapper; // ❌ 禁止跨业务调用 Mapper
public User getUserWithRoles(Long id) {
User user = userMapper.selectById(id);
List<Role> roles = roleMapper.listByUserId(id); // ❌ 错误
return user;
}
}
// ✅ 正确:通过 RoleService 获取角色
@Service
public class UserServiceImpl implements UserService {
private final UserMapper userMapper;
private final RoleService roleService; // ✅ 通过 Service 交互
public User getUserWithRoles(Long id) {
User user = userMapper.selectById(id);
List<Role> roles = roleService.listRolesByUserId(id); // ✅ 正确
return user;
}
}跨业务数据交互
| 场景 | 正确做法 |
|---|---|
| UserService 需要角色数据 | 调用 RoleService.listRolesByUserId() |
| OrderService 需要用户数据 | 调用 UserService.getUserById() |
| 需要多个业务数据组装 | 在 Service 层调用多个 Service 组装 |
RESTful API 规范
URL 设计
| 规则 | 示例 | 说明 |
|---|---|---|
| 使用名词复数 | /api/v1/users | ✅ 正确 |
| 避免动词 | /api/v1/getUsers | ❌ 错误 |
| 层级不超过 3 层 | /api/v1/users/{id}/roles | ✅ 正确 |
| 使用连字符分隔 | /api/v1/user-profiles | ✅ 正确 |
HTTP 方法映射
| HTTP 方法 | 操作 | URL 示例 | 权限标识 |
|---|---|---|---|
GET | 列表查询 | GET /api/v1/users | sys:user:list |
GET | 单个查询 | GET /api/v1/users/{id} | sys:user:list |
POST | 新增 | POST /api/v1/users | sys:user:create |
PUT | 全量修改 | PUT /api/v1/users/{id} | sys:user:update |
PATCH | 部分修改 | PATCH /api/v1/users/{id}/status | sys:user:update |
DELETE | 删除 | DELETE /api/v1/users/{id} | sys:user:delete |
接口示例
java
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
// 列表查询
@GetMapping
public PageResult<UserPageVO> getUserList(UserQuery queryParams) { }
// 单个查询
@GetMapping("/{id}")
public Result<User> getUser(@PathVariable Long id) { }
// 新增
@PostMapping
@PreAuthorize("@ss.hasPerm('sys:user:create')")
public Result<Void> saveUser(@RequestBody @Valid UserForm form) { }
// 修改
@PutMapping("/{id}")
@PreAuthorize("@ss.hasPerm('sys:user:update')")
public Result<Void> updateUser(@PathVariable Long id, @RequestBody @Valid UserForm form) { }
// 删除
@DeleteMapping("/{id}")
@PreAuthorize("@ss.hasPerm('sys:user:delete')")
public Result<Void> deleteUser(@PathVariable Long id) { }
// 部分修改
@PatchMapping("/{id}/status")
@PreAuthorize("@ss.hasPerm('sys:user:update')")
public Result<Void> updateUserStatus(@PathVariable Long id, @RequestParam Integer status) { }
// 子资源
@GetMapping("/{id}/roles")
public Result<List<Role>> getUserRoles(@PathVariable Long id) { }
// 特殊操作(动词转名词)
@PostMapping("/{id}/password/reset")
@PreAuthorize("@ss.hasPerm('sys:user:reset-password')")
public Result<Void> resetPassword(@PathVariable Long id) { }
}URL 命名规范
java
// ✅ 推荐
GET /api/v1/users // 用户列表
GET /api/v1/users/{id} // 用户详情
GET /api/v1/users/{id}/form // 用户表单数据
GET /api/v1/users/me // 当前用户信息
POST /api/v1/users/{id}/password/reset // 重置密码
// ❌ 不推荐
GET /api/v1/getUserList // URL 包含动词
GET /api/v1/user // 应使用复数
POST /api/v1/users/resetPassword/{id} // 路径参数应在最后权限标识规范
命名格式
模块:资源:操作操作命名对照表
| 操作 | 标识 | HTTP 方法 | 说明 |
|---|---|---|---|
| 列表查询 | list | GET | 分页/列表查询 |
| 新增 | create | POST | 创建资源 |
| 修改 | update | PUT/PATCH | 更新资源 |
| 删除 | delete | DELETE | 删除资源 |
| 导入 | import | POST | 批量导入 |
| 导出 | export | GET | 数据导出 |
| 发布 | publish | POST | 发布操作 |
| 撤销 | revoke | POST | 撤销操作 |
| 刷新 | refresh | POST/GET | 缓存刷新 |
| 分配 | assign | POST | 权限分配 |
权限标识示例
java
// 用户管理
sys:user:list // 列表
sys:user:create // 新增
sys:user:update // 修改
sys:user:delete // 删除
sys:user:import // 导入
sys:user:export // 导出
sys:user:reset-password // 重置密码
// 角色管理
sys:role:create
sys:role:update
sys:role:delete
sys:role:assign // 分配权限
// 公告管理
sys:notice:list
sys:notice:create
sys:notice:update
sys:notice:delete
sys:notice:publish // 发布
sys:notice:revoke // 撤销通配符使用
java
// 资源级别通配
sys:user:* // 用户模块所有权限
// 模块级别通配
sys:*:* // 系统模块所有权限
// 超级管理员
*:*:* // 所有权限错误码规范
设计原则
参考《阿里巴巴 Java 开发手册》错误码规范:
- 快速溯源:通过错误码快速定位错误来源
- 简单易记:5 位字符串,易于记忆和比对
- 沟通标准化:脱离文档也能准确沟通
错误码结构
错误来源(1位)+ 数字编号(4位)| 来源 | 前缀 | 说明 |
|---|---|---|
| 用户端错误 | A | 参数错误、认证失败、权限不足等 |
| 系统错误 | B | 系统超时、内部异常等 |
| 第三方服务 | C | 数据库、中间件、外部 API 等 |
号段划分
| 号段 | 分类 | 示例 |
|---|---|---|
A0001 | 一级宏观 | 用户端错误 |
A0100 | 二级宏观 | 用户注册错误 |
A01xx | 三级细分 | A0101 用户未同意协议 |
A0200 | 二级宏观 | 用户登录异常 |
A02xx | 三级细分 | A0230 Token 无效 |
常用错误码
| 错误码 | 说明 | 场景 |
|---|---|---|
00000 | 成功 | 正常执行 |
A0001 | 用户端错误 | 一级宏观 |
A0200 | 用户登录异常 | 登录失败 |
A0230 | Token 无效或过期 | 认证失败 |
A0301 | 访问未授权 | 权限不足 |
A0400 | 请求参数错误 | 参数校验失败 |
A0506 | 请勿重复提交 | 防重复提交 |
B0001 | 系统执行出错 | 一级宏观 |
C0001 | 第三方服务出错 | 一级宏观 |
使用示例
java
// 定义业务异常
public class BusinessException extends RuntimeException {
private final IResultCode resultCode;
public BusinessException(ResultCode resultCode) {
super(resultCode.getMsg());
this.resultCode = resultCode;
}
}
// 使用示例
public void saveUser(UserForm form) {
if (userRepository.existsByUsername(form.getUsername())) {
throw new BusinessException(ResultCode.USER_REQUEST_PARAMETER_ERROR);
}
// ...
}
// 响应格式
{
"code": "A0400",
"msg": "用户请求参数错误",
"data": null
}排序字段规范
字段命名
| 字段名 | 类型 | 说明 |
|---|---|---|
sort | int | 排序号(升序,值越小越靠前) |
create_time | datetime | 创建时间(降序) |
update_time | datetime | 更新时间(降序) |
排序规则
java
// ✅ 推荐:先按 sort 升序,再按创建时间降序
lambdaQuery()
.orderByAsc(Entity::getSort)
.orderByDesc(Entity::getCreateTime)
.list();
// 示例:角色列表排序
lambdaQuery()
.orderByAsc(Role::getSort)
.orderByDesc(Role::getCreateTime)
.orderByDesc(Role::getUpdateTime)
.list();
// 示例:菜单列表排序
lambdaQuery()
.orderByAsc(Menu::getSort)
.list();默认排序
| 业务场景 | 默认排序 |
|---|---|
| 列表查询 | sort ASC, create_time DESC |
| 树形结构 | sort ASC |
| 日志记录 | create_time DESC |
代码注释规范
类注释
java
/**
* 用户业务实现类
*
* @author Ray.Hao
* @since 2022/1/14
*/
@Service
public class UserServiceImpl implements UserService {
// ...
}方法注释
java
/**
* 获取用户分页列表
*
* @param queryParams 查询参数
* @return {@link IPage<UserPageVO>} 用户分页列表
*/
@Override
public IPage<UserPageVO> getUserPage(UserQuery queryParams) {
// ...
}
/**
* 新增用户
*
* @param userForm 用户表单对象
* @return {@link Boolean} 是否新增成功
*/
@Override
public boolean saveUser(UserForm userForm) {
// ...
}字段注释
java
public class User {
/** 用户ID */
private Long id;
/** 用户名 */
private String username;
/** 用户状态:1-启用,0-禁用 */
private Integer status;
}参数校验规范
Bean Validation
java
@Data
public class UserForm {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度为2-20个字符")
private String username;
@NotBlank(message = "密码不能为空")
@Size(min = 6, max = 20, message = "密码长度为6-20个字符")
private String password;
@Email(message = "邮箱格式不正确")
private String email;
@Pattern(regexp = "^1[3-9]\\d{9}$", message = "手机号格式不正确")
private String mobile;
}Controller 校验
java
@PostMapping
public Result<Void> saveUser(@RequestBody @Valid UserForm form) {
// 参数校验通过后执行业务逻辑
return Result.judge(userService.saveUser(form));
}Service 校验
java
public boolean saveUser(UserForm form) {
// 业务校验
if (userRepository.existsByUsername(form.getUsername())) {
throw new BusinessException("用户名已存在");
}
// 执行保存
User user = userConverter.toEntity(form);
return this.save(user);
}日志规范
日志级别
| 级别 | 场景 |
|---|---|
ERROR | 错误信息,需要立即处理 |
WARN | 警告信息,可能存在问题 |
INFO | 关键流程信息 |
DEBUG | 调试信息 |
日志格式
java
// ✅ 推荐:使用占位符
log.info("用户登录成功:userId={}, username={}", userId, username);
log.error("用户登录失败:username={}, reason={}", username, e.getMessage(), e);
// ❌ 不推荐:字符串拼接
log.info("用户登录成功:userId=" + userId);敏感信息脱敏
java
// ✅ 推荐:敏感信息脱敏
log.info("用户登录:mobile={}", maskMobile(mobile));
// ❌ 不推荐:打印敏感信息
log.info("用户登录:password={}", password);最佳实践
避免魔法值
java
// ❌ 不推荐
if (user.getStatus() == 1) { }
// ✅ 推荐:使用枚举
if (Objects.equals(user.getStatus(), UserStatusEnum.ENABLED.getValue())) { }
// ✅ 推荐:使用常量
public class UserConstants {
public static final int STATUS_ENABLED = 1;
public static final int STATUS_DISABLED = 0;
}集合判空
java
// ❌ 不推荐
if (list.size() > 0) { }
// ✅ 推荐
if (CollectionUtil.isNotEmpty(list)) { }字符串判空
java
// ❌ 不推荐
if (str != null && str.length() > 0) { }
// ✅ 推荐
if (StrUtil.isNotBlank(str)) { }避免 NPE
java
// ❌ 不推荐
return user.getUsername().equals("admin");
// ✅ 推荐
return Objects.equals(user.getUsername(), "admin");
// ✅ 推荐:使用 Optional
return Optional.ofNullable(user)
.map(User::getUsername)
.map("admin"::equals)
.orElse(false);相关文件
| 文件 | 说明 |
|---|---|
core/web/ResultCode.java | 错误码枚举定义 |
core/exception/GlobalExceptionHandler.java | 全局异常处理 |
common/base/BaseQuery.java | 查询基类 |
common/annotation/Log.java | 操作日志注解 |
common/annotation/RepeatSubmit.java | 防重复提交注解 |
