RuoYi-Vue-Plus 后端 CRUD 开发规范:基于分层架构的单表增删改查实战指南
2026/9/18 2:16:30 网站建设 项目流程

RuoYi-Vue-Plus 后端 CRUD 开发规范:基于分层架构的单表增删改查实战指南

【免费下载链接】RuoYi-Vue-Plus多租户后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-Token、Mybatis-Plus、WarmFlow、SpringDoc、Hutool、OSS 定期同步项目地址: https://gitcode.com/GitHub_Trending/ru/RuoYi-Vue-Plus

RuoYi-Vue-Plus 为开发者沉淀了一套"标准后端 CRUD"的工程规范,覆盖新增单表业务时从 entity/bo/vo/mapper/service/controller 的完整搭建链路,并统一了分页查询、导出、删除前校验等约定。本文以仓库中backend-crud规范文档为主体,结合ruoyi-system模块中角色管理等标准实现与ruoyi-common-mybatis的底层封装源码,逐层讲解这套 CRUD 开发模式的落地方式。读完本文,你将掌握在 RuoYi-Vue-Plus 中新增一个标准管理模块的全部约定、关键注解用法与可复制的代码骨架。

一、总览:一套稳定的 CRUD 分层结构

规范的首要原则是"先参考、再动手":

  1. 先参考ruoyi-modules/ruoyi-gen模块下的代码生成器模板;
  2. 再参考当前模块内最近似的标准管理模块(例如ruoyi-system中的角色/用户/部门等模块);
  3. 分层保持稳定,包结构固定为:
domain -- 实体类(Entity) domain.bo -- 业务对象(接收请求参数) domain.vo -- 视图对象(返回前端数据) mapper -- 数据访问层 service -- 业务接口 service.impl -- 业务实现 controller -- 控制器

这一约定在仓库中得到了完整印证:以角色管理为例,其分层文件依次位于 SysRole.java、SysRoleBo.java、SysRoleVo.java、SysRoleMapper.java、ISysRoleService.java、SysRoleServiceImpl.java、SysRoleController.java。新增业务时照此包结构对号入座即可。

二、Entity 层约定:基类继承与 MyBatis-Plus 注解

实体(Entity)是数据库表的映射,规范要求:

  • 默认继承BaseEntity:BaseEntity 位于 BaseEntity.java,内置了createDept(创建部门)、createBy(创建者)、createTime(创建时间)、updateBy(更新者)、updateTime(更新时间)五个审计字段,并全部通过@TableField(fill = FieldFill.INSERT / FieldFill.INSERT_UPDATE)声明了自动填充策略,写入数据时无需手动赋值。
  • 表名与主键注解:实体使用@TableName标注表名,主键字段使用@TableId
  • 逻辑删除与乐观锁:若表存在delFlag逻辑删除字段、乐观锁版本字段,必须保留@TableLogic@Version注解,由 MyBatis-Plus 自动完成逻辑删除过滤与乐观锁校验。

这套约定保证了所有实体具备统一的审计字段与通用 CRUD 能力,也是后续BaseMapperPlus泛型方法能够直接生效的前提。

三、Mapper 层:继承 BaseMapperPlus<T, V>

规范要求 mapper 默认继承BaseMapperPlus<Entity, Vo>。该接口位于 BaseMapperPlus.java,是对 MyBatis-PlusBaseMapper的二次封装,核心能力包括:

  • 泛型双绑定BaseMapperPlus<T, V>同时持有实体类型 T 与 VO 类型 V,并通过ClassValue缓存解析结果(见TYPE_ARGUMENT_CACHE),避免重复反射解析。
  • VO 直查方法族selectVoByIdselectVoByIdsselectVoByMapselectVoOneselectVoListselectVoPage等,底层流程均为"按条件查出实体列表 → 通过MapstructUtils.convert转换为 VO",例如selectVoList的实现是this.selectList(wrapper)后调用MapstructUtils.convert(list, voClass)
  • 批量操作insertBatchupdateBatchByIdinsertOrUpdateBatch三个批量方法,底层委托 MyBatis-Plus 的Db.saveBatch/Db.updateBatchById/Db.saveOrUpdateBatch,并支持自定义batchSize
  • 链式查询入口lambda()返回LambdaCrudChainWrapper<T, V>lambdaUpdate()返回LambdaUpdateChainWrapper<T>。其中lambda()内部通过 Spring 容器获取 Mapper 代理对象(mapperProxy()),确保 Mapper 上的切面注解继续生效。

实际使用示例(来自角色模块的分页查询):

Page<SysRoleVo> page = roleMapper.selectPageRoleList(pageQuery.build(), this.buildQueryWrapper(role)); return PageResult.build(page.getRecords(), page.getTotal());

