SKILL.md 27 KB

RuoYi-Cloud-Plus 后端开发技能

语言要求

所有对话、说明、注释均使用中文。 与用户的交互全程中文,代码注释也使用中文。

简介

本技能指导基于 RuoYi-Cloud-Plus 微服务框架的 Java 后端开发,涵盖代码结构、分层设计、Dubbo 远程调用等核心开发规范。

项目结构

ykt_server/
├── ruoyi-api/                    # API接口定义层
│   ├── ruoyi-api-backstage/      # backstage模块对外暴露的Dubbo接口
│   ├── ruoyi-api-system/         # system模块对外暴露的Dubbo接口
│   └── ...
├── ruoyi-modules/                # 业务模块实现层
│   ├── ruoyi-backstage/          # 后台管理模块
│   ├── ruoyi-ecs/                # ECS电子班牌模块
│   ├── ruoyi-system/             # 系统管理模块
│   └── ...
├── ruoyi-common/                 # 公共组件
│   ├── ruoyi-common-core/        # 核心工具类
│   ├── ruoyi-common-mybatis/     # MyBatis-Plus封装
│   ├── ruoyi-common-dubbo/       # Dubbo公共配置
│   └── ...
├── ruoyi-gateway/                # 网关服务
├── ruoyi-auth/                   # 认证中心
└── pom.xml                       # 父POM

核心框架栈

组件 技术 版本
基础框架 Spring Boot 3.1+
微服务框架 Spring Cloud Alibaba -
RPC框架 Apache Dubbo 3.X
ORM框架 MyBatis-Plus -
权限认证 Sa-Token + JWT -
注册中心 Nacos -
数据库连接池 HikariCP -
主键生成 雪花ID -

分层对象定义规范

1. Domain(实体类)

  • 位置:ruoyi-modules/{module}/domain/
  • 继承:TenantEntity(多租户)或 BaseEntity
  • 注解:@TableName 指定表名,@TableId 指定主键
  • 职责:与数据库表一一对应

    @Data
    @EqualsAndHashCode(callSuper = true)
    @TableName("t_pt_room")
    public class PtRoom extends TenantEntity {
    @TableId(value = "room_id")
    private Long roomId;
    private String roomName;
    // ...
    }
    

