mirror of
https://github.com/dromara/RuoYi-Vue-Plus.git
synced 2026-07-11 23:26:10 +00:00
14 KiB
14 KiB
后端约定
优先参考的代码来源
ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/*.vmruoyi-modules/ruoyi-demo/...ruoyi-modules/ruoyi-system/...ruoyi-modules/ruoyi-workflow/...ruoyi-common/ruoyi-common-mybatis/...
决策顺序
写代码时按下面顺序取样:
- 当前业务模块下最近似实现。
- 当前仓库公共能力模块中的统一约定。
- generator 模板。
- 通用 Spring / MyBatis-Plus 默认习惯。
如果规则冲突,优先相信当前仓库真实代码。
分层结构
标准 CRUD 代码应优先遵循下面这套结构:
domain/Entity.javadomain/bo/EntityBo.javadomain/vo/EntityVo.javamapper/EntityMapper.javaservice/IEntityService.javaservice/impl/EntityServiceImpl.javacontroller/EntityController.java
Entity 规则
- 除非所在模块明显另有约定,否则实体类继承
org.dromara.common.mybatis.core.domain.BaseEntity。 - 使用 Lombok
@Data和@EqualsAndHashCode(callSuper = true)。 - 使用
@TableName("table_name")。 - 主键使用
@TableId。 - 存在
delFlag时保留@TableLogic,存在乐观锁字段时保留@Version。 - 如果附近实体已经使用
@OrderBy等额外注解,应继续保持。
BO 规则
- 实现
Serializable。 - 添加
@AutoMapper(target = Entity.class, reverseConvertGenerate = false)。 - 请求专用字段、查询专用字段放在 BO 中,包括
params。 - 在生成器或附近代码已有分组校验时,继续使用:
AddGroup、EditGroup、QueryGroup。 @Xss、@Email、@Size、@NotBlank、@NotNull要按真实业务语义添加,不要一股脑全套上。- 查询存在日期范围或扩展条件时,保留
params = new HashMap<>()。
VO 规则
- 实现
Serializable。 - 添加
@AutoMapper(target = Entity.class)。 - 生成器风格的导出对象通常带
@ExcelIgnoreUnannotated。 @ExcelProperty、@ExcelDictFormat、ExcelDictConvert、@ExcelRequired、@ExcelNotation、@DateTimeFormat只在导入导出场景下使用。- 如果附近代码会把 ID 翻译成展示字段,沿用
@Translation(type = TransConstant.USER_ID_TO_NAME, mapper = "createBy")这类写法。 - 展示型派生字段放在 VO,不放在 Entity。
Mapper 规则
- 默认形式是
interface XxxMapper extends BaseMapperPlus<Xxx, XxxVo>。 - 不要为简单的 entity 转 vo 手写重复代码,优先依赖
BaseMapperPlus。 - 模块已经使用
@DataPermission时,在重写方法和自定义查询上继续保留。 - 复杂模块里 mapper 可能同时继承
MPJBaseMapper<Entity>并使用QueryBuilder.lambdaJoin(...)构造 MPJ 查询,遇到这种风格要延续,不要换一种写法。 - 只有在
selectVoList/selectVoPage不够用时,才补 XML 或自定义 mapper 方法。 - Mapper 默认方法可以承载短小的 wrapper 查询;涉及复杂业务编排、缓存、事务或跨 mapper 写入时放到 service。
ruoyi-system的用户、角色、菜单、部门等模块常带数据权限、MPJ 联表、角色状态过滤,修改前先读对应 mapper/service。
Mapper 建议结构
标准 mapper 一般按这个顺序组织:
- 接口声明
- 默认查询方法
- 自定义分页或列表方法
- 特殊数据权限重写
- 辅助构造方法
什么时候需要 XML
- 复杂联表 SQL 无法仅靠 wrapper 清晰表达时。
- 需要手写查询列和结果映射时。
- 项目当前模块已经大量使用 XML 时。
如果 BaseMapperPlus + wrapper 已足够,优先不要补 XML。
Service 规则
- 类声明通常是
@RequiredArgsConstructor、@Service,按需补@Slf4j。 - 手写 mapper 注入字段使用具体业务短名;代码生成器模板按类名首字母小写命名。
- 命名时去掉清晰的模块/系统前缀后使用 lowerCamel +
Mapper,例如SysRoleMapper->roleMapper、SysDictDataMapper->dictDataMapper。 - 如果去掉前缀会产生歧义或命名冲突,保留必要前缀。
- 读操作通常返回
Vo、List<Vo>或PageResult<Vo>。 - BO 转实体用
MapstructUtils.convert(bo, Entity.class)。 - 查询条件优先返回
LambdaQueryWrapper;新增 generator 风格代码优先用QueryBuilder.lambda(Entity.class),老模块已有Wrappers.lambdaQuery()时可继续保持。 - 字符串和空值条件优先用
eqIfText、likeIfText、eqIfPresent、inIfNotEmpty、betweenParams等项目扩展;老代码已有直接StringUtils.isNotBlank(...)和 null 判断时可增量保持。 - 分页查询优先采用:
Page<Vo> result = entityMapper.selectVoPage(pageQuery.build(), lqw);return PageResult.build(result.getRecords(), result.getTotal()); - 生成器风格模块保留
validEntityBeforeSave(...)这种扩展点。 - 多表写操作使用
@Transactional(rollbackFor = Exception.class)。 - 明确的业务失败,尤其是权限、数据完整性、删除校验,使用
ServiceException。 - 不要绕过模块现有的数据权限、角色校验、删除前校验。
Service 建议结构
标准 service impl 一般按下面顺序组织:
- 查询单条
- 分页查询
- 列表查询
- 构建查询条件
- 新增
- 修改
- 保存前校验
- 删除前校验与删除
- 其他扩展业务方法
查询逻辑建议
- 单表查询优先返回
LambdaQueryWrapper,生成器风格优先通过QueryBuilder.lambda(Entity.class).build()构造。 - 条件判断直接放在 wrapper 链式条件上,不要额外写大量 if 套壳。
- 日期范围统一从
bo.getParams()取 begin/end;生成器默认使用betweenParams(Entity::getField, params, "beginField", "endField")。 - 复杂联表查询优先查同模块是否已有 MPJ 风格可复用;新写法优先用
QueryBuilder.lambdaJoin("u", Entity.class)。
写入逻辑建议
- BO 转实体统一走
MapstructUtils.convert。 - 批量关系维护时优先拆成私有方法,例如角色、岗位、用户关联。
- 修改前优先保留已有防误删、防越权、防并发覆盖逻辑。
Controller 规则
- 继承
BaseController。 - 类上通常带
@Validated、@RestController、@RequiredArgsConstructor、@RequestMapping。 - 返回值使用
R<T>或R<Void>。 - 标准 CRUD 接口通常是:
GET /list、POST /export、GET /{id}、POST、PUT、DELETE /{ids}。 - 树表接口通常不分页,
list返回R<List<Vo>>;导出路由以目标模块或 generator 模板为准,旧 demo 树表存在GET /export,新版生成器通常是POST /export。 @SaCheckPermission权限格式遵循${module}:${business}:${action}。- 写操作、导入导出接口通常加
@Log(title = "...", businessType = BusinessType.X)。 - 附近接口已有防重时,写接口继续使用
@RepeatSubmit。 - 适合分组校验时,使用
@Validated(AddGroup.class)和@Validated(EditGroup.class)。 - 特殊接口直接复用模块内现成做法,例如导入导出、
@ApiEncrypt、multipart 上传、数据权限检查、写入前唯一性校验。
Controller 建议结构
标准 controller 一般按下面顺序组织:
- 列表
- 导出
- 详情
- 新增
- 修改
- 删除
- 特殊接口
Controller 边界
- controller 负责接参、校验、权限、日志、返回值转换。
- 重业务逻辑尽量放 service,不要在 controller 里堆长逻辑。
- 但前置权限检查、唯一性提示、显式业务失败提示可以留在 controller,前提是同模块已有这种习惯。
查询与工具规则
- 分页统一使用
PageQuery和PageResult,不要无故引入新的分页 DTO。 - 优先使用项目工具类:
MapstructUtils、StringUtils、StreamUtils、ValidatorUtils、SpringUtils、RedisUtils。 - 数组转列表按附近代码习惯使用
List.of(ids)或Arrays.asList(ids)。 - 日期范围查询通常从
bo.getParams()中读取beginTime、endTime或beginFieldName、endFieldName。 - 构建查询优先识别
QueryBuilder.lambda(...)、QueryBuilder.lambdaJoin(...)、BaseMapperPlus#lambda()三类入口,不要退回临时手写 SQL 或自造 wrapper。
common-mybatis 规则
- 链式查询能力优先沿用
QueryBuilder.lambda(...)、QueryBuilder.lambdaJoin(...)、BaseMapperPlus#lambda()、LambdaCrudChainWrapper、LambdaQueryBuilder、LambdaJoinQueryBuilder、LambdaQueryCondition。 - 条件辅助方法使用项目已有命名:
eqIfPresent、eqIfText、neIfPresent、likeIfText、betweenIfPresent、betweenParams、inIfNotEmpty、findInSetIfPresent。 - 新增 wrapper 方法时保持链式返回
this/typedThis,不要返回底层LambdaQueryWrapper破坏调用链。 LambdaCrudChainWrapper既承担查询又承担更新 set 片段,新增能力时要同时考虑getSqlSelect、getSqlSet、clear、instance的状态复制和清理。- MPJ 联表查询沿用别名风格,例如
QueryBuilder.lambdaJoin("u", SysUser.class)、.leftJoin(..., "d", ...)、.eq("u", Entity::getField, value)。 - 数据权限注解使用
@DataPermission+@DataColumn,列名需和实际 SQL 别名一致,例如d.dept_id、u.create_by。
translation / JSON 增强规则
- 翻译实现类实现
TranslationInterface<T>并标注@TranslationType(type = ...)。 - 使用方在 VO 字段上通过
@Translation(type = ..., mapper = "...", other = "...")指定翻译来源。 - 批量翻译必须优先实现
translationBatch(Set<Object> keys, String other),避免默认逐条查询。 - 支持逗号分隔 ID 的翻译实现应复用
collectLongIds、parseLongIds、joinMappedValues。 TranslationJsonFieldProcessor遵循三阶段:collect收集待翻译值,prepare批量查询,process写入翻译结果;新增处理器也应优先套这个模型。- 翻译失败时保持降级返回原值或
null的现有语义,不要让响应增强中断主流程。
缓存与异步/监听规则
- 已有 service 使用
@Cacheable、@CachePut、@CacheEvict、@Caching或CacheUtils.evict/clear时,新增写操作要同步考虑缓存失效。 - 部门、字典、OSS 配置等模块已有缓存初始化或失效逻辑,不要只改数据库不处理缓存;字典这类模块常同时维护
CacheNames.SYS_DICT与CacheNames.SYS_DICT_TYPE。 - Excel 导入监听器实现
ExcelListener时,保留getExcelResult()的回执语义和错误聚合方式。 - 定时任务、MQTT、SSE、异步回调等框架方法一般按接口覆写语义实现,除非业务不直观,不要添加冗长注释。
工作流模块规则
ruoyi-workflow通常带@ConditionalOnEnable,新增 workflow bean、controller、service 时检查同包是否需要该条件。- 流程分类、任务、实例等查询常带分类权限或用户维度过滤,先读同类 mapper/service 再改。
- 工作流的翻译实现可以放在 workflow 模块内,例如流程分类 ID 到名称,仍应遵守
TranslationInterface批量翻译规则。
JavaDoc 注释规则
- 公共 API、接口、VO/BO/Entity 字段、Mapper 默认方法、Service/Controller 方法应有简洁 JavaDoc。
- 注释描述“做什么”和关键参数语义,不复述显而易见的实现细节。
void方法不要写@return;返回布尔值时说明true/false含义。- 私有方法只有在业务规则、算法、映射关系不直观时补注释。
- 框架覆写方法如果只是标准回调,可不重复注释;但当前文件已有统一注释风格时保持一致。
- 只改注释时,不重排 import、不格式化全文件、不修改代码行为。
前后端联动规则
- 新增后端接口时,路径和权限前缀尽量保持 generator 约定,方便前端目录和 API 命名同步。
- 新增日期范围查询时,记得保留
bo.params结构,避免前端addDateRange无法对接。 - 导出接口通常保持
POST /export风格,便于前端直接复用现有下载逻辑。 - 批量删除接口通常使用
DELETE /{ids},便于前端直接传数组或逗号串。
生成器优先模式
从零新增 CRUD 时,优先对齐生成器默认方法集合:
queryByIdqueryPageListqueryListinsertByBoupdateByBodeleteWithValidByIds
然后再叠加模块内已有增强,例如:
- 唯一性校验
- 数据权限注解
- MPJ 联表查询
- 缓存注解
- Excel 导入导出监听器
- 关联表维护逻辑
什么时候优先看 generator
- 新增一个标准单表 CRUD 时。
- 只有表结构和基本接口需求,没有现成业务模块可参考时。
- 需要快速补齐整套骨架代码时。
什么时候优先看现有模块
- 目标模块已经有类似业务。
- 涉及数据权限、联表、缓存、角色岗位关系、导入导出、工作流扩展时。
- 任务是“修改已有模块”而不是“新建模块”时。
避免事项
- 不要在 controller 里直接暴露 entity 代替 BO/VO。
- 不要给新的管理接口漏掉权限注解。
- 没有明确必要时,不要从
BaseMapperPlus风格退回手工映射。 - 前端查询页用了日期范围时,不要删掉后端
params相关处理。 - 不要把
ruoyi-system这类复杂逻辑强行简化成生成器式单表 CRUD。
交付前自检
交付前至少检查这些点:
- CRUD 主链路是否完整。
- BO / VO / Entity 职责是否清晰。
- 分页、查询、删除校验是否与前端对得上。
- 权限、日志、防重、事务是否遗漏。
- 是否只是 generator 裸产物,如果是,需要继续补齐同模块已有增强。