From 70dacfb3799b94f0ce5e89c6eb5dd47f5310ae1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Fri, 15 May 2026 14:32:59 +0800 Subject: [PATCH] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agents/backend-common-infrastructure.md | 42 ++ .claude/agents/backend-crud.md | 18 +- .claude/agents/backend-engineering.md | 13 +- .claude/agents/backend-javadoc.md | 42 ++ .claude/agents/backend-module-enhancement.md | 13 +- .claude/agents/backend-query-permission.md | 19 +- .codex/skills/ruoyi-plus-ai-coding/SKILL.md | 54 +- .../ruoyi-plus-ai-coding/agents/openai.yaml | 4 +- .../references/backend.md | 139 ++--- .../common/core/enums/FormatsType.java | 6 + .../common/core/utils/SpringUtils.java | 6 + .../common/core/utils/ip/AddressUtils.java | 6 + .../common/excel/core/CellMergeHandler.java | 9 +- .../excel/utils/ExcelWriterWrapper.java | 127 ++++ .../json/enhance/JsonEnhancementContext.java | 22 +- .../common/json/enhance/JsonFieldContext.java | 7 + .../json/enhance/JsonValueEnhancer.java | 18 + .../core/domain/DataPermissionAccess.java | 5 + .../core/mapper/LambdaCrudChainWrapper.java | 241 ++++++-- .../common/mybatis/core/page/PageQuery.java | 13 +- .../core/query/LambdaQueryBuilder.java | 572 +++++++++++++++++- .../core/query/LambdaQueryCondition.java | 303 +++++++++- .../mybatis/core/query/QueryBuilder.java | 9 +- .../common/oss/config/OssClientConfig.java | 12 + .../common/oss/enums/AccessPolicy.java | 6 + .../oss/exception/S3StorageException.java | 52 ++ .../common/oss/model/HandleAsyncResult.java | 42 ++ .../common/oss/model/PutObjectResult.java | 9 + .../TranslationJsonFieldProcessor.java | 60 +- .../core/impl/DeptNameTranslationImpl.java | 21 + .../core/impl/DictTypeTranslationImpl.java | 14 + .../core/impl/NicknameTranslationImpl.java | 21 + .../core/impl/OssUrlTranslationImpl.java | 21 + .../core/impl/UserNameTranslationImpl.java | 21 + .../gen/util/template/PathNamedTemplate.java | 21 + .../runner/ResourceApplicationRunner.java | 5 + .../org/dromara/system/domain/SysRole.java | 4 - .../org/dromara/system/domain/SysUser.java | 10 + .../dromara/system/domain/bo/SysRoleBo.java | 15 +- .../dromara/system/domain/bo/SysSocialBo.java | 1 - .../dromara/system/domain/bo/SysUserBo.java | 9 +- .../system/domain/vo/SysRoleMenuPermVo.java | 6 + .../dromara/system/domain/vo/SysRoleVo.java | 9 +- .../dromara/system/mapper/SysMenuMapper.java | 15 +- .../dromara/system/mapper/SysUserMapper.java | 6 + .../system/service/ISysMenuService.java | 5 +- .../service/impl/SysMenuServiceImpl.java | 46 +- .../impl/CategoryNameTranslationImpl.java | 21 + 48 files changed, 1868 insertions(+), 272 deletions(-) create mode 100644 .claude/agents/backend-common-infrastructure.md create mode 100644 .claude/agents/backend-javadoc.md diff --git a/.claude/agents/backend-common-infrastructure.md b/.claude/agents/backend-common-infrastructure.md new file mode 100644 index 000000000..3e5a77fc7 --- /dev/null +++ b/.claude/agents/backend-common-infrastructure.md @@ -0,0 +1,42 @@ +--- +name: backend-common-infrastructure +description: 公共基础模块专家。用于修改 ruoyi-common 下的 mybatis、translation、json enhance、excel、oss、redis、web、encrypt 等公共能力,强调 API 兼容、调用点检查和同包风格一致。 +--- + +你负责 `ruoyi-common` 公共基础模块的增量修改。 + +## 核心原则 + +1. 先阅读同包接口、实现类和调用点,再改公共 API。 +2. 优先保持公开方法签名、泛型、返回值、异常语义兼容。 +3. 新增能力要贴合已有命名和链式调用风格,不自造平行体系。 +4. 只补注释时不改实现、不重排 import、不运行无关格式化。 + +## common-mybatis + +- 链式查询能力沿用 `BaseMapperPlus#lambda()`、`LambdaCrudChainWrapper`、`LambdaQueryBuilder`、`LambdaQueryCondition`。 +- 条件辅助方法命名沿用 `eqIfPresent`、`eqIfText`、`likeIfText`、`betweenIfPresent`、`inIfNotEmpty`、`findInSetIfPresent`。 +- `LambdaCrudChainWrapper` 同时维护查询字段和更新 set 片段;新增状态时必须检查 `instance()` 与 `clear()`。 +- 返回链式对象时保持 `this` / `typedThis`,不要暴露底层 wrapper 破坏调用链。 + +## translation / JSON 增强 + +- 翻译实现类实现 `TranslationInterface` 并标注 `@TranslationType(type = ...)`。 +- 批量翻译优先实现 `translationBatch(Set keys, String other)`,避免默认逐条查询。 +- 支持逗号分隔 ID 时复用 `collectLongIds`、`parseLongIds`、`joinMappedValues`。 +- `TranslationJsonFieldProcessor` 按 `collect` / `prepare` / `process` 三阶段组织。 +- 翻译失败应降级返回原值或 `null`,不要让响应增强中断主流程。 + +## excel / oss / json / web + +- Excel 导入监听器保持 `ExcelListener#getExcelResult()` 回执语义和错误聚合方式。 +- OSS 结果、异常、配置对象的工厂方法保持现有 `form...` 命名,不随意改成通用 builder。 +- JSON 响应增强处理器优先实现 `JsonFieldProcessor`,并按字段上下文读取注解。 +- Web、Redis、Encrypt、Sa-Token 自动配置类新增 bean 时检查条件注解、配置属性和已有命名。 + +## 自检 + +- 是否破坏已有调用点。 +- 是否遗漏 `instance()` / `clear()` / 批量翻译 / 缓存失效等公共模块关键路径。 +- 是否新增了与现有工具重复的临时类或临时方法。 +- JavaDoc 是否简洁说明公共 API 的参数、返回值和兼容语义。 diff --git a/.claude/agents/backend-crud.md b/.claude/agents/backend-crud.md index 5292c937b..6da0b453d 100644 --- a/.claude/agents/backend-crud.md +++ b/.claude/agents/backend-crud.md @@ -1,6 +1,6 @@ --- name: backend-crud -description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRUD、补 entity/bo/vo/mapper/service/controller、分页查询、导出、删除前校验等任务。适用于单个微服务内部开发;如果需求跨服务复用、Dubbo 远程调用、gateway 或 Seata,请先升级为微服务任务处理。 +description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRUD、补 entity/bo/vo/mapper/service/controller、分页查询、导出、删除前校验等任务。 --- 你负责当前项目中的标准后端 CRUD 实现。 @@ -11,16 +11,16 @@ description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRU 2. 再参考当前模块内最近似的标准管理模块。 3. 分层保持稳定: `domain`、`domain.bo`、`domain.vo`、`mapper`、`service`、`service.impl`、`controller` -4. 默认这是单个微服务内部的 CRUD;不要顺手扩成 `ruoyi-api` 远程契约,除非明确有跨服务复用需求。 ## 结构约定 - entity 默认继承 `BaseEntity` +- entity 使用 `@TableName`,主键使用 `@TableId`;存在 `delFlag`、乐观锁字段时保留 `@TableLogic`、`@Version` - mapper 默认继承 `BaseMapperPlus` - BO 使用 `@AutoMapper(target = Entity.class, reverseConvertGenerate = false)` - VO 使用 `@AutoMapper(target = Entity.class)` -- service 使用 `baseMapper` -- controller 暴露 HTTP 接口,跨服务复用另走 `ruoyi-api + Dubbo` +- BO/VO/Entity 职责分离:请求、查询扩展字段放 BO;展示派生字段和 `@Translation` 放 VO +- 代码生成器模板按类名首字母小写命名 Mapper 字段,例如 `SysRoleMapper` -> `sysRoleMapper`;手写业务代码可使用具体业务短名 ## 默认方法集合 @@ -34,9 +34,11 @@ description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRU ## 查询规则 - 单表查询优先用 `LambdaQueryWrapper` +- 项目公共链式查询可使用 `BaseMapperPlus#lambda()`、`LambdaCrudChainWrapper`、`LambdaQueryCondition` 的 `IfPresent` / `IfText` / `IfNotEmpty` 风格 - 日期范围默认从 `bo.getParams()` 中读取 begin/end - 分页优先返回 `PageResult` -- 日期范围与查询参数风格要保持前端与 gateway 入口路由稳定,不因微服务拆分改变 HTTP 接口习惯 +- BO 转实体使用 `MapstructUtils.convert(bo, Entity.class)` +- 写入前校验优先放在 `validEntityBeforeSave(...)` ## 接口规则 @@ -50,8 +52,8 @@ description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRU `PUT` `DELETE /{ids}` - 默认检查是否需要 `@SaCheckPermission`、`@Log`、`@RepeatSubmit` -- 如果只是服务内 CRUD,不要新增 `@DubboReference` -- 如果别的服务也需要这块能力,先停止裸 CRUD 思路,改为补 `ruoyi-api` 契约再继续 +- 权限标识遵循 `${module}:${business}:${action}` +- 导出接口通常保持 `POST /export` ## 自检 @@ -59,4 +61,4 @@ description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRU - BO / VO / Entity 是否职责分离 - 导出、分页、删除前校验是否齐全 - 是否只是 generator 裸产物,如果是要继续补齐项目约定 -- 是否错误把单服务 CRUD 做成了跨服务实现 +- 前端 `api/types/index.vue` 如需同步,接口路径、返回结构、日期范围参数要与后端一致 diff --git a/.claude/agents/backend-engineering.md b/.claude/agents/backend-engineering.md index c1470705b..59efb4daf 100644 --- a/.claude/agents/backend-engineering.md +++ b/.claude/agents/backend-engineering.md @@ -1,6 +1,6 @@ --- name: backend-engineering -description: 后端工程总入口。用于在当前项目中识别任务属于标准 CRUD、复杂模块增强、联表与数据权限、微服务契约与 Dubbo 协作、或前后端联动,并选择合适的后端子 agent。 +description: 后端工程总入口。用于在当前 RuoYi-Vue-Plus 项目中识别任务属于标准 CRUD、复杂模块增强、联表与数据权限、公共 common 模块、JavaDoc 注释、或前后端联动,并选择合适的后端子 agent。 --- 你是当前后端工程的总入口 agent。 @@ -10,14 +10,15 @@ description: 后端工程总入口。用于在当前项目中识别任务属于 1. 如果是新增标准单表 CRUD、从表结构补 entity/bo/vo/mapper/service/controller,优先使用 `backend-crud.md` 的规则。 2. 如果是修改 `system`、`workflow` 等已经很复杂的模块,优先使用 `backend-module-enhancement.md` 的规则。 3. 如果重点在 MPJ 联表、`@DataPermission`、复杂查询、数据范围控制,优先使用 `backend-query-permission.md` 的规则。 -4. 如果任务涉及 `ruoyi-api`、`Remote*Service`、`@DubboReference`、`@DubboService`、gateway、Nacos 配置、Seata 边界,先按微服务任务处理,不要误判成普通 CRUD。 -5. 如果同时要求同步前端接口或前端页面骨架,保持后端路由与 generator 风格稳定,便于前端 agent 对接。 +4. 如果是修改 `ruoyi-common` 公共基础能力,例如 `common-mybatis`、`common-translation`、`common-json`、`common-excel`、`common-oss`,优先使用 `backend-common-infrastructure.md` 的规则。 +5. 如果只要求补充或修正 JavaDoc 注释,优先使用 `backend-javadoc.md` 的规则。 +6. 如果同时要求同步前端接口或前端页面骨架,保持后端路由与 generator 风格稳定,便于前端 agent 对接。 通用要求: - 先读同模块最近似实现,再动代码。 - 发生冲突时优先相信当前模块真实代码,其次是公共基础设施,再其次才是 generator 模板。 - 默认直接产出可落地代码,而不是只给抽象建议。 -- 当前仓库服务间协作默认是 Dubbo,不要按 Feign 习惯生成跨服务代码。 -- 需要跨服务复用能力时,优先判断是否应修改 `ruoyi-api` 契约层。 -- gateway 负责统一入口与横切能力,不承载业务 CRUD。 +- 不要把 `BaseMapperPlus`、`PageQuery`、`PageResult`、`R`、`MapstructUtils`、`StringUtils`、`StreamUtils` 等项目工具替换成临时自造方案。 +- import、注解顺序、文件结构以附近代码为准,不做无关重排。 +- 修改公开 API 前先查调用点;公共模块优先保持方法签名、泛型、返回值和异常语义兼容。 diff --git a/.claude/agents/backend-javadoc.md b/.claude/agents/backend-javadoc.md new file mode 100644 index 000000000..a65d7b99d --- /dev/null +++ b/.claude/agents/backend-javadoc.md @@ -0,0 +1,42 @@ +--- +name: backend-javadoc +description: JavaDoc 注释专家。用于当前项目中补充或修正 JavaDoc 注释,覆盖公共 API、接口、BO/VO/Entity 字段、Mapper 默认方法、Service/Controller 方法和复杂私有辅助方法。 +--- + +你负责只补充或修正注释,不改变代码行为。 + +## 核心原则 + +1. 默认补 JavaDoc,保持当前文件和同包注释风格。 +2. 不改方法签名、泛型、返回值、实现逻辑。 +3. 不重排 import,不格式化全文件,不改无关注解顺序。 +4. 先确认注释是否准确反映当前实现,发现明显错注释要修正。 + +## 优先补充范围 + +- 公共 API、接口方法、record 参数、构造器。 +- BO / VO / Entity 字段,尤其是导入导出、翻译、权限相关字段。 +- Mapper 默认方法、Service/Controller 公开方法。 +- 公共模块里的私有辅助方法,如果涉及映射、批量处理、状态复制、降级语义。 + +## 注释风格 + +- 描述“做什么”和关键参数语义,不复述每行实现。 +- `@param` 名称必须和方法签名一致。 +- `void` 方法不要写 `@return`。 +- 布尔返回值说明 true/false 含义。 +- 简单框架覆写方法可不重复注释;如果当前文件已有统一注释风格,保持一致。 +- 只修正错注释时,尽量保持最小 diff。 + +## 常见错注释 + +- 方法实际查询全部数据,注释写成“根据用户查询”。 +- 方法返回树、映射、分页,`@return` 只写笼统“结果”。 +- 参数从 ID 集合变成 Collection,注释还写“ID 串”。 +- `translationBatch`、`buildValue`、`collect/prepare/process` 这类公共扩展点缺少批量语义说明。 + +## 自检 + +- 运行 `git diff --check`。 +- 检查是否只改注释或文档。 +- 检查新增 JavaDoc 是否和当前实现一致。 diff --git a/.claude/agents/backend-module-enhancement.md b/.claude/agents/backend-module-enhancement.md index a8e6aba1f..fe25515e2 100644 --- a/.claude/agents/backend-module-enhancement.md +++ b/.claude/agents/backend-module-enhancement.md @@ -1,6 +1,6 @@ --- name: backend-module-enhancement -description: 复杂后端模块增强专家。用于修改当前项目中已经存在较重业务逻辑的模块,例如 system、workflow 等,强调增量修改、保留现有权限、事务、缓存、导入导出、Dubbo 协作和业务校验。 +description: 复杂后端模块增强专家。用于修改当前项目中已经存在较重业务逻辑的模块,例如 system、workflow 等,强调增量修改、保留现有权限、事务、缓存、导入导出和业务校验。 --- 你负责复杂后端模块的增量增强,不是从零生成裸 CRUD。 @@ -11,7 +11,7 @@ description: 复杂后端模块增强专家。用于修改当前项目中已经 2. 增量修改,不重写整块 service/controller。 3. 保留已有的数据权限、事务、缓存、导入导出、唯一性校验、删除前校验。 4. 不能为了“简洁”把复杂模块退化成模板式单表 CRUD。 -5. 如果模块已经通过 `@DubboReference` 调其他服务,或在 `dubbo/` 下提供远程能力,新增逻辑默认保持同样的微服务边界。 +5. 修改 workflow 模块时检查同包是否需要 `@ConditionalOnEnable`。 ## 常见任务 @@ -19,7 +19,7 @@ description: 复杂后端模块增强专家。用于修改当前项目中已经 - 新增或调整写入前校验 - 维护角色、岗位、用户等关联数据 - 增加复杂页面所需的特殊接口 -- 扩展已有 `Remote*Service` provider 或消费其他服务远程能力 +- 增加或调整缓存、翻译、字典、OSS、导入导出能力 ## 约束 @@ -27,12 +27,13 @@ description: 复杂后端模块增强专家。用于修改当前项目中已经 - service 里的旧逻辑要先理解再改 - 如果附近已有 `ServiceException`、缓存注解、事务注解、数据权限判断,新增逻辑默认保持一致 - 如果存在联动前端页面,接口路径与返回结构尽量稳定 -- 不要把已有 Dubbo 调用改回 controller 互调或其他 HTTP 临时方案 -- 只有明确跨服务写一致性要求时,才扩大到 `@GlobalTransactional` +- 已有 `@Cacheable`、`@CacheEvict`、`@Caching` 的 service,新增写操作要同步考虑缓存失效 +- 工作流分类、任务、实例等查询常带分类权限或用户维度过滤,不要绕过旧逻辑 +- 翻译实现仍遵守 `TranslationInterface` + `@TranslationType` + `translationBatch` 批量查询规则 ## 自检 - 是否破坏了原模块的权限边界 - 是否误删了旧逻辑中的事务或校验 - 是否错误简化了复杂关系维护 -- 是否破坏了原有微服务边界或远程契约 +- 是否漏掉缓存失效、导入导出回执、翻译字段、前端调用路径等联动点 diff --git a/.claude/agents/backend-query-permission.md b/.claude/agents/backend-query-permission.md index bba2bfe85..c801d598c 100644 --- a/.claude/agents/backend-query-permission.md +++ b/.claude/agents/backend-query-permission.md @@ -1,6 +1,6 @@ --- name: backend-query-permission -description: 后端查询、联表与数据权限专家。用于当前项目中的 MPJ 联表、DataPermission、复杂分页查询、范围控制、查询增强,以及需要兼顾微服务边界的查询类任务。 +description: 后端查询、联表与数据权限专家。用于当前项目中的 MPJ 联表、DataPermission、复杂分页查询、范围控制和查询增强任务。 --- 你负责当前项目中的复杂查询和数据权限类任务。 @@ -11,21 +11,32 @@ description: 后端查询、联表与数据权限专家。用于当前项目中 2. 涉及数据权限时优先复用 `@DataPermission` 与已有字段映射方式。 3. 复杂联表优先参考 MPJ 风格,不轻易改回手写零散 SQL。 4. 如果 `BaseMapperPlus + wrapper` 足够,不要额外补 XML。 -5. 如果查询能力需要跨服务复用,先判断应该暴露为 `ruoyi-api` 远程查询接口,还是只保留在当前服务内。 +5. wrapper 查询条件优先使用项目已有工具和命名,不自造查询 DSL。 ## 重点关注 - `BaseMapperPlus` +- `LambdaCrudChainWrapper` +- `LambdaQueryBuilder` +- `LambdaQueryCondition` - `@DataPermission` - `DataColumn` - `MPJBaseMapper` - `JoinWrappers.lambda(...)` - 复杂分页与列表查询 -- 跨服务查询 DTO 与远程返回对象边界 + +## 项目写法 + +- MPJ 联表查询沿用别名风格,例如 `JoinWrappers.lambda("u", SysUser.class)`。 +- 带别名字段条件使用 `.eq("u", Entity::getField, value)`、`.orderByAsc("m", SysMenu::getOrderNum)`。 +- 数据权限列名要和真实 SQL 别名一致,例如 `d.dept_id`、`u.create_by`。 +- `ruoyi-system` 的用户、角色、菜单、部门查询常带角色状态、删除标识、部门权限过滤,修改前先读对应 mapper/service。 +- 日期范围参数继续从 `bo.getParams()` 获取,避免前端 `addDateRange` 对不上。 +- 简单查询默认留在 service wrapper;短小且复用性强的 mapper 默认方法可以保留在 mapper。 ## 输出要求 - 明确说明查询是单表、联表还是带权限控制 - 保持与当前模块 mapper 风格一致 - 不要让查询参数风格和前端现有调用脱节 -- 不要为了跨服务查询去直接依赖对方 controller 路由 +- 不要为了“易读”移除已有数据权限、角色状态、删除标识过滤 diff --git a/.codex/skills/ruoyi-plus-ai-coding/SKILL.md b/.codex/skills/ruoyi-plus-ai-coding/SKILL.md index cfcbab540..3cfbc2940 100644 --- a/.codex/skills/ruoyi-plus-ai-coding/SKILL.md +++ b/.codex/skills/ruoyi-plus-ai-coding/SKILL.md @@ -1,6 +1,6 @@ --- name: ruoyi-plus-ai-coding -description: 在仓库内按代码生成器模板和项目既有约定生成或修改代码。用于新增 CRUD 模块、补全 controller/service/mapper/BO/VO/entity、编写 MyBatis-Plus 查询,以及新增与后端接口配套的 Vue 3 + TypeScript 页面、types 和 api 文件。 +description: 在仓库内按代码生成器模板和项目既有约定生成或修改代码。用于新增或修改 CRUD 模块、controller/service/mapper/BO/VO/entity、MyBatis-Plus/MPJ 查询、数据权限、缓存、翻译/JSON 增强、公共 common 模块能力、JavaDoc 注释,以及与后端接口配套的 Vue 3 + TypeScript 页面、types 和 api 文件。 --- # RuoYi Plus AI 编码规范 @@ -14,6 +14,8 @@ description: 在仓库内按代码生成器模板和项目既有约定生成或 - 新增标准 CRUD 模块。 - 根据新表结构补齐 entity、bo、vo、mapper、service、controller。 - 修改已有模块的查询、校验、导入导出、数据权限、事务逻辑。 +- 修改 `ruoyi-common` 公共能力,例如 mybatis 查询构造器、translation、json enhance、excel、oss、redis、web 配置。 +- 补充或修正 JavaDoc 注释,尤其是公共 API、接口、BO/VO/Entity 字段、Mapper 默认方法、Service/Controller 方法。 - 在系统、监控、工作流、demo 等模块内按现有约定扩展业务代码。 - 为后端新增接口同步补前端 `api/types/index.vue` 骨架。 @@ -32,9 +34,10 @@ description: 在仓库内按代码生成器模板和项目既有约定生成或 2. 新增标准 CRUD 代码前,先读取 `ruoyi-modules/ruoyi-gen/src/main/resources/vm/` 下的模板。 3. 命名和分层保持与仓库一致: `domain` entity、`domain.bo`、`domain.vo`、`mapper`、`service`、`service.impl`、`controller`。 -4. 先判断任务只发生在单个微服务内,还是同时涉及 `ruoyi-api`、Dubbo 远程调用、gateway 路由、跨服务事务。 -5. 优先在生成器结构上扩展,不要自行发明新的分层。 -6. 修改 `ruoyi-system` 这类复杂模块前,先阅读同类现有实现,因为这些模块通常比生成器默认产物多出数据权限、联表、缓存、安全校验等逻辑。 +4. 优先在生成器结构上扩展,不要自行发明新的分层。 +5. 修改 `ruoyi-system` 这类复杂模块前,先阅读同类现有实现,因为这些模块通常比生成器默认产物多出数据权限、联表、缓存、安全校验等逻辑。 +6. 修改 `ruoyi-common` 公共模块前,先阅读同包接口、实现类和调用点,优先保持已有 API 语义与兼容性。 +7. 只补注释或文档时,不运行无关格式化,不重排 import,不改代码逻辑。 ## 优先级规则 @@ -55,8 +58,6 @@ description: 在仓库内按代码生成器模板和项目既有约定生成或 Java、MyBatis-Plus、BO/VO/entity、controller、mapper、service 的具体规则见 [references/backend.md](references/backend.md)。 -如果任务涉及微服务边界、`ruoyi-api` 契约、Dubbo 调用、Seata、Nacos 配置或 gateway 路由,也优先参考 [references/backend.md](references/backend.md) 中的微服务章节。 - ## 前端规则 Vue 3、TypeScript API 文件、生成式列表页、表单状态、字典和日期范围约定见 [references/frontend.md](references/frontend.md)。 @@ -79,9 +80,8 @@ Vue 3、TypeScript API 文件、生成式列表页、表单状态、字典和日 - 如果目标模块已经存在自定义校验、数据权限、事务、缓存、Excel 导入导出、联表查询等逻辑,应在此基础上扩展,不要为了“简洁”把它们削平。 - 如果附近 controller 接口已经带有权限、日志、防重、加密、分组校验等注解,新接口默认同步保持一致,除非有明确理由不这样做。 - 如果 BO 或 VO 需要字段校验、翻译、Excel 注解,应优先参考同模块同用途对象,不要机械套通用注解。 -- 如果需求跨服务复用能力,优先新增或扩展 `ruoyi-api` 下的 `Remote*Service` 契约,再在服务提供方用 `@DubboService` 实现,在消费方用 `@DubboReference` 注入。 -- 如果只是服务内 controller/service/mapper 调整,不要顺手把它拔高成远程接口。 -- 如果需求只影响前端访问入口但不改变服务内部逻辑,先检查是否只需要调整 gateway 或 Nacos 中的路由配置;不要把路由问题误改成业务代码问题。 +- 如果修改公共基础模块,优先保持公开 API 兼容,新增能力要查调用点和同包风格。 +- 如果任务只涉及注释,默认补 JavaDoc 并保持实现不变;框架覆写方法不强行重复注释,除非业务语义不直观。 ## 目录映射规则 @@ -106,13 +106,6 @@ Vue 3、TypeScript API 文件、生成式列表页、表单状态、字典和日 - `vm/java/controller.java.vm` -> controller - `vm/xml/mapper.xml.vm` -> 自定义 XML mapper 起点 -微服务相关目录通常按下面结构组织: - -- `ruoyi-auth`、`ruoyi-gateway`、`ruoyi-modules/*`:具体服务实现与服务内 controller/service/mapper。 -- `ruoyi-api/ruoyi-api-system|resource|workflow`:跨服务契约层,放 `Remote*Service`、远程 BO/VO/model、mock/stub。 -- `ruoyi-modules/*/src/main/java/.../dubbo/`:`@DubboService` 提供者实现。 -- `ruoyi-*/src/main/resources/application.yml`:服务自身端口、应用名、Nacos 配置导入入口。 - ## 任务分型 ### 1. 标准单表 CRUD @@ -123,19 +116,18 @@ Vue 3、TypeScript API 文件、生成式列表页、表单状态、字典和日 如果目标模块像 `system`、`workflow` 一样已经有复杂逻辑,优先增量修改,不要回退成模板式简化代码。 -### 3. 微服务协作扩展 - -如果需求横跨两个及以上服务,先明确服务提供方、消费方与契约归属: - -- 对外给其他服务复用的能力,放到 `ruoyi-api`。 -- provider 在归属服务内实现 `dubbo/Remote*ServiceImpl`。 -- consumer 通过 `@DubboReference` 调用,不在 controller 里手写 HTTP 互调。 -- 需要容错时复用现有 `Mock` / `Stub` 风格,不自造另一套降级约定。 - -### 4. 基础能力复用 +### 3. 基础能力复用 如果涉及数据权限、缓存、事务、导入导出、字典、翻译、加密、分组校验,优先查项目已有做法并复用公共能力。 +### 4. 公共基础模块修改 + +修改 `ruoyi-common` 下的基础能力时,优先保证二进制/API 兼容:不要轻易改公开方法签名、泛型、返回值或异常语义。新增注释和小范围能力时,先查同包现有风格,例如 `common-mybatis` 的链式 wrapper、`common-translation` 的 `TranslationInterface` 实现、`common-json` 的字段处理器。 + +### 5. 注释修正任务 + +只要求“加注释/完善注释”时,默认补 JavaDoc,不改实现。优先补公共 API、接口方法、字段含义、复杂私有辅助方法;覆写框架回调方法只有在当前文件已有注释风格或业务语义不直观时才补。 + ## 输出要求 使用本 skill 时,默认期望产出应满足: @@ -145,8 +137,6 @@ Vue 3、TypeScript API 文件、生成式列表页、表单状态、字典和日 - 查询、分页、删除校验、写入校验逻辑闭环完整。 - 权限、日志、防重、事务、数据权限尽量贴近同模块现有实现。 - 如果同步改前端,前端 API 路径和后端接口保持一致。 -- 如果跨服务,`ruoyi-api` 契约、provider 实现、consumer 调用三处保持一致。 -- 如果涉及跨服务写操作,明确本地事务与分布式事务边界,不要无故扩大 `@GlobalTransactional` 范围。 ## 快速检查清单 @@ -156,15 +146,13 @@ Vue 3、TypeScript API 文件、生成式列表页、表单状态、字典和日 - 手写 Service 注入 Mapper 时使用具体业务短名;代码生成器模板按类名首字母小写命名,例如 `SysRoleMapper` 生成 `sysRoleMapper`。 - Service 按场景返回 `PageResult` 或 `List`。 - 查询代码优先使用 `LambdaQueryWrapper`,复杂模块沿用既有 MPJ 联表风格。 +- 公共 Mapper 链式能力优先沿用 `LambdaCrudChainWrapper`、`LambdaQueryBuilder`、`LambdaQueryCondition` 的 `IfPresent` / `IfText` / `IfNotEmpty` 风格。 +- 翻译能力优先沿用 `TranslationInterface` + `@TranslationType` + `@Translation`,批量翻译实现 `translationBatch`,避免退化成逐条查询。 +- JSON 响应增强优先沿用 `JsonFieldProcessor` 的 `collect` / `prepare` / `process` 三阶段模型。 - BO 使用 `@AutoMapper(target = Entity.class, reverseConvertGenerate = false)`。 - VO 使用 `@AutoMapper(target = Entity.class)`。 - 前端 API 路径与后端路由完全对应。 - 前端列表页继续使用仓库里的 `proxy?.addDateRange`、`proxy?.$modal`、`proxy?.download`、`useDict`、`pagination` 等工具。 -- 不要把当前仓库的 Dubbo 远程调用误写成 Feign。 -- 新增远程能力时,先看是否应落在 `ruoyi-api-system`、`ruoyi-api-resource`、`ruoyi-api-workflow`。 -- `@DubboReference` 只有在确实跨服务时才新增;服务内调用继续直接走本地 service。 -- gateway 负责入口、鉴权、过滤、跨域、日志等,不承载具体业务 service 逻辑。 -- 服务 `application.yml` 继续沿用当前的 `spring.config.import` + `optional:nacos:*` 结构,不要擅自改成另一套配置加载方式。 ## 推荐提问方式 diff --git a/.codex/skills/ruoyi-plus-ai-coding/agents/openai.yaml b/.codex/skills/ruoyi-plus-ai-coding/agents/openai.yaml index de6cbc2d0..d5977d1a9 100644 --- a/.codex/skills/ruoyi-plus-ai-coding/agents/openai.yaml +++ b/.codex/skills/ruoyi-plus-ai-coding/agents/openai.yaml @@ -1,7 +1,7 @@ interface: display_name: "RuoYi Plus 编码" - short_description: "按生成器与仓库约定编写代码" - default_prompt: "使用 $ruoyi-plus-ai-coding 在这个仓库里按现有约定实现代码修改。" + short_description: "按生成器、common 与仓库约定编写代码" + default_prompt: "使用 $ruoyi-plus-ai-coding 在这个仓库里按现有约定实现代码修改,保持生成器、公共模块和业务模块风格一致。" policy: allow_implicit_invocation: true diff --git a/.codex/skills/ruoyi-plus-ai-coding/references/backend.md b/.codex/skills/ruoyi-plus-ai-coding/references/backend.md index cad981db2..3a86cc759 100644 --- a/.codex/skills/ruoyi-plus-ai-coding/references/backend.md +++ b/.codex/skills/ruoyi-plus-ai-coding/references/backend.md @@ -18,76 +18,6 @@ 如果规则冲突,优先相信当前仓库真实代码。 -## 微服务总览 - -当前仓库的微服务相关真实约定不是 Feign 风格,而是: - -- 服务注册与配置中心使用 Nacos。 -- 对外入口使用 `ruoyi-gateway`。 -- 服务间 RPC 默认使用 Dubbo。 -- 跨服务契约放在 `ruoyi-api/*`。 -- 跨服务写操作按需使用 Seata,不是所有写接口都默认上全局事务。 - -写代码前先判断任务属于下面哪类: - -1. 纯服务内 CRUD 或模块增强。 -2. 需要给其他服务复用能力,必须新增或修改 `ruoyi-api` 契约。 -3. 只是网关入口、跨域、过滤、日志、鉴权相关,应该改 `ruoyi-gateway` 或配置。 -4. 涉及跨服务写入一致性,才评估 `@GlobalTransactional`。 - -## 微服务模块边界 - -- `ruoyi-auth`:认证授权中心,本身也通过 `@DubboReference` 依赖 system/resource 等远程能力。 -- `ruoyi-gateway`:统一入口,负责过滤器、异常处理、跨域、日志、语言解析等,不写业务 CRUD。 -- `ruoyi-modules/ruoyi-system|resource|workflow|job|gen`:具体微服务业务实现。 -- `ruoyi-api/ruoyi-api-system|resource|workflow`:服务间共享契约,放 `Remote*Service`、远程 BO/VO/model、mock/stub。 -- `ruoyi-common/*`:跨服务都会复用的基础能力,不要在业务模块里重复造轮子。 - -## 应用入口与基础配置规则 - -- 每个微服务应用入口默认使用 `@EnableDubbo` + `@SpringBootApplication`。 -- 每个服务的 `application.yml` 只保留本服务端口、服务名、激活环境、Nacos 导入入口。 -- 配置导入继续沿用 `spring.config.import` + `optional:nacos:*` 结构。 -- 业务服务通常额外导入 `datasource.yml`;像 `ruoyi-auth`、`ruoyi-gateway` 这类服务不一定导入数据源配置,先以当前模块现状为准。 -- 不要因为新增一个接口就改动服务名、端口、Nacos group/namespace、配置加载方式。 - -## 跨服务契约规则 - -需要被其他服务调用的能力,按下面顺序落代码: - -1. 在 `ruoyi-api` 下新增或扩展 `Remote*Service` 接口。 -2. 如需跨服务传参或返回展示字段,在 `ruoyi-api` 下补充专用 `domain.bo`、`domain.vo` 或 `model`。 -3. 在服务提供方模块的 `dubbo/` 包下新增 `@DubboService` 实现。 -4. 在服务消费方用 `@DubboReference` 注入。 - -### 契约放置原则 - -- `Remote*Service` 放到能力归属服务对应的 `ruoyi-api-*` 模块。 -- 远程 DTO 只放跨服务真正需要的字段,不直接暴露服务内 Entity。 -- 不要把 controller 的 BO/VO 直接搬去做远程 DTO,除非该对象本身已经是跨服务语义。 -- 远程接口命名延续当前仓库风格:`RemoteUserService`、`RemoteFileService`、`RemoteWorkflowService`。 - -### provider 实现规则 - -- provider 一般放在 `src/main/java/.../dubbo/RemoteXxxServiceImpl.java`。 -- 类上保留 `@DubboService`,通常同时保留 Spring `@Service` 或使用构造注入风格与当前模块一致。 -- provider 内优先复用本服务已有 `service`、`mapper`、`convert`、工具类,不重新实现一套业务。 -- provider 返回远程 VO / model,不把本服务内部实体直接外泄。 - -### consumer 调用规则 - -- 跨服务调用使用 `@DubboReference`,不要新写服务间 HTTP/Feign 调用。 -- 只有真正跨服务时才新增 `@DubboReference`;同服务内调用继续直接注入本地 service。 -- controller 中允许少量直接 `@DubboReference`,但仅限当前模块已有这种写法的场景;新逻辑优先看附近代码风格保持一致。 -- 消费方如果附近已有 `stub = "true"`、`mock = "true"` 等容错写法,继续沿用。 - -### mock / stub 规则 - -- 可选能力或允许降级的远程服务,优先复用 `ruoyi-api` 中现有 `Mock` / `Stub` 风格。 -- `Mock` 适合返回空对象、空列表、空字符串等兜底值。 -- `Stub` 适合包一层 try/catch 做调用保护和告警日志。 -- 不要在业务代码里散落重复的远程降级逻辑,优先收敛到 API 契约侧。 - ## 分层结构 标准 CRUD 代码应优先遵循下面这套结构: @@ -134,6 +64,8 @@ - 模块已经使用 `@DataPermission` 时,在重写方法和自定义查询上继续保留。 - 复杂模块里 mapper 可能同时继承 `MPJBaseMapper` 并使用 `JoinWrappers.lambda(...)`,遇到这种风格要延续,不要换一种写法。 - 只有在 `selectVoList/selectVoPage` 不够用时,才补 XML 或自定义 mapper 方法。 +- Mapper 默认方法可以承载短小的 wrapper 查询;涉及复杂业务编排、缓存、事务或跨 mapper 写入时放到 service。 +- `ruoyi-system` 的用户、角色、菜单、部门等模块常带数据权限、MPJ 联表、角色状态过滤,修改前先读对应 mapper/service。 ### Mapper 建议结构 @@ -170,8 +102,6 @@ - 多表写操作使用 `@Transactional(rollbackFor = Exception.class)`。 - 明确的业务失败,尤其是权限、数据完整性、删除校验,使用 `ServiceException`。 - 不要绕过模块现有的数据权限、角色校验、删除前校验。 -- 如果写操作只发生在单服务内,优先本地 `@Transactional`,不要无故提升成分布式事务。 -- 如果像 `SysProfileController` 这类场景同时写本服务数据并调用远程文件服务,再参考附近代码决定是否使用 `@GlobalTransactional`。 ### Service 建议结构 @@ -211,8 +141,6 @@ - 附近接口已有防重时,写接口继续使用 `@RepeatSubmit`。 - 适合分组校验时,使用 `@Validated(AddGroup.class)` 和 `@Validated(EditGroup.class)`。 - 特殊接口直接复用模块内现成做法,例如导入导出、`@ApiEncrypt`、multipart 上传、数据权限检查、写入前唯一性校验。 -- controller 默认是对前端开放的 HTTP 接口,不要把跨服务契约直接建在 controller 上。 -- 新增给其他服务复用的能力时,优先补 `Remote*Service`,不是补一个“内部专用 controller”。 ### Controller 建议结构 @@ -239,27 +167,52 @@ - 数组转列表按附近代码习惯使用 `List.of(ids)` 或 `Arrays.asList(ids)`。 - 日期范围查询通常从 `bo.getParams()` 中读取 `beginTime`、`endTime` 或 `beginFieldName`、`endFieldName`。 +## common-mybatis 规则 + +- 链式查询能力优先沿用 `BaseMapperPlus#lambda()`、`LambdaCrudChainWrapper`、`LambdaQueryBuilder`、`LambdaQueryCondition`。 +- 条件辅助方法使用项目已有命名:`eqIfPresent`、`eqIfText`、`likeIfText`、`betweenIfPresent`、`inIfNotEmpty`、`findInSetIfPresent`。 +- 新增 wrapper 方法时保持链式返回 `this` / `typedThis`,不要返回底层 `LambdaQueryWrapper` 破坏调用链。 +- `LambdaCrudChainWrapper` 既承担查询又承担更新 set 片段,新增能力时要同时考虑 `getSqlSelect`、`getSqlSet`、`clear`、`instance` 的状态复制和清理。 +- MPJ 联表查询沿用别名风格,例如 `JoinWrappers.lambda("u", SysUser.class)`、`.leftJoin(..., "d", ...)`、`.eq("u", Entity::getField, value)`。 +- 数据权限注解使用 `@DataPermission` + `@DataColumn`,列名需和实际 SQL 别名一致,例如 `d.dept_id`、`u.create_by`。 + +## translation / JSON 增强规则 + +- 翻译实现类实现 `TranslationInterface` 并标注 `@TranslationType(type = ...)`。 +- 使用方在 VO 字段上通过 `@Translation(type = ..., mapper = "...", other = "...")` 指定翻译来源。 +- 批量翻译必须优先实现 `translationBatch(Set keys, String other)`,避免默认逐条查询。 +- 支持逗号分隔 ID 的翻译实现应复用 `collectLongIds`、`parseLongIds`、`joinMappedValues`。 +- `TranslationJsonFieldProcessor` 遵循三阶段:`collect` 收集待翻译值,`prepare` 批量查询,`process` 写入翻译结果;新增处理器也应优先套这个模型。 +- 翻译失败时保持降级返回原值或 `null` 的现有语义,不要让响应增强中断主流程。 + +## 缓存与异步/监听规则 + +- 已有 service 使用 `@Cacheable`、`@CacheEvict`、`@Caching` 时,新增写操作要同步考虑缓存失效。 +- 部门、字典、OSS 配置等模块已有缓存初始化或失效逻辑,不要只改数据库不处理缓存。 +- 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}`,便于前端直接传数组或逗号串。 -- 前端访问路径保持面向 gateway 的稳定 HTTP 路由,不把 Dubbo 远程接口暴露给前端设计。 - -## Gateway 规则 - -- `ruoyi-gateway` 主要承载入口、过滤器、异常处理、跨域、日志、国际化等横切能力。 -- 不在 gateway 中新增业务 service、mapper、领域对象 CRUD。 -- 如果问题只与请求头、跨域、过滤链、日志、安全入口有关,优先检查 gateway。 -- 如果问题属于业务数据查询、写入、校验,优先改对应业务服务,不要误改 gateway。 - -## Seata 与事务边界规则 - -- 本地单服务写操作优先 `@Transactional(rollbackFor = Exception.class)`。 -- 只有一个业务动作明确跨越多个微服务资源写入时,才考虑 `@GlobalTransactional(rollbackFor = Exception.class)`。 -- 不要把纯查询接口、纯远程读取接口或可容忍最终一致的场景都包进全局事务。 -- 是否需要 Seata,以当前模块已有实现和业务一致性要求为准,不做想当然扩张。 ## 生成器优先模式 @@ -300,11 +253,6 @@ - 没有明确必要时,不要从 `BaseMapperPlus` 风格退回手工映射。 - 前端查询页用了日期范围时,不要删掉后端 `params` 相关处理。 - 不要把 `ruoyi-system` 这类复杂逻辑强行简化成生成器式单表 CRUD。 -- 不要把当前仓库默认的 Dubbo 远程协作改写成 Feign 风格。 -- 不要把 `ruoyi-api` 变成业务实现模块;它只承载契约、远程 DTO、mock/stub。 -- 不要为跨服务复用去直接依赖对方 controller 路由。 -- 不要在 gateway 里堆业务逻辑。 -- 不要无故扩大全局事务边界。 ## 交付前自检 @@ -315,6 +263,3 @@ - 分页、查询、删除校验是否与前端对得上。 - 权限、日志、防重、事务是否遗漏。 - 是否只是 generator 裸产物,如果是,需要继续补齐同模块已有增强。 -- 如果改了跨服务能力,`ruoyi-api`、provider、consumer 三处是否同步。 -- 如果改了微服务入口或配置,是否仍保持当前 Nacos 导入结构。 -- 如果改了事务,是否能说明为什么需要本地事务或全局事务。 diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/FormatsType.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/FormatsType.java index 8d4b6d911..6df247a87 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/FormatsType.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/enums/FormatsType.java @@ -135,6 +135,12 @@ public enum FormatsType { */ private final String timeFormat; + /** + * 根据字符串内容匹配时间格式类型。 + * + * @param str 字符串内容 + * @return 时间格式类型 + */ public static FormatsType getFormatsType(String str) { for (FormatsType value : values()) { if (StringUtils.contains(str, value.getTimeFormat())) { diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java index 016115316..fdf49585f 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java @@ -52,6 +52,7 @@ public final class SpringUtils extends SpringUtil { return (T) getBean(invoker.getClass()); } + /** * 获取spring上下文 */ @@ -59,6 +60,11 @@ public final class SpringUtils extends SpringUtil { return getApplicationContext(); } + /** + * 当前是否启用虚拟线程。 + * + * @return true 启用 false 未启用 + */ public static boolean isVirtual() { return Threading.VIRTUAL.isActive(getBean(Environment.class)); } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java index fe36d9cc0..d9accb091 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ip/AddressUtils.java @@ -21,6 +21,12 @@ public class AddressUtils { // 内网地址 public static final String LOCAL_ADDRESS = "内网IP"; + /** + * 根据 IP 查询真实地址。 + * + * @param ip IP 地址 + * @return 真实地址 + */ public static String getRealAddressByIP(String ip) { // 处理空串并过滤HTML标签 ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,"")); diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java index bcda202bf..cc2232fd0 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/CellMergeHandler.java @@ -3,10 +3,10 @@ package org.dromara.common.excel.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; -import lombok.SneakyThrows; import org.apache.fesod.sheet.annotation.ExcelIgnore; import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated; import org.apache.fesod.sheet.annotation.ExcelProperty; +import lombok.SneakyThrows; import org.apache.poi.ss.util.CellRangeAddress; import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.excel.annotation.CellMerge; @@ -33,6 +33,13 @@ public class CellMergeHandler { this.hasTitle = hasTitle; this.rowIndex = hasTitle ? rowIndex : 0; } + + /** + * 计算需要合并的单元格区域。 + * + * @param rows 数据行 + * @return 单元格合并区域列表 + */ @SneakyThrows public List handle(List rows) { // 如果入参为空集合则返回空集 diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelWriterWrapper.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelWriterWrapper.java index 1db39da8e..376a8d378 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelWriterWrapper.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelWriterWrapper.java @@ -22,38 +22,95 @@ import java.util.function.Supplier; */ public record ExcelWriterWrapper(ExcelWriter excelWriter) { + /** + * 写出集合数据到指定工作表。 + * + * @param data 数据集合 + * @param writeSheet 工作表 + */ public void write(Collection data, WriteSheet writeSheet) { excelWriter.write(data, writeSheet); } + /** + * 通过数据提供器写出集合数据到指定工作表。 + * + * @param supplier 数据提供器 + * @param writeSheet 工作表 + */ public void write(Supplier> supplier, WriteSheet writeSheet) { excelWriter.write(supplier.get(), writeSheet); } + /** + * 写出集合数据到指定工作表和表格。 + * + * @param data 数据集合 + * @param writeSheet 工作表 + * @param writeTable 表格 + */ public void write(Collection data, WriteSheet writeSheet, WriteTable writeTable) { excelWriter.write(data, writeSheet, writeTable); } + /** + * 通过数据提供器写出集合数据到指定工作表和表格。 + * + * @param supplier 数据提供器 + * @param writeSheet 工作表 + * @param writeTable 表格 + */ public void write(Supplier> supplier, WriteSheet writeSheet, WriteTable writeTable) { excelWriter.write(supplier.get(), writeSheet, writeTable); } + /** + * 填充数据到指定工作表。 + * + * @param data 填充数据 + * @param writeSheet 工作表 + */ public void fill(Object data, WriteSheet writeSheet) { excelWriter.fill(data, writeSheet); } + /** + * 按填充配置填充数据到指定工作表。 + * + * @param data 填充数据 + * @param fillConfig 填充配置 + * @param writeSheet 工作表 + */ public void fill(Object data, FillConfig fillConfig, WriteSheet writeSheet) { excelWriter.fill(data, fillConfig, writeSheet); } + /** + * 通过数据提供器填充数据到指定工作表。 + * + * @param supplier 数据提供器 + * @param writeSheet 工作表 + */ public void fill(Supplier supplier, WriteSheet writeSheet) { excelWriter.fill(supplier, writeSheet); } + /** + * 通过数据提供器按填充配置填充数据到指定工作表。 + * + * @param supplier 数据提供器 + * @param fillConfig 填充配置 + * @param writeSheet 工作表 + */ public void fill(Supplier supplier, FillConfig fillConfig, WriteSheet writeSheet) { excelWriter.fill(supplier, fillConfig, writeSheet); } + /** + * 获取写出上下文。 + * + * @return 写出上下文 + */ public WriteContext writeContext() { return excelWriter.writeContext(); } @@ -70,34 +127,82 @@ public record ExcelWriterWrapper(ExcelWriter excelWriter) { // -------------------------------- sheet start + /** + * 创建工作表。 + * + * @param sheetNo 工作表编号 + * @param sheetName 工作表名称 + * @return 工作表 + */ public static WriteSheet buildSheet(Integer sheetNo, String sheetName) { return sheetBuilder(sheetNo, sheetName).build(); } + /** + * 创建工作表。 + * + * @param sheetNo 工作表编号 + * @return 工作表 + */ public static WriteSheet buildSheet(Integer sheetNo) { return sheetBuilder(sheetNo).build(); } + /** + * 创建工作表。 + * + * @param sheetName 工作表名称 + * @return 工作表 + */ public static WriteSheet buildSheet(String sheetName) { return sheetBuilder(sheetName).build(); } + /** + * 创建工作表。 + * + * @return 工作表 + */ public static WriteSheet buildSheet() { return sheetBuilder().build(); } + /** + * 创建工作表构造器。 + * + * @param sheetNo 工作表编号 + * @param sheetName 工作表名称 + * @return 工作表构造器 + */ public static ExcelWriterSheetBuilder sheetBuilder(Integer sheetNo, String sheetName) { return FesodSheet.writerSheet(sheetNo, sheetName); } + /** + * 创建工作表构造器。 + * + * @param sheetNo 工作表编号 + * @return 工作表构造器 + */ public static ExcelWriterSheetBuilder sheetBuilder(Integer sheetNo) { return FesodSheet.writerSheet(sheetNo); } + /** + * 创建工作表构造器。 + * + * @param sheetName 工作表名称 + * @return 工作表构造器 + */ public static ExcelWriterSheetBuilder sheetBuilder(String sheetName) { return FesodSheet.writerSheet(sheetName); } + /** + * 创建工作表构造器。 + * + * @return 工作表构造器 + */ public static ExcelWriterSheetBuilder sheetBuilder() { return FesodSheet.writerSheet(); } @@ -106,18 +211,40 @@ public record ExcelWriterWrapper(ExcelWriter excelWriter) { // -------------------------------- table start + /** + * 创建表格。 + * + * @param tableNo 表格编号 + * @return 表格 + */ public static WriteTable buildTable(Integer tableNo) { return tableBuilder(tableNo).build(); } + /** + * 创建表格。 + * + * @return 表格 + */ public static WriteTable buildTable() { return tableBuilder().build(); } + /** + * 创建表格构造器。 + * + * @param tableNo 表格编号 + * @return 表格构造器 + */ public static ExcelWriterTableBuilder tableBuilder(Integer tableNo) { return FesodSheet.writerTable(tableNo); } + /** + * 创建表格构造器。 + * + * @return 表格构造器 + */ public static ExcelWriterTableBuilder tableBuilder() { return FesodSheet.writerTable(); } diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonEnhancementContext.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonEnhancementContext.java index 30d0924d8..64005fc8d 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonEnhancementContext.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonEnhancementContext.java @@ -3,8 +3,8 @@ package org.dromara.common.json.enhance; import lombok.Getter; import tools.jackson.databind.json.JsonMapper; +import java.util.LinkedHashMap; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * 单次响应增强上下文。 @@ -14,17 +14,35 @@ public class JsonEnhancementContext { private final JsonMapper jsonMapper; - private final Map attributes = new ConcurrentHashMap<>(); + private final Map attributes = new LinkedHashMap<>(); + /** + * 构造响应增强上下文。 + * + * @param jsonMapper JSON 映射器 + */ public JsonEnhancementContext(JsonMapper jsonMapper) { this.jsonMapper = jsonMapper; } + /** + * 获取上下文属性。 + * + * @param key 属性键 + * @param 属性值类型 + * @return 属性值 + */ @SuppressWarnings("unchecked") public T getAttribute(String key) { return (T) attributes.get(key); } + /** + * 设置上下文属性。 + * + * @param key 属性键 + * @param value 属性值 + */ public void setAttribute(String key, Object value) { attributes.put(key, value); } diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldContext.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldContext.java index c5fa81148..198a6579d 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldContext.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldContext.java @@ -9,6 +9,13 @@ import java.lang.annotation.Annotation; */ public record JsonFieldContext(Object owner, String propertyName, AnnotatedMember member, Object value) { + /** + * 获取字段上的指定注解。 + * + * @param annotationType 注解类型 + * @param 注解类型 + * @return 注解对象 + */ public A getAnnotation(Class annotationType) { return member == null ? null : member.getAnnotation(annotationType); } diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonValueEnhancer.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonValueEnhancer.java index bc2e8123c..68dd8da22 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonValueEnhancer.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonValueEnhancer.java @@ -31,6 +31,12 @@ public class JsonValueEnhancer { private final Map, List> propertyCache = new ConcurrentHashMap<>(); + /** + * 构造统一响应增强器。 + * + * @param jsonMapper JSON 映射器 + * @param processors 字段处理器列表 + */ public JsonValueEnhancer(JsonMapper jsonMapper, List processors) { this.jsonMapper = jsonMapper; List sortedProcessors = new ArrayList<>(processors); @@ -38,6 +44,12 @@ public class JsonValueEnhancer { this.processors = Collections.unmodifiableList(sortedProcessors); } + /** + * 增强响应对象。 + * + * @param body 响应对象 + * @return 增强后的响应对象 + */ public Object enhance(Object body) { if (body == null || body instanceof JsonNode || processors.isEmpty()) { return body; @@ -45,6 +57,12 @@ public class JsonValueEnhancer { return enhanceTree(body); } + /** + * 判断消息转换器是否支持响应增强。 + * + * @param converterType 消息转换器类型 + * @return true 支持 false 不支持 + */ public boolean supports(Class converterType) { return !processors.isEmpty() && !ByteArrayHttpMessageConverter.class.isAssignableFrom(converterType) diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/DataPermissionAccess.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/DataPermissionAccess.java index 7abd7ee34..e68f4158d 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/DataPermissionAccess.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/domain/DataPermissionAccess.java @@ -18,6 +18,11 @@ public record DataPermissionAccess(Set perms, Set roleKeys) impl public static final DataPermissionAccess EMPTY = new DataPermissionAccess(Set.of(), Set.of()); + /** + * 是否存在数据权限约束。 + * + * @return true 存在权限约束 false 不存在权限约束 + */ public boolean constrained() { return CollUtil.isNotEmpty(perms) || CollUtil.isNotEmpty(roleKeys); } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/LambdaCrudChainWrapper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/LambdaCrudChainWrapper.java index 91d47f49f..4e8cff3ad 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/LambdaCrudChainWrapper.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/LambdaCrudChainWrapper.java @@ -25,10 +25,10 @@ import java.util.function.Function; import java.util.function.Predicate; /** - * Mapper ? Lambda CRUD ?????? + * Mapper 级 Lambda CRUD 链式包装器。 * - * @param table ?? - * @param vo ?? + * @param table 泛型 + * @param vo 泛型 * @author Lion Li */ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper> @@ -40,6 +40,11 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper sqlSet; private SharedString sqlSelect = new SharedString(); + /** + * 构造 Mapper 级 Lambda CRUD 链式包装器。 + * + * @param crudMapper Mapper 对象 + */ public LambdaCrudChainWrapper(BaseMapperPlus crudMapper) { this.crudMapper = crudMapper; super.setEntityClass(crudMapper.currentModelClass()); @@ -47,6 +52,22 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper(); } + /** + * 构造 Mapper 级 Lambda CRUD 链式包装器实例。 + * + * @param crudMapper Mapper 对象 + * @param entity 实体对象 + * @param entityClass 实体类型 + * @param sqlSelect 查询字段 SQL 片段 + * @param sqlSet 更新 set SQL 片段集合 + * @param paramNameSeq 参数名称序列 + * @param paramNameValuePairs 参数名称与参数值映射 + * @param mergeSegments 查询条件表达式 + * @param paramAlias 参数别名 + * @param lastSql SQL 尾部片段 + * @param sqlComment SQL 注释片段 + * @param sqlFirst SQL 起始片段 + */ LambdaCrudChainWrapper(BaseMapperPlus crudMapper, T entity, Class entityClass, SharedString sqlSelect, List sqlSet, AtomicInteger paramNameSeq, Map paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias, SharedString lastSql, @@ -65,6 +86,13 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper select(boolean condition, List> columns) { if (condition && CollectionUtils.isNotEmpty(columns)) { @@ -73,16 +101,36 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper select(SFunction... columns) { return select(true, CollectionUtils.toList(columns)); } + /** + * 按条件选择查询字段。 + * + * @param condition 是否选择字段 + * @param columns 查询字段 + * @return this + */ @SafeVarargs public final LambdaCrudChainWrapper select(boolean condition, SFunction... columns) { return select(condition, CollectionUtils.toList(columns)); } + /** + * 按字段过滤条件选择查询字段。 + * + * @param entityClass 实体类型 + * @param predicate 字段过滤条件 + * @return this + */ @Override public LambdaCrudChainWrapper select(Class entityClass, Predicate predicate) { if (entityClass == null) { @@ -95,11 +143,25 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper set(boolean condition, SFunction column, Object val, String mapping) { return maybeDo(condition, () -> { @@ -109,10 +171,10 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper setIfPresent(SFunction column, Object value) { @@ -120,21 +182,37 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper setIfText(SFunction column, String value) { return set(org.dromara.common.core.utils.StringUtils.isNotBlank(value), column, value); } + /** + * 按条件设置自定义 SQL 更新片段。 + * + * @param condition 是否设置该片段 + * @param setSql SQL 更新片段 + * @param params SQL 片段参数 + * @return this + */ @Override public LambdaCrudChainWrapper setSql(boolean condition, String setSql, Object... params) { return maybeDo(condition && StringUtils.isNotBlank(setSql), () -> sqlSet.add(formatSqlMaybeWithParam(setSql, params))); } + /** + * 按条件设置字段自增。 + * + * @param condition 是否设置该字段 + * @param column 字段 + * @param val 自增值 + * @return this + */ @Override public LambdaCrudChainWrapper setIncrBy(boolean condition, SFunction column, Number val) { return maybeDo(condition, () -> { @@ -144,6 +222,14 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper setDecrBy(boolean condition, SFunction column, Number val) { return maybeDo(condition, () -> { @@ -153,6 +239,11 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper findInSet(Object value, SFunction column) { return findInSet(true, value, column); } + /** + * 添加 FIND_IN_SET 条件。 + * + * @param condition 是否添加该条件 + * @param value 匹配值 + * @param column 字段 + * @return this + */ public LambdaCrudChainWrapper findInSet(boolean condition, Object value, SFunction column) { return findInSet(condition, value, columnToString(column)); } + /** + * 值不为空时添加 FIND_IN_SET 条件。 + * + * @param value 匹配值 + * @param column 字段 + * @return this + */ public LambdaCrudChainWrapper findInSetIfPresent(Object value, SFunction column) { return findInSet(value != null, value, column); } /** - * ???? Wrapper? + * 获取当前 Wrapper。 * * @return this */ @@ -192,83 +305,83 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper list() { return crudMapper.selectList(typedThis); } /** - * ????????? + * 查询实体分页记录。 * - * @param page ???? - * @return ?????? + * @param page 分页条件 + * @return 实体分页记录 */ public List list(IPage page) { return crudMapper.selectList(page, typedThis); } /** - * ?? VO ??? + * 查询 VO 列表。 * - * @return VO ?? + * @return VO 列表 */ public List voList() { return crudMapper.selectVoList(typedThis); } /** - * ????????? + * 查询单列对象列表。 * - * @return ?????? + * @return 单列对象列表 */ public List objs() { return crudMapper.selectObjs(typedThis); } /** - * ?????????????? + * 查询单列对象列表并转换类型。 * - * @param mapper ???? - * @param ?????? - * @return ?????? + * @param mapper 转换函数 + * @param 转换后的类型 + * @return 单列对象列表 */ public List objs(Function mapper) { return crudMapper.selectObjs(typedThis, mapper); } /** - * ??????? + * 查询单个实体。 * - * @return ?? + * @return 实体 */ public T one() { return crudMapper.selectOne(typedThis); } /** - * ??????? + * 查询单个实体。 * - * @param throwEx ??????????? - * @return ?? + * @param throwEx 查询到多条时是否抛异常 + * @return 实体 */ public T one(boolean throwEx) { return crudMapper.selectOne(typedThis, throwEx); } /** - * ?????? Optional? + * 查询单个实体 Optional。 * - * @return Optional ?? + * @return Optional 实体 */ public Optional oneOpt() { return Optional.ofNullable(one()); } /** - * ???? VO? + * 查询单个 VO。 * * @return VO */ @@ -277,9 +390,9 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper ???? - * @return ???? + * @param page 分页条件 + * @param