四、BO / VO / Entity 职责分离

规范强调三层职责严格分离:

  • BO(业务对象):接收请求与查询扩展字段,使用@AutoMapper(target = Entity.class, reverseConvertGenerate = false)声明到实体的映射关系。reverseConvertGenerate = false表示只生成"BO → Entity"方向的映射,不反向生成。BO 上同时承载参数校验注解(@NotBlank@Size@NotNull等),例如 SysRoleBo.java 中的角色名称校验:
    @NotBlank(message = "角色名称不能为空") @Size(min = 0, max = 30, message = "角色名称长度不能超过{max}个字符") private String roleName;
  • VO(视图对象):返回给前端的展示对象,使用@AutoMapper(target = Entity.class)声明与实体的映射。展示派生字段、@Translation翻译注解、导出注解@ExcelProperty都放在 VO 上。例如 SysRoleVo.java 中大量使用@ExcelProperty(value = "角色序号")定义导出列,并用@ExcelProperty(value = "数据范围", converter = ExcelDictConvert.class)挂接字典转换器。
  • BO/VO 转换:BO 转实体统一使用MapstructUtils.convert(bo, Entity.class)(MapstructUtils 位于 ruoyi-common-core,由@AutoMapper注解在编译期生成映射代码)。

五、Service 层:默认方法集合

标准业务 Service 接口默认包含六个方法,构成了 CRUD 的完整闭环:

方法职责
queryById按主键查询详情
queryPageList分页查询列表
queryList无条件/条件查询全部列表
insertByBo新增(接收 BO)
updateByBo修改(接收 BO)
deleteWithValidByIds批量删除,删除前进行业务校验

实现要点:

  • 写入前校验:优先放在validEntityBeforeSave(...)方法中统一完成,保证新增与修改走同一校验逻辑。
  • 业务级校验:如唯一性校验、数据权限校验,可在insertByBo/updateByBo内显式调用。角色模块即为典型参考:新增前调用checkRoleNameUniquecheckRoleKeyUnique(见 SysRoleServiceImpl.java)。
  • 修改/删除后联动:数据变更后可发布 Spring 事件做联动清理,例如角色信息变更后发布OnlineUserCleanEvent.byRole(role.getRoleId())清理在线用户缓存。

六、Controller 层:接口规则与标准路由

Controller 规范要点:

  • 继承BaseController:BaseController 位于 ruoyi-common-web,提供toAjax(...)等通用返回转换方法。
  • 返回值统一R<T>R<Void>:成功用R.ok(...),失败用R.fail(...)
  • 标准 CRUD 路由(以 SysRoleController.java 为模板):
方法路由说明
GET/list分页列表,返回R<PageResult<Vo>>
POST/export导出 Excel
GET/{id}查询详情
POST(空)新增
PUT(空)修改
DELETE/{ids}批量删除,ids逗号分隔
  • 注解使用约定

    • 每个接口按需添加@SaCheckPermission权限校验,权限标识遵循${module}:${business}:${action}三段式,例如system:role:listsystem:role:addsystem:role:editsystem:role:export
    • 写操作(增删改)添加@Log(title = "...", businessType = BusinessType.INSERT / UPDATE / DELETE / EXPORT)记录操作日志;
    • 新增与修改添加@RepeatSubmit()防重复提交(注解位于 ruoyi-common-redis);
    • 请求参数 BO 使用@Validated @RequestBody触发校验。
  • 导出接口固定为POST /export,实现方式参考角色模块:

    @Log(title = "角色管理", businessType = BusinessType.EXPORT) @SaCheckPermission("system:role:export") @PostMapping("/export") public void export(SysRoleBo role, HttpServletResponse response) { List<SysRoleVo> list = roleService.selectRoleList(role); ExcelBuilder.of(list, SysRoleVo.class).sheetName("角色数据").toResponse(response); }

    ExcelBuilder位于 ruoyi-common-excel,基于 VO 上的@ExcelProperty注解自动生成 Excel 响应流。

七、查询规则:QueryBuilder、LambdaQueryCondition 与分页

规范对查询层有明确的统一约定:

1. 查询构造入口

单表查询优先返回LambdaQueryWrapper;新增 generator 风格代码优先使用QueryBuilder.lambda(Entity.class).build()QueryBuilder位于 QueryBuilder.java,提供三个静态入口:

  • lambda(Class<T> entityClass):单表 Lambda 查询,内部构造AggregateLambdaQueryWrapper
  • lambdaJoin(Class<T> entityClass):基于 MPJ(MyBatis-Plus Join)的联表查询;
  • lambdaJoin(String alias, Class<T> entityClass):带主表别名的联表查询。

