spring-boot
SKILL.md
Spring Boot 后端开发规范
触发条件
- 开发 Spring Boot 项目
- 实现 REST API
- 使用 MyBatis-Plus 数据访问
- 实现 JWT/Redis Token 认证
- 实现权限控制
Part 1: 技术栈
| 技术 | 版本 | 用途 |
|---|---|---|
| JDK | 17+ | Java 运行环境 |
| Spring Boot | 3.2+ | 基础框架 |
| MyBatis-Plus | 3.5+ | ORM 框架 |
| MySQL | 8.0+ | 数据库 |
| Redis | 7.0+ | 缓存 |
| JWT | 0.12+ | Token 认证 |
| Knife4j | 4.4+ | API 文档 |
Part 2: 目录结构
src/main/java/com/youlai/
├── common/ # 公共模块
│ ├── annotation/ # 自定义注解
│ │ ├── RepeatSubmit.java
│ │ └── DataScope.java
│ ├── aspect/ # 切面
│ │ └── LogAspect.java
│ ├── config/ # 配置类
│ │ ├── MybatisPlusConfig.java
│ │ ├── RedisConfig.java
│ │ └── SecurityConfig.java
│ ├── constant/ # 常量
│ │ ├── SecurityConstants.java
│ │ └── RedisConstants.java
│ ├── enums/ # 枚举
│ │ ├── ResultCode.java
│ │ └── MenuType.java
│ ├── exception/ # 异常
│ │ ├── BusinessException.java
│ │ └── GlobalExceptionHandler.java
│ ├── model/ # 基础模型
│ │ ├── BaseEntity.java
│ │ ├── PageQuery.java
│ │ └── Result.java
│ └── util/ # 工具类
│ ├── JwtUtils.java
│ └── SecurityUtils.java
├── module/ # 业务模块
│ ├── system/ # 系统模块
│ │ ├── controller/
│ │ │ └── UserController.java
│ │ ├── converter/ # 对象转换
│ │ │ └── UserConverter.java
│ │ ├── entity/
│ │ │ └── SysUser.java
│ │ ├── mapper/
│ │ │ └── SysUserMapper.java
│ │ ├── service/
│ │ │ ├── UserService.java
│ │ │ └── impl/UserServiceImpl.java
│ │ └── dto/
│ │ ├── UserPageQuery.java
│ │ ├── UserForm.java
│ │ └── UserVO.java
│ └── auth/ # 认证模块
│ ├── controller/
│ │ └── AuthController.java
│ ├── service/
│ │ └── AuthService.java
│ └── dto/
│ ├── LoginForm.java
│ └── LoginResult.java
├── security/ # 安全模块
│ ├── filter/
│ │ └── JwtAuthenticationFilter.java
│ ├── handler/
│ │ └── AuthenticationEntryPointImpl.java
│ └── model/
│ └── SysUserDetails.java
└── Application.java # 启动类
Part 3: 命名规范
类命名
| 类型 | 规范 | 示例 |
|---|---|---|
| 实体 | 实体名 + Entity 或前缀 Sys | User 或 SysUser |
| DTO | 功能 + DTO 类型 | UserVO, UserForm, UserPageQuery |
| Service | 实体 + Service | UserService |
| ServiceImpl | 实体 + ServiceImpl | UserServiceImpl |
| Controller | 实体 + Controller | UserController |
| Mapper | 实体 + Mapper | UserMapper |
| Converter | 实体 + Converter | UserConverter |
方法命名
| 动作 | 前缀 | 示例 |
|---|---|---|
| 查询单个 | get | getById() |
| 查询列表 | list | listUsers() |
| 分页查询 | page | pageUsers() |
| 新增 | save/add | saveUser() |
| 更新 | update | updateUser() |
| 删除 | remove/delete | removeById() |
| 统计 | count | countUsers() |
| 判断存在 | exists | existsByUsername() |
变量命名
| 类型 | 规范 | 示例 |
|---|---|---|
| 成员变量 | camelCase | userService |
| 常量 | UPPER_SNAKE_CASE | MAX_SIZE |
| 私有变量 | _ 前缀或无前缀 | _internalState |
| 布尔值 | is/has/can 前缀 | isDeleted, hasPermission |
Part 4: RESTful API 规范
路径命名
- 使用 kebab-case:
/api/v1/user-profile - 版本前缀:
/api/v1/ - 资源命名:复数名词
/users,/roles
标准 CRUD 路径
| 操作 | 方法 | 路径 |
|---|---|---|
| 分页列表 | GET | /api/v1/users/page |
| 详情 | GET | /api/v1/users/{id} |
| 新增 | POST | /api/v1/users |
| 更新 | PUT | /api/v1/users |
| 删除 | DELETE | /api/v1/users/{id} |
| 批量删除 | DELETE | /api/v1/users/batch |
| 下拉选项 | GET | /api/v1/users/options |
| 表单数据 | GET | /api/v1/users/{id}/form |
Controller 模板
@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
@Tag(name = "用户管理")
public class UserController {
private final UserService userService;
@GetMapping("/page")
@Operation(summary = "用户分页列表")
public Result<PageResult<UserVO>> page(UserPageQuery query) {
return Result.success(userService.pageUsers(query));
}
@GetMapping("/{id}")
@Operation(summary = "用户详情")
public Result<UserVO> getById(@PathVariable Long id) {
return Result.success(userService.getUserById(id));
}
@GetMapping("/{id}/form")
@Operation(summary = "用户表单数据")
public Result<UserForm> getFormData(@PathVariable Long id) {
return Result.success(userService.getUserFormData(id));
}
@PostMapping
@Operation(summary = "新增用户")
@PreAuthorize("hasAuthority('sys:user:add')")
public Result<Long> save(@Valid @RequestBody UserForm form) {
return Result.success(userService.saveUser(form));
}
@PutMapping
@Operation(summary = "更新用户")
@PreAuthorize("hasAuthority('sys:user:edit')")
public Result<Void> update(@Valid @RequestBody UserForm form) {
userService.updateUser(form);
return Result.success();
}
@DeleteMapping("/{id}")
@Operation(summary = "删除用户")
@PreAuthorize("hasAuthority('sys:user:delete')")
public Result<Void> remove(@PathVariable Long id) {
userService.removeUserById(id);
return Result.success();
}
@DeleteMapping("/batch")
@Operation(summary = "批量删除用户")
@PreAuthorize("hasAuthority('sys:user:delete')")
public Result<Void> batchRemove(@RequestBody List<Long> ids) {
userService.removeUsersByIds(ids);
return Result.success();
}
@GetMapping("/options")
@Operation(summary = "用户选项列表")
public Result<List<OptionType>> options() {
return Result.success(userService.listUserOptions());
}
}
Part 5: 响应格式
统一响应类
@Data
public class Result<T> {
private Integer code;
private String msg;
private T data;
public static <T> Result<T> success(T data) {
Result<T> result = new Result<>();
result.setCode(ResultCode.SUCCESS.getCode());
result.setMsg(ResultCode.SUCCESS.getMsg());
result.setData(data);
return result;
}
public static <T> Result<T> failed(ResultCode code) {
Result<T> result = new Result<>();
result.setCode(code.getCode());
result.setMsg(code.getMsg());
return result;
}
public static <T> Result<T> failed(Integer code, String msg) {
Result<T> result = new Result<>();
result.setCode(code);
result.setMsg(msg);
return result;
}
}
分页响应类
@Data
public class PageResult<T> {
private List<T> list;
private Long total;
public static <T> PageResult<T> of(List<T> list, Long total) {
PageResult<T> result = new PageResult<>();
result.setList(list);
result.setTotal(total);
return result;
}
}
响应码枚举
@Getter
@AllArgsConstructor
public enum ResultCode {
SUCCESS(200, "操作成功"),
PARAM_ERROR(400, "参数错误"),
UNAUTHORIZED(401, "未登录或token过期"),
FORBIDDEN(403, "无权限"),
NOT_FOUND(404, "资源不存在"),
SYSTEM_ERROR(500, "系统异常");
private final Integer code;
private final String msg;
}
Part 6: 实体规范
基础实体
@Data
public class BaseEntity {
@TableId(type = IdType.ASSIGN_ID)
private Long id;
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;
@TableField(fill = FieldFill.INSERT)
private Long createBy;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Long updateBy;
@TableLogic
private Integer deleted;
}
实体示例
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("sys_user")
@Schema(description = "用户实体")
public class SysUser extends BaseEntity {
@Schema(description = "用户名")
private String username;
@Schema(description = "昵称")
private String nickname;
@Schema(description = "密码")
private String password;
@Schema(description = "手机号")
private String mobile;
@Schema(description = "邮箱")
private String email;
@Schema(description = "性别(0未知 1男 2女)")
private Integer gender;
@Schema(description = "状态(1正常 0禁用)")
private Integer status;
@Schema(description = "部门ID")
private Long deptId;
}
Part 7: Service 规范
Service 接口
public interface UserService {
PageResult<UserVO> pageUsers(UserPageQuery query);
UserVO getUserById(Long id);
UserForm getUserFormData(Long id);
Long saveUser(UserForm form);
void updateUser(UserForm form);
void removeUserById(Long id);
void removeUsersByIds(List<Long> ids);
List<OptionType> listUserOptions();
}
Service 实现
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final SysUserMapper userMapper;
private final UserConverter userConverter;
@Override
public PageResult<UserVO> pageUsers(UserPageQuery query) {
Page<SysUser> page = new Page<>(query.getPageNum(), query.getPageSize());
LambdaQueryWrapper<SysUser> wrapper = Wrappers.lambdaQuery();
wrapper.like(StrUtil.isNotBlank(query.getKeywords()), SysUser::getUsername, query.getKeywords())
.eq(query.getStatus() != null, SysUser::getStatus, query.getStatus())
.orderByDesc(SysUser::getCreateTime);
Page<SysUser> result = userMapper.selectPage(page, wrapper);
List<UserVO> list = userConverter.toVOList(result.getRecords());
return PageResult.of(list, result.getTotal());
}
@Override
public UserVO getUserById(Long id) {
SysUser user = userMapper.selectById(id);
Assert.notNull(user, "用户不存在");
return userConverter.toVO(user);
}
@Override
@Transactional(rollbackFor = Exception.class)
public Long saveUser(UserForm form) {
// 检查用户名是否存在
Assert.isTrue(!existsByUsername(form.getUsername()), "用户名已存在");
// 保存用户
SysUser user = userConverter.toEntity(form);
user.setPassword(BCryptUtils.encode(form.getPassword()));
userMapper.insert(user);
// 保存用户角色
userRoleMapper.insertBatch(user.getId(), form.getRoleIds());
return user.getId();
}
@Override
@Transactional(rollbackFor = Exception.class)
public void updateUser(UserForm form) {
SysUser user = userConverter.toEntity(form);
userMapper.updateById(user);
// 更新用户角色
userRoleMapper.deleteByUserId(user.getId());
userRoleMapper.insertBatch(user.getId(), form.getRoleIds());
}
@Override
@Transactional(rollbackFor = Exception.class)
public void removeUserById(Long id) {
userMapper.deleteById(id);
userRoleMapper.deleteByUserId(id);
}
private boolean existsByUsername(String username) {
return userMapper.exists(Wrappers.lambdaQuery(SysUser.class)
.eq(SysUser::getUsername, username));
}
}
Part 8: 认证规范
JWT 配置
security:
session:
mode: jwt # 或 redis-token
jwt:
secret: SecretKey012345678901234567890123456789
issuer: youlai-boot
access-token-ttl: 7200 # 2小时
refresh-token-ttl: 604800 # 7天
Token 生成
@Component
@RequiredArgsConstructor
public class JwtTokenManager {
@Value("${security.session.jwt.secret}")
private String secret;
@Value("${security.session.jwt.issuer}")
private String issuer;
public String generateAccessToken(SysUserDetails userDetails) {
return Jwts.builder()
.subject(String.valueOf(userDetails.getUserId()))
.claim("username", userDetails.getUsername())
.issuer(issuer)
.issuedAt(new Date())
.expiration(new Date(System.currentTimeMillis() + accessTokenTtl * 1000))
.signWith(Keys.hmacShaKeyFor(secret.getBytes()))
.compact();
}
public SysUserDetails parseAccessToken(String token) {
Claims claims = Jwts.parser()
.verifyWith(Keys.hmacShaKeyFor(secret.getBytes()))
.build()
.parseSignedClaims(token)
.getPayload();
return SysUserDetails.builder()
.userId(Long.parseLong(claims.getSubject()))
.username(claims.get("username", String.class))
.build();
}
}
权限注解
// 按钮权限
@PreAuthorize("hasAuthority('sys:user:add')")
// 角色权限
@PreAuthorize("hasRole('ADMIN')")
// 数据权限
@DataScope(deptAlias = "d", userAlias = "u")
Part 9: 异常处理
全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(BusinessException.class)
public Result<Void> handleBusinessException(BusinessException e) {
return Result.failed(e.getCode(), e.getMessage());
}
@ExceptionHandler(MethodArgumentNotValidException.class)
public Result<Void> handleValidationException(MethodArgumentNotValidException e) {
String message = e.getBindingResult().getFieldErrors().stream()
.map(FieldError::getDefaultMessage)
.collect(Collectors.joining(", "));
return Result.failed(ResultCode.PARAM_ERROR.getCode(), message);
}
@ExceptionHandler(Exception.class)
public Result<Void> handleException(Exception e) {
log.error("系统异常", e);
return Result.failed(ResultCode.SYSTEM_ERROR);
}
}
业务异常
@Getter
public class BusinessException extends RuntimeException {
private final Integer code;
public BusinessException(String message) {
super(message);
this.code = ResultCode.PARAM_ERROR.getCode();
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public BusinessException(ResultCode code) {
super(code.getMsg());
this.code = code.getCode();
}
}
Part 10: 代码质量检查清单
- 遵循 RESTful API 路径规范
- 使用统一响应格式
- 实现全局异常处理
- 添加权限注解
- 分页接口使用 PageResult
- 参数校验使用 @Valid
- 重要操作记录日志
- 使用 @Transactional 处理事务
- 实体继承 BaseEntity
- 使用 MyBatis-Plus 逻辑删除
- API 添加 @Operation 注解
- Service 接口与实现分离
Weekly Installs
3
Repository
youlaitech/youlai-skillsGitHub Stars
3
First Seen
13 days ago
Security Audits
Installed on
amp3
cline3
opencode3
cursor3
kimi-cli3
warp3