分页类型 + * @return 实体分页 */ public

> P page(P page) { return crudMapper.selectPage(page, typedThis); } /** - * ?? VO ??? + * 查询 VO 分页。 * - * @param page ???? - * @param

???? - * @return VO ?? + * @param page 分页条件 + * @param

分页类型 + * @return VO 分页 */ public

> P voPage(IPage page) { return crudMapper.selectVoPage(page, typedThis); } /** - * ????? + * 删除数据。 * - * @return ?????? + * @return 是否删除成功 */ public boolean delete() { return deleteCount() > 0; } /** - * ????? + * 删除数据。 * - * @return ???? + * @return 影响行数 */ public int deleteCount() { return crudMapper.delete(typedThis); } /** - * ?? set ??????? + * 使用 set 片段更新数据。 * - * @return ?????? + * @return 是否更新成功 */ public boolean update() { return updateCount() > 0; } /** - * ?????????????? + * 使用实体和查询条件更新数据。 * - * @param entity ?? - * @return ?????? + * @param entity 实体 + * @return 是否更新成功 */ public boolean update(T entity) { return updateCount(entity) > 0; } /** - * ?? set ??????? + * 使用 set 片段更新数据。 * - * @return ???? + * @return 影响行数 */ public int updateCount() { return crudMapper.update(typedThis); } /** - * ?????????????? + * 使用实体和查询条件更新数据。 * - * @param entity ?? - * @return ???? + * @param entity 实体 + * @return 影响行数 */ public int updateCount(T entity) { return crudMapper.update(entity, typedThis); } + /** + * 创建新的链式包装器实例。 + * + * @return 新的链式包装器实例 + */ @Override protected LambdaCrudChainWrapper instance() { return new LambdaCrudChainWrapper<>(crudMapper, getEntity(), getEntityClass(), null, null, paramNameSeq, @@ -389,6 +507,9 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper ???? + * @param 实体类型 * @author Lion Li */ public final class LambdaQueryBuilder implements LambdaQueryCondition> { + /** + * MyBatis-Plus Lambda 查询包装器。 + */ private final LambdaQueryWrapper wrapper; + /** + * 构造 Lambda 查询构造辅助对象。 + * + * @param wrapper Lambda 查询包装器 + */ LambdaQueryBuilder(LambdaQueryWrapper wrapper) { this.wrapper = wrapper; } + /** + * 指定查询字段。 + * + * @param columns 查询字段 + * @return 当前查询构造辅助对象 + */ @SafeVarargs public final LambdaQueryBuilder select(SFunction... columns) { wrapper.select(columns); return this; } + /** + * 添加等于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder eq(SFunction column, Object value) { wrapper.eq(column, value); return this; } + /** + * 添加等于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder eq(boolean condition, SFunction column, Object value) { wrapper.eq(condition, column, value); return this; } + /** + * 添加不等于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder ne(SFunction column, Object value) { wrapper.ne(column, value); return this; } + /** + * 添加不等于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder ne(boolean condition, SFunction column, Object value) { wrapper.ne(condition, column, value); return this; } + /** + * 添加大于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder gt(SFunction column, Object value) { wrapper.gt(column, value); return this; } + /** + * 添加大于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder gt(boolean condition, SFunction column, Object value) { wrapper.gt(condition, column, value); return this; } + /** + * 添加大于等于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder ge(SFunction column, Object value) { wrapper.ge(column, value); return this; } + /** + * 添加大于等于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder ge(boolean condition, SFunction column, Object value) { wrapper.ge(condition, column, value); return this; } + /** + * 添加小于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder lt(SFunction column, Object value) { wrapper.lt(column, value); return this; } + /** + * 添加小于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder lt(boolean condition, SFunction column, Object value) { wrapper.lt(condition, column, value); return this; } + /** + * 添加小于等于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder le(SFunction column, Object value) { wrapper.le(column, value); return this; } + /** + * 添加小于等于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder le(boolean condition, SFunction column, Object value) { wrapper.le(condition, column, value); return this; } + /** + * 添加模糊匹配条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder like(SFunction column, Object value) { wrapper.like(column, value); return this; } + /** + * 添加模糊匹配条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder like(boolean condition, SFunction column, Object value) { wrapper.like(condition, column, value); return this; } + /** + * 添加非模糊匹配条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notLike(SFunction column, Object value) { wrapper.notLike(column, value); return this; } + /** + * 添加非模糊匹配条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notLike(boolean condition, SFunction column, Object value) { wrapper.notLike(condition, column, value); return this; } + /** + * 添加左模糊匹配条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder likeLeft(SFunction column, Object value) { wrapper.likeLeft(column, value); return this; } + /** + * 添加左模糊匹配条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder likeLeft(boolean condition, SFunction column, Object value) { wrapper.likeLeft(condition, column, value); return this; } + /** + * 添加右模糊匹配条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder likeRight(SFunction column, Object value) { wrapper.likeRight(column, value); return this; } + /** + * 添加右模糊匹配条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder likeRight(boolean condition, SFunction column, Object value) { wrapper.likeRight(condition, column, value); return this; } + /** + * 添加区间条件。 + * + * @param column 字段 + * @param begin 起始值 + * @param end 结束值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder between(SFunction column, Object begin, Object end) { wrapper.between(column, begin, end); return this; } + /** + * 添加区间条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param begin 起始值 + * @param end 结束值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder between(boolean condition, SFunction column, Object begin, Object end) { wrapper.between(condition, column, begin, end); return this; } + /** + * 添加非区间条件。 + * + * @param column 字段 + * @param begin 起始值 + * @param end 结束值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notBetween(SFunction column, Object begin, Object end) { wrapper.notBetween(column, begin, end); return this; } + /** + * 添加非区间条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param begin 起始值 + * @param end 结束值 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notBetween(boolean condition, SFunction column, Object begin, Object end) { wrapper.notBetween(condition, column, begin, end); return this; } + /** + * 添加全部等于条件。 + * + * @param params 字段和值映射 + * @param null2IsNull 值为空时是否转为 IS NULL 条件 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder allEq(Map params, boolean null2IsNull) { wrapper.allEq(true, (Map) params, null2IsNull); return this; } + /** + * 添加全部等于条件。 + * + * @param condition 是否添加该条件 + * @param params 字段和值映射 + * @param null2IsNull 值为空时是否转为 IS NULL 条件 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder allEq(boolean condition, Map params, boolean null2IsNull) { wrapper.allEq(condition, (Map) params, null2IsNull); return this; } + /** + * 添加经过过滤的全部等于条件。 + * + * @param filter 字段和值过滤器 + * @param params 字段和值映射 + * @param null2IsNull 值为空时是否转为 IS NULL 条件 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder allEq(BiPredicate, Object> filter, Map params, boolean null2IsNull) { wrapper.allEq(true, (BiPredicate) filter, (Map) params, null2IsNull); return this; } + /** + * 添加经过过滤的全部等于条件。 + * + * @param condition 是否添加该条件 + * @param filter 字段和值过滤器 + * @param params 字段和值映射 + * @param null2IsNull 值为空时是否转为 IS NULL 条件 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder allEq(boolean condition, BiPredicate, Object> filter, Map params, boolean null2IsNull) { wrapper.allEq(condition, (BiPredicate) filter, (Map) params, null2IsNull); return this; } + /** + * 添加字段为空条件。 + * + * @param column 字段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder isNull(SFunction column) { wrapper.isNull(column); return this; } + /** + * 添加字段为空条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder isNull(boolean condition, SFunction column) { wrapper.isNull(condition, column); return this; } + /** + * 添加字段非空条件。 + * + * @param column 字段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder isNotNull(SFunction column) { wrapper.isNotNull(column); return this; } + /** + * 添加字段非空条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder isNotNull(boolean condition, SFunction column) { wrapper.isNotNull(condition, column); return this; } + /** + * 添加包含集合条件。 + * + * @param column 字段 + * @param values 条件值集合 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder in(SFunction column, Collection values) { wrapper.in(column, values); return this; } + /** + * 添加包含集合条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param values 条件值集合 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder in(boolean condition, SFunction column, Collection values) { wrapper.in(condition, column, values); return this; } + /** + * 添加包含数组条件。 + * + * @param column 字段 + * @param values 条件值数组 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder in(SFunction column, Object... values) { wrapper.in(column, values); return this; } + /** + * 添加包含数组条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param values 条件值数组 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder in(boolean condition, SFunction column, Object... values) { wrapper.in(condition, column, values); return this; } + /** + * 添加不包含集合条件。 + * + * @param column 字段 + * @param values 条件值集合 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notIn(SFunction column, Collection values) { wrapper.notIn(column, values); return this; } + /** + * 添加不包含集合条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param values 条件值集合 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notIn(boolean condition, SFunction column, Collection values) { wrapper.notIn(condition, column, values); return this; } + /** + * 添加不包含数组条件。 + * + * @param column 字段 + * @param values 条件值数组 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notIn(SFunction column, Object... values) { wrapper.notIn(column, values); return this; } + /** + * 添加不包含数组条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param values 条件值数组 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notIn(boolean condition, SFunction column, Object... values) { wrapper.notIn(condition, column, values); return this; } + /** + * 添加分组字段。 + * + * @param columns 分组字段 + * @return 当前查询构造辅助对象 + */ @SafeVarargs public final LambdaQueryBuilder groupBy(SFunction... columns) { wrapper.groupBy(Arrays.asList(columns)); return this; } + /** + * 添加分组字段。 + * + * @param condition 是否添加该分组 + * @param columns 分组字段 + * @return 当前查询构造辅助对象 + */ @SafeVarargs public final LambdaQueryBuilder groupBy(boolean condition, SFunction... columns) { wrapper.groupBy(condition, Arrays.asList(columns)); return this; } + /** + * 添加排序条件。 + * + * @param condition 是否添加该排序 + * @param isAsc 是否升序 + * @param column 排序字段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder orderBy(boolean condition, boolean isAsc, SFunction column) { wrapper.orderBy(condition, isAsc, column); return this; } + /** + * 添加升序排序字段。 + * + * @param columns 排序字段 + * @return 当前查询构造辅助对象 + */ @SafeVarargs public final LambdaQueryBuilder orderByAsc(SFunction... columns) { wrapper.orderByAsc(Arrays.asList(columns)); return this; } + /** + * 添加升序排序字段。 + * + * @param condition 是否添加该排序 + * @param columns 排序字段 + * @return 当前查询构造辅助对象 + */ @SafeVarargs public final LambdaQueryBuilder orderByAsc(boolean condition, SFunction... columns) { wrapper.orderByAsc(condition, Arrays.asList(columns)); return this; } + /** + * 添加降序排序字段。 + * + * @param columns 排序字段 + * @return 当前查询构造辅助对象 + */ @SafeVarargs public final LambdaQueryBuilder orderByDesc(SFunction... columns) { wrapper.orderByDesc(Arrays.asList(columns)); return this; } + /** + * 添加降序排序字段。 + * + * @param condition 是否添加该排序 + * @param columns 排序字段 + * @return 当前查询构造辅助对象 + */ @SafeVarargs public final LambdaQueryBuilder orderByDesc(boolean condition, SFunction... columns) { wrapper.orderByDesc(condition, Arrays.asList(columns)); return this; } + /** + * 添加 HAVING 条件。 + * + * @param sqlHaving HAVING SQL 片段 + * @param params SQL 片段参数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder having(String sqlHaving, Object... params) { wrapper.having(sqlHaving, params); return this; } + /** + * 添加 HAVING 条件。 + * + * @param condition 是否添加该条件 + * @param sqlHaving HAVING SQL 片段 + * @param params SQL 片段参数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder having(boolean condition, String sqlHaving, Object... params) { wrapper.having(condition, sqlHaving, params); return this; } + /** + * 添加 AND 嵌套条件。 + * + * @param consumer 条件构造函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder and(Consumer> consumer) { wrapper.and(consumer); return this; } + /** + * 添加 AND 嵌套条件。 + * + * @param condition 是否添加该条件 + * @param consumer 条件构造函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder and(boolean condition, Consumer> consumer) { wrapper.and(condition, consumer); return this; } + /** + * 添加 OR 拼接。 + * + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder or() { wrapper.or(); return this; } + /** + * 添加 OR 嵌套条件。 + * + * @param consumer 条件构造函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder or(Consumer> consumer) { wrapper.or(consumer); return this; } + /** + * 添加 OR 嵌套条件。 + * + * @param condition 是否添加该条件 + * @param consumer 条件构造函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder or(boolean condition, Consumer> consumer) { wrapper.or(condition, consumer); return this; } + /** + * 添加普通嵌套条件。 + * + * @param consumer 条件构造函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder nested(Consumer> consumer) { wrapper.nested(consumer); return this; } + /** + * 添加普通嵌套条件。 + * + * @param condition 是否添加该条件 + * @param consumer 条件构造函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder nested(boolean condition, Consumer> consumer) { wrapper.nested(condition, consumer); return this; } + /** + * 添加 EXISTS 条件。 + * + * @param existsSql EXISTS SQL 片段 + * @param values SQL 片段参数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder exists(String existsSql, Object... values) { wrapper.exists(existsSql, values); return this; } + /** + * 添加 EXISTS 条件。 + * + * @param condition 是否添加该条件 + * @param existsSql EXISTS SQL 片段 + * @param values SQL 片段参数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder exists(boolean condition, String existsSql, Object... values) { wrapper.exists(condition, existsSql, values); return this; } + /** + * 添加字段等于 SQL 片段条件。 + * + * @param column 字段 + * @param inValue SQL 片段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder eqSql(SFunction column, String inValue) { wrapper.eqSql(true, column, inValue); return this; } + /** + * 添加字段等于 SQL 片段条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param inValue SQL 片段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder eqSql(boolean condition, SFunction column, String inValue) { wrapper.eqSql(condition, column, inValue); return this; } + /** + * 添加字段 IN SQL 片段条件。 + * + * @param column 字段 + * @param inValue SQL 片段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder inSql(SFunction column, String inValue) { wrapper.inSql(true, column, inValue); return this; } + /** + * 添加字段 IN SQL 片段条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param inValue SQL 片段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder inSql(boolean condition, SFunction column, String inValue) { wrapper.inSql(condition, column, inValue); return this; } + /** + * 添加字段 NOT IN SQL 片段条件。 + * + * @param column 字段 + * @param inValue SQL 片段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notInSql(SFunction column, String inValue) { wrapper.notInSql(true, column, inValue); return this; } + /** + * 添加字段 NOT IN SQL 片段条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param inValue SQL 片段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notInSql(boolean condition, SFunction column, String inValue) { wrapper.notInSql(condition, column, inValue); return this; } + /** + * 添加 NOT EXISTS 条件。 + * + * @param existsSql EXISTS SQL 片段 + * @param values SQL 片段参数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notExists(String existsSql, Object... values) { wrapper.notExists(existsSql, values); return this; } + /** + * 添加 NOT EXISTS 条件。 + * + * @param condition 是否添加该条件 + * @param existsSql EXISTS SQL 片段 + * @param values SQL 片段参数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder notExists(boolean condition, String existsSql, Object... values) { wrapper.notExists(condition, existsSql, values); return this; } + /** + * 拼接 SQL 片段条件。 + * + * @param applySql SQL 片段 + * @param values SQL 片段参数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder apply(String applySql, Object... values) { wrapper.apply(applySql, values); return this; } + /** + * 拼接 SQL 片段条件。 + * + * @param condition 是否添加该条件 + * @param applySql SQL 片段 + * @param values SQL 片段参数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder apply(boolean condition, String applySql, Object... values) { wrapper.apply(condition, applySql, values); return this; } + /** + * 直接对底层查询包装器应用自定义处理。 + * + * @param consumer 查询包装器处理函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder apply(Consumer> consumer) { consumer.accept(wrapper); return this; } + /** + * 使用函数式方式追加条件。 + * + * @param consumer 条件构造函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder func(Consumer> consumer) { wrapper.func(true, consumer); return this; } + /** + * 使用函数式方式追加条件。 + * + * @param condition 是否添加该条件 + * @param consumer 条件构造函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder func(boolean condition, Consumer> consumer) { wrapper.func(condition, consumer); return this; } + /** + * 添加 NOT 嵌套条件。 + * + * @param consumer 条件构造函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder not(Consumer> consumer) { wrapper.not(true, consumer); return this; } + /** + * 添加 NOT 嵌套条件。 + * + * @param condition 是否添加该条件 + * @param consumer 条件构造函数 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder not(boolean condition, Consumer> consumer) { wrapper.not(condition, consumer); return this; } + /** + * 添加 SQL 注释。 + * + * @param comment SQL 注释内容 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder comment(String comment) { wrapper.comment(true, comment); return this; } + /** + * 添加 SQL 注释。 + * + * @param condition 是否添加该注释 + * @param comment SQL 注释内容 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder comment(boolean condition, String comment) { wrapper.comment(condition, comment); return this; } + /** + * 拼接 SQL 尾部片段。 + * + * @param lastSql SQL 尾部片段 + * @return 当前查询构造辅助对象 + */ public LambdaQueryBuilder last(String lastSql) { wrapper.last(lastSql); return this; } + /** + * 获取底层 Lambda 查询包装器。 + * + * @return Lambda 查询包装器 + */ public LambdaQueryWrapper build() { return wrapper; } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryCondition.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryCondition.java index 748460d3a..33e64abec 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryCondition.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryCondition.java @@ -8,100 +8,339 @@ import java.util.Collection; import java.util.Map; /** - * Lambda ????????? + * Lambda 查询常用条件扩展。 * - * @param ???? - * @param ?????? + * @param 实体类型 + * @param 链式返回类型 * @author Lion Li */ public interface LambdaQueryCondition { + /** + * 等于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ Children eq(boolean condition, SFunction column, Object value); + /** + * 不等于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ Children ne(boolean condition, SFunction column, Object value); + /** + * 大于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ Children gt(boolean condition, SFunction column, Object value); + /** + * 大于等于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ Children ge(boolean condition, SFunction column, Object value); + /** + * 小于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ Children lt(boolean condition, SFunction column, Object value); + /** + * 小于等于条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ Children le(boolean condition, SFunction column, Object value); + /** + * 模糊匹配条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ Children like(boolean condition, SFunction column, Object value); + /** + * 非模糊匹配条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ Children notLike(boolean condition, SFunction column, Object value); + /** + * 左模糊匹配条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ Children likeLeft(boolean condition, SFunction column, Object value); + /** + * 右模糊匹配条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ Children likeRight(boolean condition, SFunction column, Object value); + /** + * 区间条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param begin 起始值 + * @param end 结束值 + * @return 链式返回对象 + */ Children between(boolean condition, SFunction column, Object begin, Object end); + /** + * 非区间条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param begin 起始值 + * @param end 结束值 + * @return 链式返回对象 + */ Children notBetween(boolean condition, SFunction column, Object begin, Object end); + /** + * 包含集合条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param values 条件值集合 + * @return 链式返回对象 + */ Children in(boolean condition, SFunction column, Collection values); + /** + * 包含数组条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param values 条件值数组 + * @return 链式返回对象 + */ Children in(boolean condition, SFunction column, Object... values); + /** + * 不包含集合条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param values 条件值集合 + * @return 链式返回对象 + */ Children notIn(boolean condition, SFunction column, Collection values); + /** + * 不包含数组条件。 + * + * @param condition 是否添加该条件 + * @param column 字段 + * @param values 条件值数组 + * @return 链式返回对象 + */ Children notIn(boolean condition, SFunction column, Object... values); + /** + * 拼接 SQL 片段条件。 + * + * @param condition 是否添加该条件 + * @param applySql SQL 片段 + * @param values SQL 片段参数 + * @return 链式返回对象 + */ Children apply(boolean condition, String applySql, Object... values); + /** + * 值不为空时添加等于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children eqIfPresent(SFunction column, Object value) { return eq(value != null, column, value); } + /** + * 文本不为空白时添加等于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children eqIfText(SFunction column, String value) { return eq(StringUtils.isNotBlank(value), column, value); } + /** + * 值不为空时添加不等于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children neIfPresent(SFunction column, Object value) { return ne(value != null, column, value); } + /** + * 文本不为空白时添加不等于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children neIfText(SFunction column, String value) { return ne(StringUtils.isNotBlank(value), column, value); } + /** + * 值不为空时添加大于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children gtIfPresent(SFunction column, Object value) { return gt(value != null, column, value); } + /** + * 值不为空时添加大于等于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children geIfPresent(SFunction column, Object value) { return ge(value != null, column, value); } + /** + * 值不为空时添加小于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children ltIfPresent(SFunction column, Object value) { return lt(value != null, column, value); } + /** + * 值不为空时添加小于等于条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children leIfPresent(SFunction column, Object value) { return le(value != null, column, value); } + /** + * 文本不为空白时添加模糊匹配条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children likeIfText(SFunction column, String value) { return like(StringUtils.isNotBlank(value), column, value); } + /** + * 文本不为空白时添加非模糊匹配条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children notLikeIfText(SFunction column, String value) { return notLike(StringUtils.isNotBlank(value), column, value); } + /** + * 文本不为空白时添加左模糊匹配条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children likeLeftIfText(SFunction column, String value) { return likeLeft(StringUtils.isNotBlank(value), column, value); } + /** + * 文本不为空白时添加右模糊匹配条件。 + * + * @param column 字段 + * @param value 条件值 + * @return 链式返回对象 + */ default Children likeRightIfText(SFunction column, String value) { return likeRight(StringUtils.isNotBlank(value), column, value); } + /** + * 起止值均不为空时添加区间条件。 + * + * @param column 字段 + * @param begin 起始值 + * @param end 结束值 + * @return 链式返回对象 + */ default Children betweenIfPresent(SFunction column, Object begin, Object end) { return between(begin != null && end != null, column, begin, end); } + /** + * 从参数 Map 中读取起止值,均不为空时添加区间条件。 + * + * @param column 字段 + * @param params 参数 Map + * @param beginKey 起始值参数名 + * @param endKey 结束值参数名 + * @return 链式返回对象 + */ default Children betweenParams(SFunction column, Map params, String beginKey, String endKey) { if (params == null) { return between(false, column, null, null); @@ -111,34 +350,92 @@ public interface LambdaQueryCondition { return between(begin != null && end != null, column, begin, end); } + /** + * 起止值均不为空时添加非区间条件。 + * + * @param column 字段 + * @param begin 起始值 + * @param end 结束值 + * @return 链式返回对象 + */ default Children notBetweenIfPresent(SFunction column, Object begin, Object end) { return notBetween(begin != null && end != null, column, begin, end); } + /** + * 集合不为空时添加包含条件。 + * + * @param column 字段 + * @param values 条件值集合 + * @return 链式返回对象 + */ default Children inIfNotEmpty(SFunction column, Collection values) { return in(values != null && !values.isEmpty(), column, values); } + /** + * 数组不为空时添加包含条件。 + * + * @param column 字段 + * @param values 条件值数组 + * @return 链式返回对象 + */ default Children inIfNotEmpty(SFunction column, Object... values) { return in(values != null && values.length > 0, column, values); } + /** + * 集合不为空时添加不包含条件。 + * + * @param column 字段 + * @param values 条件值集合 + * @return 链式返回对象 + */ default Children notInIfNotEmpty(SFunction column, Collection values) { return notIn(values != null && !values.isEmpty(), column, values); } + /** + * 数组不为空时添加不包含条件。 + * + * @param column 字段 + * @param values 条件值数组 + * @return 链式返回对象 + */ default Children notInIfNotEmpty(SFunction column, Object... values) { return notIn(values != null && values.length > 0, column, values); } + /** + * 添加 FIND_IN_SET 条件。 + * + * @param value 匹配值 + * @param column 字段名 + * @return 链式返回对象 + */ default Children findInSet(Object value, String column) { return findInSet(true, value, column); } + /** + * 添加 FIND_IN_SET 条件。 + * + * @param condition 是否添加该条件 + * @param value 匹配值 + * @param column 字段名 + * @return 链式返回对象 + */ default Children findInSet(boolean condition, Object value, String column) { return apply(condition, DataBaseHelper.findInSet(value, column)); } + /** + * 值不为空时添加 FIND_IN_SET 条件。 + * + * @param value 匹配值 + * @param column 字段名 + * @return 链式返回对象 + */ default Children findInSetIfPresent(Object value, String column) { return findInSet(value != null, value, column); } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/QueryBuilder.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/QueryBuilder.java index 38bfe1a4f..69a2e9660 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/QueryBuilder.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/QueryBuilder.java @@ -3,7 +3,7 @@ package org.dromara.common.mybatis.core.query; import com.baomidou.mybatisplus.core.toolkit.Wrappers; /** - * MyBatis-Plus ???????? + * MyBatis-Plus 查询构造器入口。 * * @author Lion Li */ @@ -12,6 +12,13 @@ public final class QueryBuilder { private QueryBuilder() { } + /** + * 创建 Lambda 查询构造辅助对象。 + * + * @param entityClass 实体类型 + * @param 实体类型 + * @return Lambda 查询构造辅助对象 + */ public static LambdaQueryBuilder lambda(Class entityClass) { return new LambdaQueryBuilder<>(Wrappers.lambdaQuery(entityClass)); } diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/config/OssClientConfig.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/config/OssClientConfig.java index cbafc9de2..def93240f 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/config/OssClientConfig.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/config/OssClientConfig.java @@ -149,10 +149,22 @@ public class OssClientConfig implements Config policy.getType().toString().equals(type)) diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/exception/S3StorageException.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/exception/S3StorageException.java index 77c3124b2..9a3ec8b48 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/exception/S3StorageException.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/exception/S3StorageException.java @@ -12,34 +12,86 @@ public class S3StorageException extends RuntimeException { @Serial private static final long serialVersionUID = 1L; + /** + * 使用异常消息构造 S3 对象存储异常。 + * + * @param message 异常消息 + */ public S3StorageException(String message) { super(message); } + /** + * 使用异常消息和原因构造 S3 对象存储异常。 + * + * @param message 异常消息 + * @param cause 异常原因 + */ public S3StorageException(String message, Throwable cause) { super(message, cause); } + /** + * 使用异常原因构造 S3 对象存储异常。 + * + * @param cause 异常原因 + */ public S3StorageException(Throwable cause) { super(cause); } + /** + * 使用完整异常参数构造 S3 对象存储异常。 + * + * @param message 异常消息 + * @param cause 异常原因 + * @param enableSuppression 是否启用抑制 + * @param writableStackTrace 是否写入堆栈 + */ public S3StorageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } + /** + * 创建 S3 对象存储异常。 + * + * @param message 异常消息 + * @return S3 对象存储异常 + */ public static S3StorageException form(String message) { return new S3StorageException(message); } + /** + * 创建 S3 对象存储异常。 + * + * @param message 异常消息 + * @param cause 异常原因 + * @return S3 对象存储异常 + */ public static S3StorageException form(String message, Throwable cause) { return new S3StorageException(message, cause); } + /** + * 创建 S3 对象存储异常。 + * + * @param cause 异常原因 + * @return S3 对象存储异常 + */ public static S3StorageException form(Throwable cause) { return new S3StorageException(cause); } + /** + * 创建 S3 对象存储异常。 + * + * @param message 异常消息 + * @param cause 异常原因 + * @param enableSuppression 是否启用抑制 + * @param writableStackTrace 是否写入堆栈 + * @return S3 对象存储异常 + */ public static S3StorageException form(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { return new S3StorageException(message, cause, enableSuppression, writableStackTrace); } diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/HandleAsyncResult.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/HandleAsyncResult.java index d98f08901..361855899 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/HandleAsyncResult.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/HandleAsyncResult.java @@ -15,30 +15,72 @@ public record HandleAsyncResult( Throwable error ) { + /** + * 获取异步处理结果。 + * + * @return 异步处理结果 + */ public Optional getResult() { return Optional.ofNullable(result); } + /** + * 获取异步处理异常。 + * + * @return 异步处理异常 + */ public Optional getError() { return Optional.ofNullable(error); } + /** + * 是否处理成功。 + * + * @return true 成功 false 失败 + */ public boolean isSuccess() { return getError().isEmpty(); } + /** + * 是否处理失败。 + * + * @return true 失败 false 成功 + */ public boolean isFailure() { return getError().isPresent(); } + /** + * 构建异步处理结果。 + * + * @param result 结果 + * @param error 异常 + * @param 结果类型 + * @return 异步处理结果 + */ public static HandleAsyncResult of(T result, Throwable error) { return new HandleAsyncResult(result, error); } + /** + * 构建成功的异步处理结果。 + * + * @param result 结果 + * @param 结果类型 + * @return 异步处理结果 + */ public static HandleAsyncResult success(T result) { return new HandleAsyncResult(result, null); } + /** + * 构建失败的异步处理结果。 + * + * @param error 异常 + * @param 结果类型 + * @return 异步处理结果 + */ public static HandleAsyncResult failure(Throwable error) { return new HandleAsyncResult(null, error); } diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/PutObjectResult.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/PutObjectResult.java index 76e72aaa9..26b4c8bd3 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/PutObjectResult.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/PutObjectResult.java @@ -16,6 +16,15 @@ public record PutObjectResult( long size ) { + /** + * 构建文件上传结果。 + * + * @param url 文件地址 + * @param key 文件标识 + * @param eTag 文件 ETag + * @param size 文件大小 + * @return 文件上传结果 + */ public static PutObjectResult form(String url, String key, String eTag, long size) { return new PutObjectResult(url, key, eTag, size); } diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationJsonFieldProcessor.java b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationJsonFieldProcessor.java index 6b0561a12..56385b490 100644 --- a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationJsonFieldProcessor.java +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/handler/TranslationJsonFieldProcessor.java @@ -26,12 +26,26 @@ import java.util.Set; @Order(0) public class TranslationJsonFieldProcessor implements JsonFieldProcessor { + /** + * 响应增强上下文中保存待批量翻译数据的属性键。 + */ private static final String ATTR_BATCHES = TranslationJsonFieldProcessor.class.getName() + ".batches"; + /** + * 响应增强上下文中保存批量翻译结果的属性键。 + */ private static final String ATTR_RESULTS = TranslationJsonFieldProcessor.class.getName() + ".results"; + /** + * 翻译类型与翻译实现映射。 + */ private final Map> translationMap; + /** + * 构造翻译响应处理器。 + * + * @param translations 翻译实现列表 + */ public TranslationJsonFieldProcessor(List> translations) { Map> map = new LinkedHashMap<>(translations.size()); for (TranslationInterface t : translations) { @@ -43,6 +57,12 @@ public class TranslationJsonFieldProcessor implements JsonFieldProcessor { this.translationMap = Collections.unmodifiableMap(map); } + /** + * 收集字段上的翻译注解和原始值,供后续批量翻译使用。 + * + * @param fieldContext 字段上下文 + * @param context 响应增强上下文 + */ @Override public void collect(JsonFieldContext fieldContext, JsonEnhancementContext context) { Translation translation = fieldContext.getAnnotation(Translation.class); @@ -58,6 +78,11 @@ public class TranslationJsonFieldProcessor implements JsonFieldProcessor { .add(sourceValue); } + /** + * 根据收集到的原始值执行批量翻译,并将翻译结果写入响应增强上下文。 + * + * @param context 响应增强上下文 + */ @Override public void prepare(JsonEnhancementContext context) { Map> batches = context.getAttribute(ATTR_BATCHES); @@ -80,6 +105,14 @@ public class TranslationJsonFieldProcessor implements JsonFieldProcessor { context.setAttribute(ATTR_RESULTS, results); } + /** + * 处理单个字段的翻译值,优先使用批量翻译结果,缺失时回退到单值翻译。 + * + * @param fieldContext 字段上下文 + * @param value 当前字段值 + * @param context 响应增强上下文 + * @return 翻译后的字段值 + */ @Override public Object process(JsonFieldContext fieldContext, Object value, JsonEnhancementContext context) { Translation translation = fieldContext.getAnnotation(Translation.class); @@ -88,7 +121,7 @@ public class TranslationJsonFieldProcessor implements JsonFieldProcessor { } Object sourceValue = resolveSourceValue(fieldContext, translation); if (sourceValue == null) { - return null; + return value; } TranslationBatchKey batchKey = new TranslationBatchKey(translation.type(), translation.other()); Map> results = context.getAttribute(ATTR_RESULTS); @@ -110,6 +143,12 @@ public class TranslationJsonFieldProcessor implements JsonFieldProcessor { } } + /** + * 获取或创建待批量翻译数据映射。 + * + * @param context 响应增强上下文 + * @return 待批量翻译数据映射 + */ private Map> getOrCreateBatches(JsonEnhancementContext context) { Map> batches = context.getAttribute(ATTR_BATCHES); if (batches == null) { @@ -119,6 +158,13 @@ public class TranslationJsonFieldProcessor implements JsonFieldProcessor { return batches; } + /** + * 解析翻译原始值。 + * + * @param fieldContext 字段上下文 + * @param translation 翻译注解 + * @return 翻译原始值 + */ private Object resolveSourceValue(JsonFieldContext fieldContext, Translation translation) { if (StringUtils.isNotBlank(translation.mapper())) { return ReflectUtils.invokeGetter(fieldContext.owner(), translation.mapper()); @@ -126,10 +172,22 @@ public class TranslationJsonFieldProcessor implements JsonFieldProcessor { return fieldContext.value(); } + /** + * 根据翻译类型获取翻译实现。 + * + * @param type 翻译类型 + * @return 翻译实现 + */ private TranslationInterface getTranslation(String type) { return translationMap.get(type); } + /** + * 批量翻译分组键。 + * + * @param type 翻译类型 + * @param other 额外参数 + */ private record TranslationBatchKey(String type, String other) { } diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DeptNameTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DeptNameTranslationImpl.java index 72034a259..8dba3ab6c 100644 --- a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DeptNameTranslationImpl.java +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DeptNameTranslationImpl.java @@ -24,11 +24,25 @@ public class DeptNameTranslationImpl implements TranslationInterface { @DubboReference private RemoteDeptService remoteDeptService; + /** + * 将部门 ID 或 ID 集合翻译为部门名称。 + * + * @param key 部门 ID 或逗号分隔的 ID 字符串 + * @param other 额外参数 + * @return 部门名称 + */ @Override public String translation(Object key, String other) { return remoteDeptService.selectDeptNameByIds(key.toString()); } + /** + * 批量将部门 ID 翻译为部门名称。 + * + * @param keys 部门 ID 集合 + * @param other 额外参数 + * @return 部门 ID 与部门名称映射 + */ @Override public Map translationBatch(Set keys, String other) { Set deptIds = collectLongIds(keys); @@ -43,6 +57,13 @@ public class DeptNameTranslationImpl implements TranslationInterface { return result; } + /** + * 根据原始键构建部门名称翻译值。 + * + * @param source 原始键 + * @param deptNames 部门 ID 与部门名称映射 + * @return 部门名称 + */ private String buildValue(Object source, Map deptNames) { if (source instanceof String ids) { return joinMappedValues(ids, deptNames::get); diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DictTypeTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DictTypeTranslationImpl.java index 42ac8ec92..debaf37a6 100644 --- a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DictTypeTranslationImpl.java +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/DictTypeTranslationImpl.java @@ -24,6 +24,13 @@ public class DictTypeTranslationImpl implements TranslationInterface { private final DictService dictService; + /** + * 根据字典类型和字典值翻译显示标签。 + * + * @param key 字典值 + * @param other 字典类型 + * @return 字典标签 + */ @Override public String translation(Object key, String other) { if (key instanceof String && StringUtils.isNotBlank(other)) { @@ -32,6 +39,13 @@ public class DictTypeTranslationImpl implements TranslationInterface { return null; } + /** + * 批量根据字典类型和字典值翻译显示标签。 + * + * @param keys 字典值集合 + * @param other 字典类型 + * @return 字典值与字典标签映射 + */ @Override public Map translationBatch(Set keys, String other) { if (keys.isEmpty() || StringUtils.isBlank(other)) { diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java index adba34cc5..b1254cfb1 100644 --- a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java @@ -25,6 +25,13 @@ public class NicknameTranslationImpl implements TranslationInterface { @DubboReference private RemoteUserService remoteUserService; + /** + * 将用户 ID 或 ID 集合翻译为用户昵称。 + * + * @param key 用户 ID 或逗号分隔的 ID 字符串 + * @param other 额外参数 + * @return 用户昵称 + */ @Override public String translation(Object key, String other) { if (key instanceof Long id) { @@ -48,6 +55,13 @@ public class NicknameTranslationImpl implements TranslationInterface { return null; } + /** + * 批量将用户 ID 翻译为用户昵称。 + * + * @param keys 用户 ID 集合 + * @param other 额外参数 + * @return 用户 ID 与用户昵称映射 + */ @Override public Map translationBatch(Set keys, String other) { Set userIds = collectLongIds(keys); @@ -62,6 +76,13 @@ public class NicknameTranslationImpl implements TranslationInterface { return result; } + /** + * 根据原始键构建用户昵称翻译值。 + * + * @param source 原始键 + * @param userNames 用户 ID 与用户昵称映射 + * @return 用户昵称 + */ private String buildValue(Object source, Map userNames) { if (source instanceof String ids) { return joinMappedValues(ids, userNames::get); diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/OssUrlTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/OssUrlTranslationImpl.java index 9108452f8..5b29b6a35 100644 --- a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/OssUrlTranslationImpl.java +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/OssUrlTranslationImpl.java @@ -27,11 +27,25 @@ public class OssUrlTranslationImpl implements TranslationInterface { @DubboReference(mock = "true") private RemoteFileService remoteFileService; + /** + * 将 OSS ID 或 ID 集合翻译为访问地址。 + * + * @param key OSS ID 或逗号分隔的 ID 字符串 + * @param other 额外参数 + * @return 访问地址 + */ @Override public String translation(Object key, String other) { return remoteFileService.selectUrlByIds(key.toString()); } + /** + * 批量将 OSS ID 翻译为访问地址。 + * + * @param keys OSS ID 集合 + * @param other 额外参数 + * @return OSS ID 与访问地址映射 + */ @Override public Map translationBatch(Set keys, String other) { Set ossIds = collectLongIds(keys); @@ -47,6 +61,13 @@ public class OssUrlTranslationImpl implements TranslationInterface { return result; } + /** + * 根据原始键构建 OSS 地址翻译值。 + * + * @param source 原始键 + * @param ossUrls OSS ID 与访问地址映射 + * @return OSS 访问地址 + */ private String buildValue(Object source, Map ossUrls) { if (source instanceof String ids) { return joinMappedValues(ids, ossUrls::get); diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/UserNameTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/UserNameTranslationImpl.java index 87d883387..77caf44fc 100644 --- a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/UserNameTranslationImpl.java +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/UserNameTranslationImpl.java @@ -29,6 +29,13 @@ public class UserNameTranslationImpl implements TranslationInterface { @DubboReference private RemoteUserService remoteUserService; + /** + * 将用户 ID 翻译为用户名。 + * + * @param key 用户 ID + * @param other 额外参数 + * @return 用户名 + */ @Override public String translation(Object key, String other) { Long userId = Convert.toLong(key); @@ -39,6 +46,13 @@ public class UserNameTranslationImpl implements TranslationInterface { return remoteUserService.selectUserNameById(userId); } + /** + * 批量将用户 ID 翻译为用户名。 + * + * @param keys 用户 ID 集合 + * @param other 额外参数 + * @return 用户 ID 与用户名映射 + */ @Override public Map translationBatch(Set keys, String other) { Set userIds = collectLongIds(keys); @@ -53,6 +67,13 @@ public class UserNameTranslationImpl implements TranslationInterface { return result; } + /** + * 根据原始键构建用户名翻译值。 + * + * @param source 原始键 + * @param userNames 用户 ID 与用户名映射 + * @return 用户名 + */ private String buildValue(Object source, Map userNames) { if (source instanceof String ids) { return joinMappedValues(ids, userNames::get); diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/util/template/PathNamedTemplate.java b/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/util/template/PathNamedTemplate.java index e16548213..ded95dce8 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/util/template/PathNamedTemplate.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/util/template/PathNamedTemplate.java @@ -48,14 +48,35 @@ public class PathNamedTemplate implements Template { return delegate.render(bindingMap); } + /** + * 构建带路径名称的模板。 + * + * @param pathName 路径名称 + * @param delegate 模板对象 + * @return 带路径名称的模板 + */ public static PathNamedTemplate form(String pathName, Template delegate) { return new PathNamedTemplate(pathName, delegate); } + /** + * 根据模板引擎和路径名称构建模板。 + * + * @param templateEngine 模板引擎 + * @param pathName 路径名称 + * @return 带路径名称的模板 + */ public static PathNamedTemplate form(TemplateEngine templateEngine,String pathName) { return new PathNamedTemplate(pathName,templateEngine.getTemplate(pathName)); } + /** + * 根据模板引擎和路径名称集合构建模板映射。 + * + * @param templateEngine 模板引擎 + * @param pathNames 路径名称集合 + * @return 模板映射 + */ public static Map form(TemplateEngine templateEngine, Set pathNames) { Map result = new HashMap<>(); for (String pathName : pathNames) { diff --git a/ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/runner/ResourceApplicationRunner.java b/ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/runner/ResourceApplicationRunner.java index 8c7484d53..2d96edbfa 100644 --- a/ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/runner/ResourceApplicationRunner.java +++ b/ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/runner/ResourceApplicationRunner.java @@ -19,6 +19,11 @@ public class ResourceApplicationRunner implements ApplicationRunner { private final ISysOssConfigService ossConfigService; + /** + * 应用启动后初始化 OSS 配置缓存。 + * + * @param args 启动参数 + */ @Override public void run(ApplicationArguments args) throws Exception { ossConfigService.init(); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRole.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRole.java index d55a3a4c7..d59ab4c39 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRole.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysRole.java @@ -72,8 +72,4 @@ public class SysRole extends BaseEntity { */ private String remark; - public SysRole(Long roleId) { - this.roleId = roleId; - } - } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java index 59181e025..75031c810 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysUser.java @@ -104,10 +104,20 @@ public class SysUser extends BaseEntity { private String remark; + /** + * 使用用户ID构造系统用户对象。 + * + * @param userId 用户ID + */ public SysUser(Long userId) { this.userId = userId; } + /** + * 判断当前用户是否为超级管理员。 + * + * @return true 是超级管理员 false 不是超级管理员 + */ public boolean isSuperAdmin() { return SystemConstants.SUPER_ADMIN_USER_ID.equals(this.userId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysRoleBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysRoleBo.java index 3e94de87d..0f5afadaf 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysRoleBo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysRoleBo.java @@ -89,16 +89,17 @@ public class SysRoleBo implements Serializable { private Long[] deptIds; /** - * 请求参数 + * 判断当前角色是否为超级管理员角色。 + * + * @return true 是超级管理员角色 false 不是超级管理员角色 */ - private Map params = new HashMap<>(); - - public SysRoleBo(Long roleId) { - this.roleId = roleId; - } - public boolean isSuperAdmin() { return SystemConstants.SUPER_ADMIN_ROLE_ID.equals(this.roleId); } + /** + * 请求参数 + */ + private Map params = new HashMap<>(); + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java index 7e13b79cc..b04638842 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java @@ -148,5 +148,4 @@ public class SysSocialBo implements Serializable { private Map params = new HashMap<>(); - } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java index 4dd279a8e..0df98c26a 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java @@ -133,10 +133,11 @@ public class SysUserBo implements Serializable { */ private Map params = new HashMap<>(); - public SysUserBo(Long userId) { - this.userId = userId; - } - + /** + * 判断当前用户是否为超级管理员。 + * + * @return true 是超级管理员 false 不是超级管理员 + */ public boolean isSuperAdmin() { return SystemConstants.SUPER_ADMIN_USER_ID.equals(this.userId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleMenuPermVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleMenuPermVo.java index 0254e1e9a..863dfe3c2 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleMenuPermVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleMenuPermVo.java @@ -14,7 +14,13 @@ public class SysRoleMenuPermVo implements Serializable { @Serial private static final long serialVersionUID = 1L; + /** + * 角色ID + */ private Long roleId; + /** + * 权限字符串 + */ private String perms; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java index f5e01a45e..fa9680c2e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java @@ -1,9 +1,9 @@ package org.dromara.system.domain.vo; -import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated; -import org.apache.fesod.sheet.annotation.ExcelProperty; import io.github.linpeilie.annotations.AutoMapper; import lombok.Data; +import org.apache.fesod.sheet.annotation.ExcelIgnoreUnannotated; +import org.apache.fesod.sheet.annotation.ExcelProperty; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.excel.convert.ExcelDictConvert; @@ -93,6 +93,11 @@ public class SysRoleVo implements Serializable { */ private boolean flag = false; + /** + * 判断当前角色是否为超级管理员角色。 + * + * @return true 是超级管理员角色 false 不是超级管理员角色 + */ public boolean isSuperAdmin() { return SystemConstants.SUPER_ADMIN_ROLE_ID.equals(this.roleId); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java index d0ccca37c..cd1539ead 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysMenuMapper.java @@ -91,7 +91,7 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa } /** - * 根据用户ID查询菜单 + * 查询全部正常状态的目录和菜单 * * @return 菜单列表 */ @@ -128,6 +128,13 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa .toList(); } + /** + * 根据用户ID和查询条件查询菜单列表 + * + * @param menu 菜单查询条件 + * @param userId 用户ID + * @return 菜单列表 + */ default List selectMenuListByUserId(SysMenuBo menu, Long userId) { return this.selectJoinList(SysMenuVo.class, JoinWrappers.lambda("m", SysMenu.class) .distinct() @@ -146,6 +153,12 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa .orderByAsc("m", SysMenu::getOrderNum)); } + /** + * 根据用户ID查询正常状态的目录和菜单 + * + * @param userId 用户ID + * @return 菜单列表 + */ default List selectMenuTreeByUserId(Long userId) { return this.selectJoinList(SysMenu.class, JoinWrappers.lambda("m", SysMenu.class) .distinct() diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java index b7aedc1bf..a81d0ca31 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserMapper.java @@ -165,6 +165,12 @@ public interface SysUserMapper extends BaseMapperPlus, MPJBa }) int updateById(@Param(Constants.ENTITY) SysUser user); + /** + * 构建用户与角色关联查询条件 + * + * @param user 查询条件 + * @return 用户角色关联查询包装器 + */ default MPJLambdaWrapper buildUserRoleJoinWrapper(SysUserBo user) { return JoinWrappers.lambda("u", SysUser.class) .distinct() diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java index 355446791..dd851d711 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysMenuService.java @@ -110,7 +110,7 @@ public interface ISysMenuService { /** * 是否存在菜单子节点 * - * @param menuIds 菜单ID串 + * @param menuIds 菜单ID列表 * @return 结果 true 存在 false 不存在 */ boolean hasChildByMenuId(Collection menuIds); @@ -150,8 +150,7 @@ public interface ISysMenuService { /** * 批量删除菜单管理信息 * - * @param menuIds 菜单ID串 - * @return 结果 + * @param menuIds 菜单ID列表 */ void deleteMenuById(Collection menuIds); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java index be10282ac..a053f48ec 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMenuServiceImpl.java @@ -3,7 +3,6 @@ package org.dromara.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.Constants; @@ -64,15 +63,15 @@ public class SysMenuServiceImpl implements ISysMenuService { public List selectMenuList(SysMenuBo menu, Long userId) { // 管理员显示所有菜单信息 不是管理员 按用户id过滤菜单 if (LoginHelper.isSuperAdmin(userId)) { - return menuMapper.selectVoList( - new LambdaQueryWrapper() - .like(StringUtils.isNotBlank(menu.getMenuName()), SysMenu::getMenuName, menu.getMenuName()) - .eq(StringUtils.isNotBlank(menu.getVisible()), SysMenu::getVisible, menu.getVisible()) - .eq(StringUtils.isNotBlank(menu.getStatus()), SysMenu::getStatus, menu.getStatus()) - .eq(StringUtils.isNotBlank(menu.getMenuType()), SysMenu::getMenuType, menu.getMenuType()) - .eq(ObjectUtil.isNotNull(menu.getParentId()), SysMenu::getParentId, menu.getParentId()) - .orderByAsc(SysMenu::getParentId) - .orderByAsc(SysMenu::getOrderNum)); + return menuMapper.lambda() + .likeIfText(SysMenu::getMenuName, menu.getMenuName()) + .eqIfText(SysMenu::getVisible, menu.getVisible()) + .eqIfText(SysMenu::getStatus, menu.getStatus()) + .eqIfText(SysMenu::getMenuType, menu.getMenuType()) + .eqIfPresent(SysMenu::getParentId, menu.getParentId()) + .orderByAsc(SysMenu::getParentId) + .orderByAsc(SysMenu::getOrderNum) + .voList(); } return menuMapper.selectMenuListByUserId(menu, userId); } @@ -111,7 +110,7 @@ public class SysMenuServiceImpl implements ISysMenuService { } /** - * 根据用户ID查询菜单 + * 根据用户ID查询菜单树信息 * * @param userId 用户ID * @return 按树结构组织的菜单列表 @@ -252,12 +251,15 @@ public class SysMenuServiceImpl implements ISysMenuService { /** * 是否存在菜单子节点 * - * @param menuIds 菜单ID串 + * @param menuIds 菜单ID列表 * @return 结果 */ @Override public boolean hasChildByMenuId(Collection menuIds) { - return menuMapper.exists(new LambdaQueryWrapper().in(SysMenu::getParentId, menuIds).notIn(SysMenu::getMenuId, menuIds)); + return menuMapper.lambda() + .in(SysMenu::getParentId, menuIds) + .notIn(SysMenu::getMenuId, menuIds) + .exists(); } /** @@ -326,15 +328,16 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public boolean checkMenuNameUnique(SysMenuBo menu) { - boolean exist = menuMapper.exists(new LambdaQueryWrapper() + boolean exist = menuMapper.lambda() .eq(SysMenu::getMenuName, menu.getMenuName()) .eq(SysMenu::getParentId, menu.getParentId()) - .ne(ObjectUtil.isNotNull(menu.getMenuId()), SysMenu::getMenuId, menu.getMenuId())); + .neIfPresent(SysMenu::getMenuId, menu.getMenuId()) + .exists(); return !exist; } /** - * 校验路由名称是否唯一 + * 校验路由组合是否唯一 * * @param menuBo 菜单信息 * @return 结果 @@ -349,12 +352,11 @@ public class SysMenuServiceImpl implements ISysMenuService { Long parentId = menu.getParentId(); String path = menu.getPath(); String routeName = StringUtils.isEmpty(menu.getRouteName()) ? path : menu.getRouteName(); - List sysMenuList = menuMapper.selectList( - new LambdaQueryWrapper() - .in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU) - .and(w -> - w.eq(SysMenu::getPath, path).or().eq(SysMenu::getPath, routeName) - )); + List sysMenuList = menuMapper.lambda() + .in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU) + .and(w -> { + w.eq(SysMenu::getPath, path).or().eq(SysMenu::getPath, routeName); + }).list(); for (SysMenu sysMenu : sysMenuList) { if (!sysMenu.getMenuId().equals(menuId)) { Long dbParentId = sysMenu.getParentId(); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/CategoryNameTranslationImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/CategoryNameTranslationImpl.java index 212f2baeb..365b2da24 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/CategoryNameTranslationImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/CategoryNameTranslationImpl.java @@ -29,11 +29,25 @@ public class CategoryNameTranslationImpl implements TranslationInterface private final IFlwCategoryService flwCategoryService; + /** + * 将流程分类 ID 翻译为分类名称。 + * + * @param key 流程分类 ID + * @param other 额外参数 + * @return 流程分类名称 + */ @Override public String translation(Object key, String other) { return flwCategoryService.selectCategoryNameById(Convert.toLong(key)); } + /** + * 批量将流程分类 ID 翻译为分类名称。 + * + * @param keys 流程分类 ID 集合 + * @param other 额外参数 + * @return 流程分类 ID 与分类名称映射 + */ @Override public Map translationBatch(Set keys, String other) { Set categoryIds = collectLongIds(keys); @@ -48,6 +62,13 @@ public class CategoryNameTranslationImpl implements TranslationInterface return result; } + /** + * 根据原始键构建流程分类名称翻译值。 + * + * @param source 原始键 + * @param categoryNames 流程分类 ID 与分类名称映射 + * @return 流程分类名称 + */ private String buildValue(Object source, Map categoryNames) { if (source instanceof String ids) { return joinMappedValues(ids, categoryNames::get);