2. BO(业务对象)

  • 位置:ruoyi-modules/{module}/domain/bo/
  • 继承:${parentEntity}(主表为 TenantEntity,从表为 BaseEntity
  • 注解:必须有 @AutoMapper(target = Domain.class, reverseConvertGenerate = false) 配置与Domain的映射(主表、从表都一样)
  • 职责:用于Service层参数传递,包含校验注解

    @Data
    @EqualsAndHashCode(callSuper = true)
    @AutoMapper(target = PtRoom.class, reverseConvertGenerate = false)
    public class PtRoomBo extends TenantEntity {
    @NotNull(message = "房间Id不能为空", groups = { EditGroup.class })
    private Long roomId;
        
    @NotBlank(message = "房间名称不能为空", groups = { AddGroup.class, EditGroup.class })
    private String roomName;
    // ...
    }
    

3. VO(视图对象)

  • 位置:ruoyi-modules/{module}/domain/vo/
  • 实现:Serializable
  • 注解:必须有 @AutoMapper(target = Domain.class) 配置与Domain的映射(主表、从表都一样)
  • 职责:用于Controller层返回给前端的数据

    @Data
    @ExcelIgnoreUnannotated
    @AutoMapper(target = PtRoom.class)
    public class PtRoomVo implements Serializable {
    @ExcelProperty(value = "房间名称")
    private String roomName;
        
    @Translation(type = TransConstant.XXX, mapper = "fieldName")
    private String fieldName;
    // ...
    }
    

4. DTO(数据传输对象)

  • 位置:ruoyi-api/{api-module}/domain/dto/
  • 实现:Serializable
  • 职责:用于跨服务(Dubbo)传输的数据对象

    @Data
    public class RemoteRoomDto implements Serializable {
    private Long roomId;
    private String roomName;
    // ...
    }
    

模块生成场景

1. 创建模块

位置ruoyi-modules/ruoyi-{模块名}/

生成内容

  • Maven 模块目录结构
  • pom.xml(引用父POM、添加依赖)
  • 基础包结构(直接在模块包下):

    src/main/java/org/dromara/{模块名}/
    ├── controller/          # 控制器
    ├── domain/              # 实体类
    │   ├── bo/              # 业务对象
    │   └── vo/              # 视图对象
    ├── mapper/              # Mapper接口
    ├── service/             # Service接口
    │   └── impl/            # Service实现
    ├── dubbo/               # Dubbo服务实现(可选)
    └── listener/            # Excel导入监听器(可选)
    
  • 如需Dubbo暴露,同步创建 ruoyi-api/ruoyi-api-{模块名}/

  • 重要:在 ruoyi-api/ruoyi-api-bom/pom.xml<dependencyManagement> 中添加 API 模块依赖:

    <dependency>
      <groupId>org.dromara</groupId>
      <artifactId>ruoyi-api-{模块名}</artifactId>
      <version>${revision}</version>
    </dependency>
    

2. 创建子模块

位置controller/{子模块名}/

生成内容

  • 子模块包目录(仅一层,不再嵌套basics等额外目录)

3. 创建功能

功能创建有两个路径:

路径1:模块下直接创建功能

  • controller/{功能名}Controller.java

路径2:子模块下创建功能

  • controller/{子模块名}/{功能名}Controller.java

生成具体文件: Domain、Bo、Vo、Mapper、Service、ServiceImpl、Controller (如有Dubbo:RemoteService接口 + RemoteServiceImpl实现)

Controller 层规范

  1. 请求路径格式/{子模块名}/{功能名}/{功能名}

    • 示例:/basicParameter/ptParameter
  2. 权限字符格式{模块名}:{功能名}:{操作名}

    • 模块名:对应子模块目录名(如 basicParameter
    • 功能名:对应功能Controller名(如 ptParameter
    • 操作名:list(列表)、query(详情)、add(新增)、edit(修改)、remove(删除)、export(导出)
    • 示例:basicParameter:ptParameter:add
  3. 标准方法模板

    /**
    * 查询列表
    */
    @SaCheckPermission("{模块名}:{功能名}:list")
    @GetMapping("/list")
    public TableDataInfo<Vo> list(Bo bo, PageQuery pageQuery) {
       return service.queryPageList(bo, pageQuery);
    }
    
    /**
    * 导出数据
    */
    @SaCheckPermission("{模块名}:{功能名}:export")
    @Log(title = "功能标题", businessType = BusinessType.EXPORT)
    @PostMapping("/export")
    public void export(Bo bo, HttpServletResponse response) {
       List<Vo> list = service.queryList(bo);
       ExcelUtil.exportExcel(list, "导出标题", Vo.class, response);
    }
    
    /**
    * 获取详情
    */
    @SaCheckPermission("{模块名}:{功能名}:query")
    @GetMapping("/{id}")
    public R<Vo> getInfo(@NotNull(message = "主键不能为空") @PathVariable Long id) {
       return R.ok(service.queryById(id));
    }
    
    /**
    * 新增数据
    */
    @SaCheckPermission("{模块名}:{功能名}:add")
    @Log(title = "功能标题", businessType = BusinessType.INSERT)
    @RepeatSubmit()
    @PostMapping()
    public R<Void> add(@Validated(AddGroup.class) @RequestBody Bo bo) {
       return toAjax(service.insertByBo(bo));
    }
    
    /**
    * 修改数据
    */
    @SaCheckPermission("{模块名}:{功能名}:edit")
    @Log(title = "功能标题", businessType = BusinessType.UPDATE)
    @RepeatSubmit()
    @PutMapping()
    public R<Void> edit(@Validated(EditGroup.class) @RequestBody Bo bo) {
       return toAjax(service.updateByBo(bo));
    }
    
    /**
    * 删除数据
    */
    @SaCheckPermission("{模块名}:{功能名}:remove")
    @Log(title = "功能标题", businessType = BusinessType.DELETE)
    @DeleteMapping("/{ids}")
    public R<Void> remove(@NotEmpty(message = "主键不能为空") @PathVariable Long[] ids) {
       return toAjax(service.deleteWithValidByIds(List.of(ids), true));
    }
    
    /**
    * 导出导入模板
    */
    @SaCheckPermission("{模块名}:{功能名}:export")
    @Log(title = "导入模板", businessType = BusinessType.EXPORT)
    @PostMapping("/exportTemplate")
    public void exportTemplate(HttpServletResponse response) {
       List<ImportVo> list = new ArrayList<>();
       ExcelUtil.exportExcel(list, "导入模板", ImportVo.class, response);
    }
    
    /**
    * 导入数据
    */
    @SaCheckPermission("{模块名}:{功能名}:import")
    @Log(title = "数据导入", businessType = BusinessType.IMPORT)
    @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public R<Void> importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception {
       ExcelResult<ImportVo> result = ExcelUtil.importExcel(
           file.getInputStream(), 
           ImportVo.class, 
           new ImportListener(updateSupport)
       );
       return R.ok(result.getAnalysis());
    }
    
  4. 导入功能相关文件

    • ImportVo:导入专用的VO类,使用 @ExcelProperty 注解标记Excel列
    • ImportListener:继承 AnalysisEventListener<ImportVo> 实现 ExcelListener<ImportVo> 接口
      • 位置:listener/{功能名}ImportListener.java
      • 通过 SpringUtils.getBean() 获取Service
      • invoke() 方法中处理每行数据
      • getExcelResult() 中返回导入结果

开发工作流程

场景1:基础数据模块开发(CRUD)

  1. 创建 Domain 实体类

    • 继承 TenantEntityBaseEntity
    • 使用 @TableName@TableId 注解
  2. 创建 BO 业务对象

    • 继承 ${parentEntity}(主表 TenantEntity,从表 BaseEntity
    • 必须有 @AutoMapper(target = Domain.class, reverseConvertGenerate = false) 注解(主表、从表都一样)
    • 添加校验注解(@NotNull, @NotBlank 等)
  3. 创建 VO 视图对象

    • 实现 Serializable
    • 必须有 @AutoMapper(target = Domain.class) 注解(主表、从表都一样)
    • 添加 Excel 导出注解(可选)
  4. 创建 Mapper 接口

    • 继承 BaseMapper<Domain>
  5. 创建 Service 接口和实现

    • 接口继承 IService<Domain>
    • 实现类使用 @RequiredArgsConstructor 注入依赖
  6. 创建 Controller

    • 使用 @RestController@RequestMapping
    • 使用 @SaCheckPermission 配置权限
    • 继承 BaseController 获取通用方法

场景2:Dubbo 接口暴露

  1. 在 ruoyi-api 模块定义接口

    public interface RemoteXxxService {
       R<RemoteXxxDto> getById(Long id);
       TableDataInfo<RemoteXxxDto> selectPage(RemoteXxxQueryDto query);
    }
    
  2. 创建 DTO 和 QueryDTO

    • DTO 用于数据传输
    • QueryDTO 用于查询条件(包含分页参数)
  3. 在 ruoyi-modules 模块实现接口

    @DubboService
    public class RemoteXxxServiceImpl implements RemoteXxxService {
       // 实现方法
    }
    
  4. 注册 Dubbo 服务

    • 实现类添加 @DubboService 注解
    • 框架会自动暴露服务

场景3:跨服务调用 Dubbo 接口

  1. 在 Consumer 端注入远程服务

    @DubboReference
    private RemoteXxxService remoteXxxService;
    
  2. 直接调用远程方法

    R<RemoteXxxDto> result = remoteXxxService.getById(id);
    

关键转换模式

DTO → BO 转换(Dubbo接口实现层)

@Override
public TableDataInfo<RemoteTraineeDto> selectTraineePage(RemoteTraineeQueryDto queryDto) {
    // 1. DTO → BO(手动设置查询条件)
    PtUserAccountBo bo = new PtUserAccountBo();
    bo.setCategory(TRAINEE_CATEGORY);
    if (queryDto.getRealName() != null) {
        bo.setRealName(queryDto.getRealName());
    }
    
    // 2. 构建分页参数
    PageQuery pageQuery = new PageQuery();
    pageQuery.setPageNum(queryDto.getPageNum() != null ? Math.max(queryDto.getPageNum(), 1) : 1);
    pageQuery.setPageSize(queryDto.getPageSize() != null ? Math.min(Math.max(queryDto.getPageSize(), 1), 500) : 10);
    
    // 3. 调用本地Service
    TableDataInfo<PtUserAccountVo> result = userAccountService.queryPageList(bo, pageQuery);
    
    // 4. VO → DTO 转换
    List<RemoteTraineeDto> dtoList = result.getRows().stream()
        .map(this::convertToTraineeDto)
        .toList();
    
    // 5. 构建返回结果
    TableDataInfo<RemoteTraineeDto> pageData = TableDataInfo.build();
    pageData.setRows(dtoList);
    pageData.setTotal(result.getTotal());
    return pageData;
}

private RemoteTraineeDto convertToTraineeDto(PtUserAccountVo vo) {
    if (vo == null) return null;
    RemoteTraineeDto dto = new RemoteTraineeDto();
    dto.setUserId(vo.getUserId());
    dto.setRealName(vo.getRealName());
    // 手动映射字段...
    return dto;
}

VO → DTO 转换(使用 MapstructUtils)

// 简单对象转换
RemoteUserAccountVo vo = userAccountService.queryById(userId);
RemoteUserAccountVo result = MapstructUtils.convert(vo, RemoteUserAccountVo.class);

// 列表转换
List<RemoteUserAccountVo> voList = userAccountService.queryList(bo);
return MapstructUtils.convert(voList, RemoteUserAccountVo.class);

Bean 拷贝(使用 Hutool)

// BO → Domain 转换(在Service层)
PtUserAccountBo ptUserAccountBo = BeanUtil.copyProperties(bo, PtUserAccountBo.class);

分页处理规范

分页查询参数

@Data
public class RemoteXxxQueryDto implements Serializable {
    private Integer pageNum = 1;    // 页码,默认1
    private Integer pageSize = 10;  // 每页条数,默认10
    private String keyword;         // 查询关键字
    // ... 其他查询条件
}

分页查询实现

@Override
public TableDataInfo<RemoteXxxDto> selectPage(RemoteXxxQueryDto queryDto) {
    // 1. 构建分页参数
    PageQuery pageQuery = new PageQuery();
    pageQuery.setPageNum(queryDto.getPageNum() != null ? Math.max(queryDto.getPageNum(), 1) : 1);
    pageQuery.setPageSize(queryDto.getPageSize() != null ? Math.min(Math.max(queryDto.getPageSize(), 1), 500) : 10);
    
    // 2. 构建查询条件
    XxxBo bo = new XxxBo();
    // 设置查询条件...
    
    // 3. 执行查询
    TableDataInfo<XxxVo> result = xxxService.queryPageList(bo, pageQuery);
    
    // 4. 转换并返回
    List<RemoteXxxDto> dtoList = MapstructUtils.convert(result.getRows(), RemoteXxxDto.class);
    TableDataInfo<RemoteXxxDto> pageData = TableDataInfo.build();
    pageData.setRows(dtoList);
    pageData.setTotal(result.getTotal());
    return pageData;
}

常用注解速查

实体类注解

注解 用途 位置
@TableName 指定数据库表名 Domain
@TableId 指定主键字段 Domain
@TableLogic 逻辑删除标记 Domain
@AutoMapper 配置对象映射(Bo/Vo 都必须有 BO/VO
@ExcelProperty Excel导出配置 VO
@Translation 数据翻译(字典/关联) VO

校验注解

注解 用途
@NotNull 非空校验(允许空字符串)
@NotBlank 非空白校验(不允许null、空字符串、空白字符)
@NotEmpty 非空校验(用于集合、字符串)
@Size 长度/大小范围校验
@Pattern 正则表达式校验

Dubbo 注解

注解 用途
@DubboService 暴露 Dubbo 服务
@DubboReference 引用 Dubbo 服务

权限注解

注解 用途
@SaCheckPermission 检查权限标识
@SaCheckRole 检查角色
@SaCheckLogin 检查登录状态

代码模板

技能目录下 templates/ 文件夹包含以下代码模板:

  • Domain.java.template - 实体类模板
  • Bo.java.template - 业务对象模板
  • Vo.java.template - 视图对象模板
  • Dto.java.template - 数据传输对象模板
  • ImportVo.java.template - 导入专用VO模板
  • Service.java.template - Service接口模板
  • ServiceImpl.java.template - Service实现模板
  • Controller.java.template - 控制器模板
  • Mapper.java.template - Mapper接口模板
  • RemoteService.java.template - Dubbo接口模板
  • RemoteServiceImpl.java.template - Dubbo实现模板
  • ImportListener.java.template - Excel导入监听器模板

模板变量说明

变量 说明 示例
${module} 模块名(小写) ecs
${submodule} 子模块包名 term
${ClassName} 类名(首字母大写) EcsTerm
${tableComment} 表注释 设备信息
${pkField} 主键字段名(驼峰) termId
${pkType} 主键类型 Long
${PkCapField} 主键字段名(首字母大写,用于getter/setter) TermId
${author} 作者 ruoyi
${date} 日期 2026-04-20
${parentEntity} 父实体类 TenantEntity / BaseEntity
${queryWrapperConditions} 查询条件代码块(由生成器根据字段自动生成) 见下方说明

ServiceImpl 查询规范(重要)

ServiceImpl 必须使用 LambdaQueryWrapper 模式,禁止直接传递 Bo 对象给 Mapper。

// ❌ 错误写法(BaseMapperPlus 没有此方法签名)
return baseMapper.selectVoList(bo);
return baseMapper.selectVoPageList(bo, pageQuery);

// ✅ 正确写法
LambdaQueryWrapper<EcsTerm> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);                                    // 列表查询
Page<EcsTermVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);  // 分页查询
return TableDataInfo.build(result);

buildQueryWrapper 生成规则

  • String 类型字段 → lqw.like(StringUtils.isNotBlank(bo.getXxx()), Entity::getXxx, bo.getXxx())
  • 字典字段(dictType,String 类型) → lqw.eq(StringUtils.isNotBlank(bo.getXxx()), Entity::getXxx, bo.getXxx())
  • Integer/Long 类型字段(非字典的精确匹配) → lqw.eq(bo.getXxx() != null, Entity::getXxx, bo.getXxx())
  • 日期范围字段 → lqw.ge(bo.getBeginTime() != null, Entity::getXxx, bo.getBeginTime()) + lqw.le(...)
  • 分页查询默认按 createTime 降序排列:lqw.orderByDesc(Entity::getCreateTime)

insertByBo 规范

  • 转换后需调用 validEntityBeforeSave(Objects.requireNonNull(add)) 进行保存前校验
  • 插入成功后回填主键:bo.set${PkCapField}(add.get${PkCapField}())

updateByBo 规范

  • 转换后需 null 检查 + 调用 validEntityBeforeSave

deleteWithValidByIds 规范

  • isValid=true 时预留校验逻辑位置

注意事项

  1. 对象转换:DTO/VO/BO 之间的转换优先使用 MapstructUtils,复杂场景手动转换
  2. 分页参数:pageSize 需要限制最大值(通常500),防止内存溢出
  3. 校验分组:新增和编辑使用不同的校验分组(AddGroup, EditGroup
  4. Dubbo 调用:远程接口返回类型建议使用 R<T> 包装,便于错误处理
  5. 租户隔离:多租户表必须继承 TenantEntity,单表继承 BaseEntity
  6. 字典命名:字典类型(dictType)是全局的,命名不加模块前缀。例如用 course_type 而非 ecs_course_type,用 data_source 而非 ecs_data_source
  7. 字典字段类型:数据字典(dictType)对应的数据库表字段类型为 varchar(16),Java 实体/Bo/Vo 中对应类型为 String(而非 Integer)。字典值虽然常为数字,但存储和传输统一使用字符串。建表语句中字典字段用 character varying(16 char)varchar(16)
  8. 主键不导出:主键字段(Long 类型)不加 @ExcelProperty 注解,不参与 Excel 导出。Vo 中主键仅作为标识,不需要在导出文件中展示

附录:从需求规格说明书生成代码

本章节定义基于需求规格说明书自动生成后端代码的完整流程。由 /dev 指令触发。

A.1 输入文件

需求规格说明书位于:d:/dt_ykt/ykt_server/doc/{模块}-{功能名}-requirements.md

文件不存在时,提示用户先执行 /require {模块} {功能名}

A.2 读取与解析

依次读取需求规格说明书的以下章节:

章节 用途
基础信息 确定包路径、表名、多租户标记
字段清单 生成 Domain/Bo/Vo 各字段
VO 结构 生成列表VO、明细VO的类型定义
接口清单 生成 Controller 方法
特殊需求 判断是否生成 ImportVo/Listener、Dubbo接口
字典项 生成 VO 的 @ExcelDictFormat 注解

A.3 后端文件生成清单

根据需求文档内容,按需生成以下文件:

A.3.1 主表文件(必有)

文件 路径 说明
Domain domain/{Name}.java 表名=基础信息.主表名,字段=字段清单中 inDb=true
Bo domain/bo/{Name}Bo.java 字段=字段清单中 inForm=true,含校验注解
Vo domain/vo/{Name}Vo.java 字段=字段清单中 inTable/inForm=true,含Excel注解
Mapper mapper/{Name}Mapper.java 继承 BaseMapper<Domain>
Service service/I{Name}Service.java 继承 IService<Domain>
ServiceImpl service/impl/{Name}ServiceImpl.java 实现 Service,含 CRUD + 导入导出逻辑
Controller controller/{Name}Controller.java REST API,含权限注解

A.3.2 从表文件(如有 relation 字段)

文件 路径 说明
Domain(从表) domain/{SubName}.java 表名=基础信息.从表名
Bo(从表) domain/bo/{SubName}Bo.java 必须有 @AutoMapper(target = {SubName}.class, reverseConvertGenerate = false) + 主表ID外键字段,继承 BaseEntity
Vo(从表) domain/vo/{SubName}Vo.java 必须有 @AutoMapper(target = {SubName}.class) + 主表ID、关联字段
Mapper(从表) mapper/{SubName}Mapper.java
Service(从表) service/I{SubName}Service.java 含批量插入方法
ServiceImpl(从表) service/impl/{SubName}ServiceImpl.java

A.3.3 导入导出文件(特殊需求.导入=true)

文件 路径 说明
ImportVo domain/vo/{Name}ImportVo.java 导入专用VO,含 @ExcelProperty
ImportListener listener/{Name}ImportListener.java 继承 AnalysisEventListener<ImportVo>

A.3.4 Dubbo 接口文件(特殊需求.Dubbo暴露=true)

文件 路径 说明
RemoteService dubbo/Remote{Name}Service.java Dubbo 接口定义
RemoteDto dto/Remote{Name}Dto.java Dubbo 传输对象
RemoteServiceImpl dubbo/Remote{Name}ServiceImpl.java @DubboService 实现

A.4 代码生成模板

使用技能目录下 templates/ 中的模板文件填充:

主表:

templates/
├── Domain.java.template      → domain/{Name}.java
├── Bo.java.template           → domain/bo/{Name}Bo.java
├── Vo.java.template           → domain/vo/{Name}Vo.java
├── Mapper.java.template       → mapper/{Name}Mapper.java
├── Service.java.template      → service/I{Name}Service.java
├── ServiceImpl.java.template  → service/impl/{Name}ServiceImpl.java
├── Controller.java.template   → controller/{Name}Controller.java
├── ImportVo.java.template     → domain/vo/{Name}ImportVo.java
├── ImportListener.java.template → listener/{Name}ImportListener.java
├── RemoteService.java.template → dubbo/Remote{Name}Service.java
├── RemoteServiceImpl.java.template → dubbo/Remote{Name}ServiceImpl.java
└── Dto.java.template          → dto/Remote{Name}Dto.java

从表(复用主表模板,替换 {Name} → {SubName}):

⚠️ 从表模板与主表使用同一套模板,关键差异:

  • ${parentEntity} 变量:主表为 TenantEntity,从表为 BaseEntity
  • 从表 Bo 必须包含 @AutoMapper(target = {SubName}.class, reverseConvertGenerate = false) 注解
  • 从表 Vo 必须包含 @AutoMapper(target = {SubName}.class) 注解
  • 不管主表还是从表,Bo 和 Vo 都必须有 @AutoMapper 映射注解,这是框架对象转换(MapstructUtils.convert)的前提
Domain.java.template      → domain/{SubName}.java
Bo.java.template           → domain/bo/{SubName}Bo.java
Vo.java.template           → domain/vo/{SubName}Vo.java          ← 必须!含 @AutoMapper
Mapper.java.template       → mapper/{SubName}Mapper.java
Service.java.template      → service/I{SubName}Service.java
└── ServiceImpl.java.template  → service/impl/{SubName}ServiceImpl.java

A.5 字段到代码属性的映射

需求字段属性 Domain 字段 Bo 校验注解 Vo Excel注解
fieldType=Long(主键) private Long xxxId; @NotNull(groups=EditGroup.class) 不导出(不加 @ExcelProperty
fieldType=String, required=true private String xxxName; @NotBlank @ExcelProperty("名称")
fieldType=Integer, dictType=xxx private Integer xxxType; @ExcelProperty("类型"), @ExcelDictFormat(dictType="xxx")
dictType=xxx(字典字段) private String xxxType; @ExcelProperty("类型"), @ExcelDictFormat(dictType="xxx")
fieldType=BigDecimal private BigDecimal xxxAmount; @ExcelProperty("金额")
fieldType=Date private Date xxxTime; @ExcelProperty("时间")
inDb=false —(不出现在 Domain) private String teacherNames;(纯计算字段)
inTable=false, inForm=true —(不出现在 Domain) private String remark;(仅表单)
从表关联字段(Bo/Vo) private List<{SubName}Vo> {Name}{Sub}List;

A.6 多对多关联处理

主表 Domain 中不直接存储讲师ID,而是:

  1. 从表 {Name}Teacher.java 存储 {xxxId}{teacherId}{teacherName}
  2. 主表列表查询时,通过 SQL GROUP_CONCAT 拼接 {teacherNames},返回到 VoinDb=false 字段
  3. 列表页面通过 teacherNames 直接展示讲师列表

A.7 输出路径约定

资源 路径
后端项目根 d:/dt_ykt/ykt_server(可通过 --backend= 参数覆盖)
主表代码 d:/dt_ykt/ykt_server/ruoyi-modules/ruoyi-{模块}/src/main/java/org/dromara/{模块}/
Dubbo API d:/dt_ykt/ykt_server/ruoyi-api/ruoyi-api-{模块}/src/main/java/org/dromara/{模块}/api/

A.8 权限字符生成规则

格式:{子模块名(如basics)}:{功能名}:{操作名}

示例:对于 ruoyi-backstage/basics/room/CourseController.javabasics:course:list

A.9 注意事项

  1. 覆盖确认:文件已存在时,提示用户确认是否覆盖
  2. 子模块路径:优先使用 controller/{子模块}/{功能名}Controller.java
  3. 多租户:多租户表继承 TenantEntity,单表继承 BaseEntity
  4. 包名大小写:类名首字母大写,包路径全小写