2. 链式条件风格

项目的公共链式查询支持QueryBuilder.lambda(...)BaseMapperPlus#lambda()LambdaCrudChainWrapperLambdaQueryCondition四种风格。其中LambdaQueryCondition(位于 LambdaQueryCondition.java)扩展了eq/ne/gt/ge/lt/le等条件方法,统一支持布尔condition参数控制"条件是否生效",并在此基础上封装了IfPresent/IfText/IfNotEmpty风格方法——当入参为空时自动跳过该条件,避免手写大量if (x != null)判断。

3. 日期范围约定

日期范围查询默认从bo.getParams()中读取begin/end键值。BO 通常内置Map<String, Object> params字段存放这类非结构化查询扩展参数,Service 层构建 Wrapper 时从中提取beginTime/endTime拼接到查询条件。

4. 分页返回约定

分页优先返回PageResult<Vo>PageQuery(位于 PageQuery.java)封装了pageNum(默认 1)、pageSize(默认Integer.MAX_VALUE)、orderByColumnisAsc四个分页参数,提供build()方法将其转换为 MyBatis-Plus 的Page对象;PageResult.build(records, total)(位于 PageResult.java)统一组装分页响应结构。

5. 实体转换

BO 转实体统一使用MapstructUtils.convert(bo, Entity.class),与@AutoMapper注解配合实现编译期类型安全映射。

八、代码生成器与模板约定

规范的起点是代码生成器。文档约定生成器模板位于ruoyi-modules/ruoyi-gen/src/main/resources/vm/下(注:当前仓库快照中该资源目录未随代码一并提交,若需查看模板需在本地生成环境执行代码生成命令后产出),生成器模块的核心逻辑位于 GenController.java、GenTableServiceImpl.java 与 GenUtils.java。

使用生成器时需注意一个命名约定:

代码生成器模板按类名首字母小写命名 Mapper 字段,例如SysRoleMappersysRoleMapper;手写业务代码时则可以使用具体业务短名。

即:由生成器产出的 Service 实现中,Mapper 注入字段名一律取类名首字母小写;而手写业务时可以用更贴合业务语义的短名。这一约定保证了生成代码与手写代码在团队内保持一致的辨识度。

此外,规范明确"不能只满足于 generator 裸产物":生成后需要继续补齐项目约定——包括 BO/VO 职责分离是否到位、导出与分页与删除前校验是否齐全、权限标识是否按三段式规范命名等,最终交付的是符合工程规范的代码而非"能跑就行"的生成物。

九、前端同步约定

CRUD 改动往往伴随前端联调,规范要求前端api/types与 Vue 的index.vue(或 React 的index.tsx)同步更新,且必须与后端保持一致:

  • 接口路径:与后端 Controller 路由一一对应(/list/export/{id}POSTPUTDELETE /{ids});
  • 返回结构:列表接口对应PageResult<Vo>结构,其余接口对应R<T>/R<Void>结构;
  • 日期范围参数:查询表单提交的日期区间字段名(begin/end或自定义别名)需与后端bo.getParams()中读取的键一致,否则会导致查询条件丢失。

十、交付前自检清单

规范在最后给出了 CRUD 交付前的自检项,这也是每次新增模块的验收标准:

  1. CRUD 链路是否完整queryByIdqueryPageListqueryListinsertByBoupdateByBodeleteWithValidByIds六个默认方法是否齐全;
  2. BO / VO / Entity 职责是否分离:请求与查询扩展字段是否放在 BO,展示派生字段与翻译/导出注解是否放在 VO;
  3. 导出、分页、删除前校验是否齐全POST /export导出、PageResult<Vo>分页、validEntityBeforeSave/ 删除前业务校验是否都已实现;
  4. 是否只是 generator 裸产物:如果是,需要继续补齐项目约定(权限标识、日志注解、防重复提交、命名规范等);
  5. 前端是否同步api/types与页面组件的接口路径、返回结构、日期范围参数是否与后端一致。

以 SysRoleController.java 为代表的角色管理模块,就是上述全部约定的完整落地样例——从@SaCheckPermission权限、@Log日志、@RepeatSubmit防重、PageResult分页到ExcelBuilder导出,一应俱全,可作为新增单表 CRUD 模块时的"最近似标准管理模块"参照物直接对照开发。

【免费下载链接】RuoYi-Vue-Plus多租户后台管理系统 重写RuoYi-Vue所有功能 集成 Sa-Token、Mybatis-Plus、WarmFlow、SpringDoc、Hutool、OSS 定期同步项目地址: https://gitcode.com/GitHub_Trending/ru/RuoYi-Vue-Plus

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询