JMI-OPENATOMJMI-OPENATOM
首页
快速开始
  • 架构概览
  • 项目结构
  • 认证与权限
  • 数据库迁移
  • 配置说明
  • 开发规范
  • 架构概览
  • 项目结构
  • 路由与权限
  • API 请求
  • 组件库
  • UniApp 小程序
  • Docker 部署
  • CI/CD
  • Nginx 反向代理
  • 环境变量
  • QQ 机器人
  • 实验室管理系统
  • API 权限清单
  • 数据库表结构
  • 常见问题
首页
快速开始
  • 架构概览
  • 项目结构
  • 认证与权限
  • 数据库迁移
  • 配置说明
  • 开发规范
  • 架构概览
  • 项目结构
  • 路由与权限
  • API 请求
  • 组件库
  • UniApp 小程序
  • Docker 部署
  • CI/CD
  • Nginx 反向代理
  • 环境变量
  • QQ 机器人
  • 实验室管理系统
  • API 权限清单
  • 数据库表结构
  • 常见问题
  • 后端开发

    • 后端架构概览
    • 后端项目结构
    • 认证与权限
    • 数据库迁移(Flyway)
    • 配置说明
    • 后端开发规范

后端开发规范

代码风格

命名规范

类型规范示例
类名UpperCamelCaseClubService
方法名lowerCamelCasecreateClub()
变量名lowerCamelCaseclubName
常量UPPER_SNAKE_CASEMAX_PAGE_SIZE
包名全小写edu.jmi.openatom.server.openatomsystem
数据库表名snake_caseclub_membership
数据库字段snake_casecreated_at

类命名约定

类型命名模式示例
Controller{模块}ControllerClubController
Service 接口{模块}ServiceClubService
Service 实现{模块}ServiceImplClubServiceImpl
Mapper{实体}MapperClubMapper
Entity{实体名}Club
请求 DTORequest{动作}{对象}DTORequestCreateClubDTO
响应 VO{对象}VOClubDetailVO
枚举{领域}EnumUserStatus

分层规范

Controller 层

  • 只做:接收请求、参数校验、调用 Service、返回 Result
  • 不做:业务逻辑、数据库操作
  • 必须使用 @SaCheckPermission 注解标注权限要求
  • 必须使用 @Valid 校验请求参数
// ✅ 正确示例
@RestController
@RequestMapping("/clubs")
public class ClubController {

    @SaCheckPermission("club:create")
    @PostMapping
    public Result<Club> create(@Valid @RequestBody RequestCreateClubDTO dto) {
        return Result.success(clubService.create(dto));
    }
}

// ❌ 错误示例 - Controller 中写业务逻辑
@PostMapping
public Result<Club> create(@RequestBody RequestCreateClubDTO dto) {
    // 不要在 Controller 中写业务逻辑
    Club club = new Club();
    club.setName(dto.getName());
    club.setStatus(ClubStatus.ACTIVE);
    clubMapper.insert(club);
    return Result.success(club);
}

Service 层

  • 职责:业务逻辑处理
  • 使用 @Transactional 管理事务
  • 复杂业务逻辑拆分为私有方法
  • 异常使用业务异常抛出,由全局异常处理器统一处理

Mapper 层

  • 继承 BaseMapper<T>
  • 简单 CRUD 使用 MyBatis Plus 内置方法
  • 复杂查询使用 XML 或 @Select 注解
  • 分页使用 PageRequests 工具类

统一响应

所有 Controller 方法返回 Result<T>:

// 成功 - 无数据
return Result.success();

// 成功 - 有数据
return Result.success(data);

// 成功 - 有数据和自定义消息
return Result.success(data, "创建成功");

// 失败
return Result.error("参数错误");
return Result.error(400, "参数错误");

权限注解

接口级权限

@SaCheckPermission("club:create")      // 检查单个权限
@SaCheckPermission({"club:list", "club:detail"})  // 检查多个权限(AND)
@SaCheckPermission(value = "club:list", orRole = "super-admin")  // 权限或角色满足其一

登录校验

@SaCheckLogin  // 仅检查登录

新增功能开发流程

1. 创建/修改实体类

@TableName("new_feature")
@Data
public class NewFeature {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String name;
    
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createdAt;
    
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updatedAt;
}

2. 创建 Flyway 迁移脚本

在 backend/src/main/resources/db/migration/ 下创建 V{下一个版本号}__add_new_feature.sql。

3. 创建 Mapper 接口

@Mapper
public interface NewFeatureMapper extends BaseMapper<NewFeature> {
    // 复杂查询可在此定义
}

4. 创建 Service 接口和实现

public interface NewFeatureService {
    NewFeature create(RequestCreateNewFeatureDTO dto);
    NewFeature detail(Long id);
    Page<NewFeature> list(PageRequest request);
}

@Service
public class NewFeatureServiceImpl implements NewFeatureService {
    @Autowired
    private NewFeatureMapper newFeatureMapper;
    
    @Override
    @Transactional
    public NewFeature create(RequestCreateNewFeatureDTO dto) {
        NewFeature feature = new NewFeature();
        feature.setName(dto.getName());
        newFeatureMapper.insert(feature);
        return feature;
    }
    
    // ...其他方法
}

5. 创建 Controller

@RestController
@RequestMapping("/new-features")
public class NewFeatureController {
    @Autowired
    private NewFeatureService newFeatureService;
    
    @SaCheckPermission("new-feature:create")
    @PostMapping
    public Result<NewFeature> create(@Valid @RequestBody RequestCreateNewFeatureDTO dto) {
        return Result.success(newFeatureService.create(dto));
    }
    
    @SaCheckPermission("new-feature:list")
    @GetMapping
    public Result<Page<NewFeature>> list(PageRequest request) {
        return Result.success(newFeatureService.list(request));
    }
}

6. 注册权限点

在 SystemPermission 枚举中添加新权限点:

NEW_FEATURE_LIST("查询新功能", "new-feature:list", "api", "/new-features", "GET"),
NEW_FEATURE_CREATE("创建新功能", "new-feature:create", "api", "/new-features", "POST"),

实体类规范

  • 使用 @TableName 标注表名
  • 使用 @TableId 标注主键
  • 使用 @TableField 标注特殊字段(如自动填充)
  • 使用 Lombok @Data 或 @Getter 简化代码
  • 时间字段使用 LocalDateTime

异常处理

业务异常

直接抛出 RuntimeException 或其子类,全局异常处理器会捕获并转换为 Result.error():

if (club == null) {
    throw new RuntimeException("社团不存在");
}

参数校验

使用 @Valid + JSR 303 注解:

public class RequestCreateClubDTO {
    @NotBlank(message = "社团名称不能为空")
    @Size(max = 100, message = "社团名称最长100字符")
    private String name;
    
    @NotNull(message = "社团类型不能为空")
    private Integer type;
}

日志规范

  • 使用 SLF4J(Lombok @Slf4j 注解)
  • 生产环境日志级别:INFO
  • 开发环境日志级别:DEBUG
  • Sa-Token 日志:开发环境开启,生产环境关闭
@Slf4j
@Service
public class ClubService {
    public void createClub(RequestCreateClubDTO dto) {
        log.info("创建社团: {}", dto.getName());
        // ...
    }
}

编码约定

  • Java 文件编码:UTF-8
  • 换行符:LF(Unix)
  • 缩进:2 空格(项目使用 google-java-format)
  • 导入顺序:标准库 → 第三方库 → 项目内部
  • 不使用 * 通配符导入
Prev
配置说明