From fdf9e4ae3945f51d2f99ede9b7e872cce71d313c 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: Wed, 13 May 2026 14:09:28 +0800 Subject: [PATCH 01/35] update redisson 4.3.0 => 4.4.0 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 02a984b55..d4ddb14f3 100644 --- a/pom.xml +++ b/pom.xml @@ -29,7 +29,7 @@ 3.9.1 5.8.43 4.0.2 - 4.3.0 + 4.4.0 2.2.7 4.5.0 1.9.0 From 7a5d6b472319cbdb6b9e9ee3021d47b5dbe20709 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 11:39:20 +0800 Subject: [PATCH 02/35] =?UTF-8?q?[=E9=87=8D=E5=A4=A7=E6=9B=B4=E6=96=B0]=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0mybatis-plus=E6=89=A9=E5=B1=95=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=20=E5=A4=A7=E5=B9=85=E7=AE=80=E5=8C=96=E6=95=B4?= =?UTF-8?q?=E4=BD=93=E4=B8=9A=E5=8A=A1=E4=BB=A3=E7=A0=81=E5=86=99=E6=B3=95?= =?UTF-8?q?=20=E4=BC=98=E9=9B=85=E8=88=92=E9=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/service/SysRegisterService.java | 6 +- .../web/service/impl/EmailAuthStrategy.java | 5 +- .../service/impl/PasswordAuthStrategy.java | 5 +- .../web/service/impl/SmsAuthStrategy.java | 5 +- .../mybatis/core/mapper/BaseMapperPlus.java | 20 + .../core/mapper/LambdaCrudChainWrapper.java | 399 +++++++++++++++++ .../core/query/LambdaQueryBuilder.java | 422 ++++++++++++++++++ .../core/query/LambdaQueryCondition.java | 146 ++++++ .../mybatis/core/query/QueryBuilder.java | 19 + .../demo/controller/TestBatchController.java | 8 +- .../gen/service/GenTableServiceImpl.java | 15 +- .../resources/vm/java/serviceImpl.java.vm | 64 ++- .../dromara/system/mapper/SysDeptMapper.java | 9 +- .../system/mapper/SysDictDataMapper.java | 9 +- .../dromara/system/mapper/SysMenuMapper.java | 7 +- .../dromara/system/mapper/SysPostMapper.java | 3 +- .../dromara/system/mapper/SysRoleMapper.java | 3 +- .../system/mapper/SysRoleMenuMapper.java | 3 +- .../dromara/system/mapper/SysUserMapper.java | 3 +- .../system/mapper/SysUserRoleMapper.java | 9 +- .../service/impl/SysClientServiceImpl.java | 32 +- .../service/impl/SysConfigServiceImpl.java | 30 +- .../service/impl/SysDataScopeServiceImpl.java | 9 +- .../service/impl/SysDeptServiceImpl.java | 80 ++-- .../service/impl/SysDictDataServiceImpl.java | 30 +- .../service/impl/SysDictTypeServiceImpl.java | 28 +- .../service/impl/SysLoginInfoServiceImpl.java | 35 +- .../service/impl/SysMenuServiceImpl.java | 44 +- .../service/impl/SysMessageServiceImpl.java | 30 +- .../service/impl/SysNoticeServiceImpl.java | 16 +- .../service/impl/SysOperLogServiceImpl.java | 30 +- .../service/impl/SysOssConfigServiceImpl.java | 37 +- .../service/impl/SysOssServiceImpl.java | 23 +- .../service/impl/SysPostServiceImpl.java | 49 +- .../service/impl/SysRoleServiceImpl.java | 79 ++-- .../service/impl/SysSocialServiceImpl.java | 17 +- .../service/impl/SysUserServiceImpl.java | 165 +++---- .../workflow/mapper/FlwCategoryMapper.java | 7 +- .../mapper/FlwInstanceBizExtMapper.java | 12 +- .../service/impl/FlwCategoryServiceImpl.java | 25 +- .../impl/FlwDefinitionServiceImpl.java | 4 +- .../service/impl/FlwInstanceServiceImpl.java | 24 +- .../service/impl/FlwSpelServiceImpl.java | 15 +- .../service/impl/FlwTaskServiceImpl.java | 37 +- 44 files changed, 1535 insertions(+), 483 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/LambdaCrudChainWrapper.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryBuilder.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryCondition.java create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/QueryBuilder.java diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java index 744b0f8d6..b6ccca1a4 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysRegisterService.java @@ -1,7 +1,6 @@ package org.dromara.web.service; import cn.hutool.crypto.digest.BCrypt; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.Constants; import org.dromara.common.core.constant.GlobalConstants; @@ -58,8 +57,9 @@ public class SysRegisterService { sysUser.setPassword(BCrypt.hashpw(password)); sysUser.setUserType(userType); - boolean exist = userMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getUserName, sysUser.getUserName())); + boolean exist = userMapper.lambda() + .eq(SysUser::getUserName, sysUser.getUserName()) + .exists(); if (exist) { throw new UserException("user.register.save.error", username); } diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java index f98ffad16..5fe3dd653 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/EmailAuthStrategy.java @@ -3,7 +3,6 @@ package org.dromara.web.service.impl; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.parameter.SaLoginParameter; 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; @@ -95,7 +94,9 @@ public class EmailAuthStrategy implements IAuthStrategy { * @return 用户信息 */ private SysUserVo loadUserByEmail(String email) { - SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getEmail, email)); + SysUserVo user = userMapper.lambda() + .eq(SysUser::getEmail, email) + .voOne(); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", email); throw new UserException("user.not.exists", email); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java index 4752175f3..cdb6022c7 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/PasswordAuthStrategy.java @@ -4,7 +4,6 @@ import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.parameter.SaLoginParameter; import cn.hutool.core.util.ObjectUtil; import cn.hutool.crypto.digest.BCrypt; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.Constants; @@ -112,7 +111,9 @@ public class PasswordAuthStrategy implements IAuthStrategy { * @return 用户信息 */ private SysUserVo loadUserByUsername(String username) { - SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, username)); + SysUserVo user = userMapper.lambda() + .eq(SysUser::getUserName, username) + .voOne(); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", username); throw new UserException("user.not.exists", username); diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java index 523efc503..d938a40a8 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/impl/SmsAuthStrategy.java @@ -3,7 +3,6 @@ package org.dromara.web.service.impl; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.parameter.SaLoginParameter; 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; @@ -95,7 +94,9 @@ public class SmsAuthStrategy implements IAuthStrategy { * @return 用户信息 */ private SysUserVo loadUserByPhoneNumber(String phoneNumber) { - SysUserVo user = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhoneNumber, phoneNumber)); + SysUserVo user = userMapper.lambda() + .eq(SysUser::getPhoneNumber, phoneNumber) + .voOne(); if (ObjectUtil.isNull(user)) { log.info("登录用户:{} 不存在.", phoneNumber); throw new UserException("user.not.exists", phoneNumber); diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java index 24557edb6..566c63eeb 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java @@ -7,7 +7,9 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.toolkit.reflect.GenericTypeUtils; +import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers; import com.baomidou.mybatisplus.extension.toolkit.Db; import org.apache.ibatis.logging.Log; import org.apache.ibatis.logging.LogFactory; @@ -60,6 +62,24 @@ public interface BaseMapperPlus extends BaseMapper { return this.selectList(new QueryWrapper<>()); } + /** + * 创建当前 Mapper 绑定的 Lambda CRUD 链式操作。 + * + * @return Lambda CRUD 链式包装器 + */ + default LambdaCrudChainWrapper lambda() { + return new LambdaCrudChainWrapper<>(this); + } + + /** + * 创建当前 Mapper 绑定的 Lambda 链式更新。 + * + * @return Lambda 链式更新包装器 + */ + default LambdaUpdateChainWrapper lambdaUpdate() { + return ChainWrappers.lambdaUpdateChain(this); + } + /** * 批量插入实体对象集合 * 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 new file mode 100644 index 000000000..3ca49382a --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/LambdaCrudChainWrapper.java @@ -0,0 +1,399 @@ +package org.dromara.common.mybatis.core.mapper; + +import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper; +import com.baomidou.mybatisplus.core.conditions.SharedString; +import com.baomidou.mybatisplus.core.conditions.query.Query; +import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; +import com.baomidou.mybatisplus.core.conditions.update.Update; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; +import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; +import com.baomidou.mybatisplus.core.toolkit.Assert; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.core.toolkit.StringUtils; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import org.dromara.common.mybatis.core.query.LambdaQueryCondition; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.function.Predicate; + +/** + * Mapper 级 Lambda CRUD 链式包装器。 + * + * @param table 泛型 + * @param vo 泛型 + * @author Lion Li + */ +public class LambdaCrudChainWrapper extends AbstractLambdaWrapper> + implements Query, T, SFunction>, + Update, SFunction>, + LambdaQueryCondition> { + + private final BaseMapperPlus baseMapper; + private final List sqlSet; + private SharedString sqlSelect = new SharedString(); + + public LambdaCrudChainWrapper(BaseMapperPlus baseMapper) { + this.baseMapper = baseMapper; + super.setEntityClass(baseMapper.currentModelClass()); + super.initNeed(); + this.sqlSet = new ArrayList<>(); + } + + LambdaCrudChainWrapper(BaseMapperPlus baseMapper, T entity, Class entityClass, SharedString sqlSelect, + List sqlSet, AtomicInteger paramNameSeq, Map paramNameValuePairs, + MergeSegments mergeSegments, SharedString paramAlias, SharedString lastSql, + SharedString sqlComment, SharedString sqlFirst) { + this.baseMapper = baseMapper; + super.setEntity(entity); + super.setEntityClass(entityClass); + this.sqlSelect = sqlSelect == null ? new SharedString() : sqlSelect; + this.sqlSet = sqlSet == null ? new ArrayList<>() : sqlSet; + this.paramNameSeq = paramNameSeq; + this.paramNameValuePairs = paramNameValuePairs; + this.expression = mergeSegments; + this.paramAlias = paramAlias; + this.lastSql = lastSql; + this.sqlComment = sqlComment; + this.sqlFirst = sqlFirst; + } + + @Override + public LambdaCrudChainWrapper select(boolean condition, List> columns) { + if (condition && CollectionUtils.isNotEmpty(columns)) { + this.sqlSelect.setStringValue(columnsToString(false, columns)); + } + return typedThis; + } + + @SafeVarargs + public final LambdaCrudChainWrapper select(SFunction... columns) { + return select(true, CollectionUtils.toList(columns)); + } + + @SafeVarargs + public final LambdaCrudChainWrapper select(boolean condition, SFunction... columns) { + return select(condition, CollectionUtils.toList(columns)); + } + + @Override + public LambdaCrudChainWrapper select(Class entityClass, Predicate predicate) { + if (entityClass == null) { + entityClass = getEntityClass(); + } else { + setEntityClass(entityClass); + } + Assert.notNull(entityClass, "entityClass can not be null"); + this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(entityClass).chooseSelect(predicate)); + return typedThis; + } + + @Override + public String getSqlSelect() { + return sqlSelect.getStringValue(); + } + + @Override + public LambdaCrudChainWrapper set(boolean condition, SFunction column, Object val, String mapping) { + return maybeDo(condition, () -> { + String sql = formatParam(mapping, val); + sqlSet.add(columnToString(column) + Constants.EQUALS + sql); + }); + } + + /** + * 值不为 null 时设置更新字段。 + * + * @param column 字段 + * @param value 值 + * @return this + */ + public LambdaCrudChainWrapper setIfPresent(SFunction column, Object value) { + return set(value != null, column, value); + } + + /** + * 文本不为空时设置更新字段。 + * + * @param column 字段 + * @param value 值 + * @return this + */ + public LambdaCrudChainWrapper setIfText(SFunction column, String value) { + return set(org.dromara.common.core.utils.StringUtils.isNotBlank(value), column, value); + } + + @Override + public LambdaCrudChainWrapper setSql(boolean condition, String setSql, Object... params) { + return maybeDo(condition && StringUtils.isNotBlank(setSql), () -> sqlSet.add(formatSqlMaybeWithParam(setSql, params))); + } + + @Override + public LambdaCrudChainWrapper setIncrBy(boolean condition, SFunction column, Number val) { + return maybeDo(condition, () -> { + String realColumn = columnToString(column); + String realVal = val instanceof BigDecimal ? ((BigDecimal) val).toPlainString() : String.valueOf(val); + sqlSet.add(String.format("%s=%s + %s", realColumn, realColumn, realVal)); + }); + } + + @Override + public LambdaCrudChainWrapper setDecrBy(boolean condition, SFunction column, Number val) { + return maybeDo(condition, () -> { + String realColumn = columnToString(column); + String realVal = val instanceof BigDecimal ? ((BigDecimal) val).toPlainString() : String.valueOf(val); + sqlSet.add(String.format("%s=%s - %s", realColumn, realColumn, realVal)); + }); + } + + @Override + public String getSqlSet() { + if (CollectionUtils.isEmpty(sqlSet)) { + return null; + } + return String.join(Constants.COMMA, sqlSet); + } + + /** + * 获取查询条件 Wrapper。 + * + * @return this + */ + public LambdaCrudChainWrapper getWrapper() { + return typedThis; + } + + public LambdaCrudChainWrapper findInSet(Object value, SFunction column) { + return findInSet(true, value, column); + } + + public LambdaCrudChainWrapper findInSet(boolean condition, Object value, SFunction column) { + return findInSet(condition, value, columnToString(column)); + } + + public LambdaCrudChainWrapper findInSetIfPresent(Object value, SFunction column) { + return findInSet(value != null, value, column); + } + + /** + * 获取当前 Wrapper。 + * + * @return this + */ + public LambdaCrudChainWrapper build() { + return typedThis; + } + + /** + * 查询实体列表。 + * + * @return 实体列表 + */ + public List list() { + return baseMapper.selectList(typedThis); + } + + /** + * 查询实体分页记录。 + * + * @param page 分页条件 + * @return 实体分页记录 + */ + public List list(IPage page) { + return baseMapper.selectList(page, typedThis); + } + + /** + * 查询 VO 列表。 + * + * @return VO 列表 + */ + public List voList() { + return baseMapper.selectVoList(typedThis); + } + + /** + * 查询单列对象列表。 + * + * @return 单列对象列表 + */ + public List objs() { + return baseMapper.selectObjs(typedThis); + } + + /** + * 查询单列对象列表并转换类型。 + * + * @param mapper 转换函数 + * @param 转换后的类型 + * @return 单列对象列表 + */ + public List objs(Function mapper) { + return baseMapper.selectObjs(typedThis, mapper); + } + + /** + * 查询单个实体。 + * + * @return 实体 + */ + public T one() { + return baseMapper.selectOne(typedThis); + } + + /** + * 查询单个实体。 + * + * @param throwEx 查询到多条时是否抛异常 + * @return 实体 + */ + public T one(boolean throwEx) { + return baseMapper.selectOne(typedThis, throwEx); + } + + /** + * 查询单个实体 Optional。 + * + * @return Optional 实体 + */ + public Optional oneOpt() { + return Optional.ofNullable(one()); + } + + /** + * 查询单个 VO。 + * + * @return VO + */ + public V voOne() { + return baseMapper.selectVoOne(typedThis); + } + + /** + * 查询单个 VO。 + * + * @param throwEx 查询到多条时是否抛异常 + * @return VO + */ + public V voOne(boolean throwEx) { + return baseMapper.selectVoOne(typedThis, throwEx); + } + + /** + * 查询数量。 + * + * @return 数量 + */ + public Long count() { + return baseMapper.selectCount(typedThis); + } + + /** + * 判断是否存在。 + * + * @return 是否存在 + */ + public boolean exists() { + return baseMapper.exists(typedThis); + } + + /** + * 查询实体分页。 + * + * @param page 分页条件 + * @param

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

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

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

> P voPage(IPage page) { + return baseMapper.selectVoPage(page, typedThis); + } + + /** + * 删除数据。 + * + * @return 是否删除成功 + */ + public boolean delete() { + return deleteCount() > 0; + } + + /** + * 删除数据。 + * + * @return 影响行数 + */ + public int deleteCount() { + return baseMapper.delete(typedThis); + } + + /** + * 使用 set 片段更新数据。 + * + * @return 是否更新成功 + */ + public boolean update() { + return updateCount() > 0; + } + + /** + * 使用实体和查询条件更新数据。 + * + * @param entity 实体 + * @return 是否更新成功 + */ + public boolean update(T entity) { + return updateCount(entity) > 0; + } + + /** + * 使用 set 片段更新数据。 + * + * @return 影响行数 + */ + public int updateCount() { + return baseMapper.update(typedThis); + } + + /** + * 使用实体和查询条件更新数据。 + * + * @param entity 实体 + * @return 影响行数 + */ + public int updateCount(T entity) { + return baseMapper.update(entity, typedThis); + } + + @Override + protected LambdaCrudChainWrapper instance() { + return new LambdaCrudChainWrapper<>(baseMapper, getEntity(), getEntityClass(), null, null, paramNameSeq, + paramNameValuePairs, new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), + SharedString.emptyString()); + } + + @Override + public void clear() { + super.clear(); + sqlSelect.toNull(); + sqlSet.clear(); + } + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryBuilder.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryBuilder.java new file mode 100644 index 000000000..2de14879f --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryBuilder.java @@ -0,0 +1,422 @@ +package org.dromara.common.mybatis.core.query; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.function.BiPredicate; +import java.util.function.Consumer; + +/** + * LambdaQueryWrapper 条件构造辅助类。 + * + * @param 实体类型 + * @author Lion Li + */ +public final class LambdaQueryBuilder implements LambdaQueryCondition> { + + private final LambdaQueryWrapper wrapper; + + LambdaQueryBuilder(LambdaQueryWrapper wrapper) { + this.wrapper = wrapper; + } + + @SafeVarargs + public final LambdaQueryBuilder select(SFunction... columns) { + wrapper.select(columns); + return this; + } + + public LambdaQueryBuilder eq(SFunction column, Object value) { + wrapper.eq(column, value); + return this; + } + + public LambdaQueryBuilder eq(boolean condition, SFunction column, Object value) { + wrapper.eq(condition, column, value); + return this; + } + + public LambdaQueryBuilder ne(SFunction column, Object value) { + wrapper.ne(column, value); + return this; + } + + public LambdaQueryBuilder ne(boolean condition, SFunction column, Object value) { + wrapper.ne(condition, column, value); + return this; + } + + public LambdaQueryBuilder gt(SFunction column, Object value) { + wrapper.gt(column, value); + return this; + } + + public LambdaQueryBuilder gt(boolean condition, SFunction column, Object value) { + wrapper.gt(condition, column, value); + return this; + } + + public LambdaQueryBuilder ge(SFunction column, Object value) { + wrapper.ge(column, value); + return this; + } + + public LambdaQueryBuilder ge(boolean condition, SFunction column, Object value) { + wrapper.ge(condition, column, value); + return this; + } + + public LambdaQueryBuilder lt(SFunction column, Object value) { + wrapper.lt(column, value); + return this; + } + + public LambdaQueryBuilder lt(boolean condition, SFunction column, Object value) { + wrapper.lt(condition, column, value); + return this; + } + + public LambdaQueryBuilder le(SFunction column, Object value) { + wrapper.le(column, value); + return this; + } + + public LambdaQueryBuilder le(boolean condition, SFunction column, Object value) { + wrapper.le(condition, column, value); + return this; + } + + public LambdaQueryBuilder like(SFunction column, Object value) { + wrapper.like(column, value); + return this; + } + + public LambdaQueryBuilder like(boolean condition, SFunction column, Object value) { + wrapper.like(condition, column, value); + return this; + } + + public LambdaQueryBuilder notLike(SFunction column, Object value) { + wrapper.notLike(column, value); + return this; + } + + public LambdaQueryBuilder notLike(boolean condition, SFunction column, Object value) { + wrapper.notLike(condition, column, value); + return this; + } + + public LambdaQueryBuilder likeLeft(SFunction column, Object value) { + wrapper.likeLeft(column, value); + return this; + } + + public LambdaQueryBuilder likeLeft(boolean condition, SFunction column, Object value) { + wrapper.likeLeft(condition, column, value); + return this; + } + + public LambdaQueryBuilder likeRight(SFunction column, Object value) { + wrapper.likeRight(column, value); + return this; + } + + public LambdaQueryBuilder likeRight(boolean condition, SFunction column, Object value) { + wrapper.likeRight(condition, column, value); + return this; + } + + public LambdaQueryBuilder between(SFunction column, Object begin, Object end) { + wrapper.between(column, begin, end); + return this; + } + + public LambdaQueryBuilder between(boolean condition, SFunction column, Object begin, Object end) { + wrapper.between(condition, column, begin, end); + return this; + } + + public LambdaQueryBuilder notBetween(SFunction column, Object begin, Object end) { + wrapper.notBetween(column, begin, end); + return this; + } + + public LambdaQueryBuilder notBetween(boolean condition, SFunction column, Object begin, Object end) { + wrapper.notBetween(condition, column, begin, end); + return this; + } + + public LambdaQueryBuilder allEq(Map params, boolean null2IsNull) { + wrapper.allEq(true, (Map) params, null2IsNull); + return this; + } + + public LambdaQueryBuilder allEq(boolean condition, Map params, boolean null2IsNull) { + wrapper.allEq(condition, (Map) params, null2IsNull); + return this; + } + + public LambdaQueryBuilder allEq(BiPredicate, Object> filter, Map params, boolean null2IsNull) { + wrapper.allEq(true, (BiPredicate) filter, (Map) params, null2IsNull); + return this; + } + + public LambdaQueryBuilder allEq(boolean condition, BiPredicate, Object> filter, Map params, boolean null2IsNull) { + wrapper.allEq(condition, (BiPredicate) filter, (Map) params, null2IsNull); + return this; + } + + public LambdaQueryBuilder isNull(SFunction column) { + wrapper.isNull(column); + return this; + } + + public LambdaQueryBuilder isNull(boolean condition, SFunction column) { + wrapper.isNull(condition, column); + return this; + } + + public LambdaQueryBuilder isNotNull(SFunction column) { + wrapper.isNotNull(column); + return this; + } + + public LambdaQueryBuilder isNotNull(boolean condition, SFunction column) { + wrapper.isNotNull(condition, column); + return this; + } + + public LambdaQueryBuilder in(SFunction column, Collection values) { + wrapper.in(column, values); + return this; + } + + public LambdaQueryBuilder in(boolean condition, SFunction column, Collection values) { + wrapper.in(condition, column, values); + return this; + } + + public LambdaQueryBuilder in(SFunction column, Object... values) { + wrapper.in(column, values); + return this; + } + + public LambdaQueryBuilder in(boolean condition, SFunction column, Object... values) { + wrapper.in(condition, column, values); + return this; + } + + public LambdaQueryBuilder notIn(SFunction column, Collection values) { + wrapper.notIn(column, values); + return this; + } + + public LambdaQueryBuilder notIn(boolean condition, SFunction column, Collection values) { + wrapper.notIn(condition, column, values); + return this; + } + + public LambdaQueryBuilder notIn(SFunction column, Object... values) { + wrapper.notIn(column, values); + return this; + } + + public LambdaQueryBuilder notIn(boolean condition, SFunction column, Object... values) { + wrapper.notIn(condition, column, values); + return this; + } + + @SafeVarargs + public final LambdaQueryBuilder groupBy(SFunction... columns) { + wrapper.groupBy(Arrays.asList(columns)); + return this; + } + + @SafeVarargs + public final LambdaQueryBuilder groupBy(boolean condition, SFunction... columns) { + wrapper.groupBy(condition, Arrays.asList(columns)); + return this; + } + + public LambdaQueryBuilder orderBy(boolean condition, boolean isAsc, SFunction column) { + wrapper.orderBy(condition, isAsc, column); + return this; + } + + @SafeVarargs + public final LambdaQueryBuilder orderByAsc(SFunction... columns) { + wrapper.orderByAsc(Arrays.asList(columns)); + return this; + } + + @SafeVarargs + public final LambdaQueryBuilder orderByAsc(boolean condition, SFunction... columns) { + wrapper.orderByAsc(condition, Arrays.asList(columns)); + return this; + } + + @SafeVarargs + public final LambdaQueryBuilder orderByDesc(SFunction... columns) { + wrapper.orderByDesc(Arrays.asList(columns)); + return this; + } + + @SafeVarargs + public final LambdaQueryBuilder orderByDesc(boolean condition, SFunction... columns) { + wrapper.orderByDesc(condition, Arrays.asList(columns)); + return this; + } + + public LambdaQueryBuilder having(String sqlHaving, Object... params) { + wrapper.having(sqlHaving, params); + return this; + } + + public LambdaQueryBuilder having(boolean condition, String sqlHaving, Object... params) { + wrapper.having(condition, sqlHaving, params); + return this; + } + + public LambdaQueryBuilder and(Consumer> consumer) { + wrapper.and(consumer); + return this; + } + + public LambdaQueryBuilder and(boolean condition, Consumer> consumer) { + wrapper.and(condition, consumer); + return this; + } + + public LambdaQueryBuilder or() { + wrapper.or(); + return this; + } + + public LambdaQueryBuilder or(Consumer> consumer) { + wrapper.or(consumer); + return this; + } + + public LambdaQueryBuilder or(boolean condition, Consumer> consumer) { + wrapper.or(condition, consumer); + return this; + } + + public LambdaQueryBuilder nested(Consumer> consumer) { + wrapper.nested(consumer); + return this; + } + + public LambdaQueryBuilder nested(boolean condition, Consumer> consumer) { + wrapper.nested(condition, consumer); + return this; + } + + public LambdaQueryBuilder exists(String existsSql, Object... values) { + wrapper.exists(existsSql, values); + return this; + } + + public LambdaQueryBuilder exists(boolean condition, String existsSql, Object... values) { + wrapper.exists(condition, existsSql, values); + return this; + } + + public LambdaQueryBuilder eqSql(SFunction column, String inValue) { + wrapper.eqSql(true, column, inValue); + return this; + } + + public LambdaQueryBuilder eqSql(boolean condition, SFunction column, String inValue) { + wrapper.eqSql(condition, column, inValue); + return this; + } + + public LambdaQueryBuilder inSql(SFunction column, String inValue) { + wrapper.inSql(true, column, inValue); + return this; + } + + public LambdaQueryBuilder inSql(boolean condition, SFunction column, String inValue) { + wrapper.inSql(condition, column, inValue); + return this; + } + + public LambdaQueryBuilder notInSql(SFunction column, String inValue) { + wrapper.notInSql(true, column, inValue); + return this; + } + + public LambdaQueryBuilder notInSql(boolean condition, SFunction column, String inValue) { + wrapper.notInSql(condition, column, inValue); + return this; + } + + public LambdaQueryBuilder notExists(String existsSql, Object... values) { + wrapper.notExists(existsSql, values); + return this; + } + + public LambdaQueryBuilder notExists(boolean condition, String existsSql, Object... values) { + wrapper.notExists(condition, existsSql, values); + return this; + } + + public LambdaQueryBuilder apply(String applySql, Object... values) { + wrapper.apply(applySql, values); + return this; + } + + public LambdaQueryBuilder apply(boolean condition, String applySql, Object... values) { + wrapper.apply(condition, applySql, values); + return this; + } + + public LambdaQueryBuilder apply(Consumer> consumer) { + consumer.accept(wrapper); + return this; + } + + public LambdaQueryBuilder func(Consumer> consumer) { + wrapper.func(true, consumer); + return this; + } + + public LambdaQueryBuilder func(boolean condition, Consumer> consumer) { + wrapper.func(condition, consumer); + return this; + } + + public LambdaQueryBuilder not(Consumer> consumer) { + wrapper.not(true, consumer); + return this; + } + + public LambdaQueryBuilder not(boolean condition, Consumer> consumer) { + wrapper.not(condition, consumer); + return this; + } + + public LambdaQueryBuilder comment(String comment) { + wrapper.comment(true, comment); + return this; + } + + public LambdaQueryBuilder comment(boolean condition, String comment) { + wrapper.comment(condition, comment); + return this; + } + + public LambdaQueryBuilder last(String lastSql) { + wrapper.last(lastSql); + return this; + } + + 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 new file mode 100644 index 000000000..459465e91 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryCondition.java @@ -0,0 +1,146 @@ +package org.dromara.common.mybatis.core.query; + +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.helper.DataBaseHelper; + +import java.util.Collection; +import java.util.Map; + +/** + * Lambda 查询常用条件扩展。 + * + * @param 实体类型 + * @param 链式返回类型 + * @author Lion Li + */ +public interface LambdaQueryCondition { + + Children eq(boolean condition, SFunction column, Object value); + + Children ne(boolean condition, SFunction column, Object value); + + Children gt(boolean condition, SFunction column, Object value); + + Children ge(boolean condition, SFunction column, Object value); + + Children lt(boolean condition, SFunction column, Object value); + + Children le(boolean condition, SFunction column, Object value); + + Children like(boolean condition, SFunction column, Object value); + + Children notLike(boolean condition, SFunction column, Object value); + + Children likeLeft(boolean condition, SFunction column, Object value); + + Children likeRight(boolean condition, SFunction column, Object value); + + Children between(boolean condition, SFunction column, Object begin, Object end); + + Children notBetween(boolean condition, SFunction column, Object begin, Object end); + + Children in(boolean condition, SFunction column, Collection values); + + Children in(boolean condition, SFunction column, Object... values); + + Children notIn(boolean condition, SFunction column, Collection values); + + Children notIn(boolean condition, SFunction column, Object... values); + + Children apply(boolean condition, String applySql, Object... values); + + default Children eqIfPresent(SFunction column, Object value) { + return eq(value != null, column, value); + } + + default Children eqIfText(SFunction column, String value) { + return eq(StringUtils.isNotBlank(value), column, value); + } + + default Children neIfPresent(SFunction column, Object value) { + return ne(value != null, column, value); + } + + default Children neIfText(SFunction column, String value) { + return ne(StringUtils.isNotBlank(value), column, value); + } + + default Children gtIfPresent(SFunction column, Object value) { + return gt(value != null, column, value); + } + + default Children geIfPresent(SFunction column, Object value) { + return ge(value != null, column, value); + } + + default Children ltIfPresent(SFunction column, Object value) { + return lt(value != null, column, value); + } + + default Children leIfPresent(SFunction column, Object value) { + return le(value != null, column, value); + } + + default Children likeIfText(SFunction column, String value) { + return like(StringUtils.isNotBlank(value), column, value); + } + + default Children notLikeIfText(SFunction column, String value) { + return notLike(StringUtils.isNotBlank(value), column, value); + } + + default Children likeLeftIfText(SFunction column, String value) { + return likeLeft(StringUtils.isNotBlank(value), column, value); + } + + default Children likeRightIfText(SFunction column, String value) { + return likeRight(StringUtils.isNotBlank(value), column, value); + } + + default Children betweenIfPresent(SFunction column, Object begin, Object end) { + return between(begin != null && end != null, column, begin, end); + } + + default Children betweenParams(SFunction column, Map params, String beginKey, String endKey) { + if (params == null) { + return between(false, column, null, null); + } + Object begin = params.get(beginKey); + Object end = params.get(endKey); + return between(begin != null && end != null, column, begin, end); + } + + default Children notBetweenIfPresent(SFunction column, Object begin, Object end) { + return notBetween(begin != null && end != null, column, begin, end); + } + + default Children inIfNotEmpty(SFunction column, Collection values) { + return in(values != null && !values.isEmpty(), column, values); + } + + default Children inIfNotEmpty(SFunction column, Object... values) { + return in(values != null && values.length > 0, column, values); + } + + default Children notInIfNotEmpty(SFunction column, Collection values) { + return notIn(values != null && !values.isEmpty(), column, values); + } + + default Children notInIfNotEmpty(SFunction column, Object... values) { + return notIn(values != null && values.length > 0, column, values); + } + + default Children findInSet(Object value, String column) { + return findInSet(true, value, column); + } + + default Children findInSet(boolean condition, Object value, String column) { + return apply(condition, DataBaseHelper.findInSet(value, column)); + } + + 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 new file mode 100644 index 000000000..30b710d80 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/QueryBuilder.java @@ -0,0 +1,19 @@ +package org.dromara.common.mybatis.core.query; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; + +/** + * MyBatis-Plus 查询构造器入口。 + * + * @author Lion Li + */ +public final class QueryBuilder { + + private QueryBuilder() { + } + + public static LambdaQueryBuilder lambda(Class entityClass) { + return new LambdaQueryBuilder<>(Wrappers.lambdaQuery(entityClass)); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestBatchController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestBatchController.java index af8c77b3a..403eab494 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestBatchController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestBatchController.java @@ -1,11 +1,10 @@ package org.dromara.demo.controller; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; import org.dromara.common.web.core.BaseController; import org.dromara.demo.domain.TestDemo; import org.dromara.demo.mapper.TestDemoMapper; -import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -83,8 +82,9 @@ public class TestBatchController extends BaseController { @DeleteMapping() // @DS("slave") public R remove() { - return toAjax(testDemoMapper.delete(new LambdaQueryWrapper() - .eq(TestDemo::getOrderNum, -1L))); + return toAjax(testDemoMapper.lambda() + .eq(TestDemo::getOrderNum, -1L) + .deleteCount()); } } diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java b/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java index 4c0595004..ad1c5022c 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java @@ -8,7 +8,6 @@ import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import com.baomidou.dynamic.datasource.annotation.DS; import com.baomidou.dynamic.datasource.annotation.DSTransactional; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -66,9 +65,10 @@ public class GenTableServiceImpl implements IGenTableService { */ @Override public List selectGenTableColumnListByTableId(Long tableId) { - return genTableColumnMapper.selectList(new LambdaQueryWrapper() + return genTableColumnMapper.lambda() .eq(GenTableColumn::getTableId, tableId) - .orderByAsc(GenTableColumn::getSort)); + .orderByAsc(GenTableColumn::getSort) + .list(); } /** @@ -246,7 +246,9 @@ public class GenTableServiceImpl implements IGenTableService { public void deleteGenTableByIds(Long[] tableIds) { List ids = Arrays.asList(tableIds); baseMapper.deleteByIds(ids); - genTableColumnMapper.delete(new LambdaQueryWrapper().in(GenTableColumn::getTableId, ids)); + genTableColumnMapper.lambda() + .in(GenTableColumn::getTableId, ids) + .deleteCount(); } /** @@ -551,10 +553,11 @@ public class GenTableServiceImpl implements IGenTableService { return tables; } List tableIds = StreamUtils.toList(tables, GenTable::getTableId); - List columns = genTableColumnMapper.selectList(new LambdaQueryWrapper() + List columns = genTableColumnMapper.lambda() .in(GenTableColumn::getTableId, tableIds) .orderByAsc(GenTableColumn::getTableId) - .orderByAsc(GenTableColumn::getSort)); + .orderByAsc(GenTableColumn::getSort) + .list(); Map> columnMap = StreamUtils.groupByKey(columns, GenTableColumn::getTableId); tables.forEach(table -> table.setColumns(columnMap.getOrDefault(table.getTableId(), new ArrayList<>()))); return tables; diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm index ce5a6dff3..b7dc57b3d 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm @@ -8,11 +8,11 @@ import org.dromara.common.core.domain.PageResult; import org.dromara.common.mybatis.core.page.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; #end -#if($enableStatus || $enableSort) -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -#end import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +#if($enableUnique) import com.baomidou.mybatisplus.core.toolkit.Wrappers; +#end +import org.dromara.common.mybatis.core.query.QueryBuilder; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -134,7 +134,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { #if($hasBetween) Map params = bo.getParams(); #end - LambdaQueryWrapper<${ClassName}> lqw = Wrappers.lambdaQuery(); + return QueryBuilder.lambda(${ClassName}.class) #foreach($column in $columns) #if($column.query) #set($queryType=$column.queryType) @@ -144,29 +144,47 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { #if($queryType != 'BETWEEN') #if($javaType == 'String') #set($condition='StringUtils.isNotBlank(bo.get'+$AttrName+'())') +#if($queryType == 'LIKE') + .likeIfText(${ClassName}::get$AttrName, bo.get$AttrName()) +#elseif($queryType == 'EQ') + .eqIfText(${ClassName}::get$AttrName, bo.get$AttrName()) +#elseif($queryType == 'NE') + .neIfText(${ClassName}::get$AttrName, bo.get$AttrName()) +#else + .$mpMethod($condition, ${ClassName}::get$AttrName, bo.get$AttrName()) +#end #else #set($condition='bo.get'+$AttrName+'() != null') -#end - lqw.$mpMethod($condition, ${ClassName}::get$AttrName, bo.get$AttrName()); +#if($queryType == 'EQ') + .eqIfPresent(${ClassName}::get$AttrName, bo.get$AttrName()) +#elseif($queryType == 'NE') + .neIfPresent(${ClassName}::get$AttrName, bo.get$AttrName()) +#elseif($queryType == 'GT') + .gtIfPresent(${ClassName}::get$AttrName, bo.get$AttrName()) +#elseif($queryType == 'LT') + .ltIfPresent(${ClassName}::get$AttrName, bo.get$AttrName()) #else - lqw.between(params.get("begin$AttrName") != null && params.get("end$AttrName") != null, - ${ClassName}::get$AttrName, params.get("begin$AttrName"), params.get("end$AttrName")); + .$mpMethod($condition, ${ClassName}::get$AttrName, bo.get$AttrName()) +#end +#end +#else + .betweenParams(${ClassName}::get$AttrName, params, "begin$AttrName", "end$AttrName") #end #end #end #if($table.tree && "" != $treeAncestorsField) - lqw.orderByAsc(${ClassName}::get${TreeAncestorsCap}); + .orderByAsc(${ClassName}::get${TreeAncestorsCap}) #end #if($table.tree && "" != $treeParentCode) - lqw.orderByAsc(${ClassName}::get${TreeParentCap}); + .orderByAsc(${ClassName}::get${TreeParentCap}) #end #if($table.tree && "" != $treeOrderField) - lqw.orderByAsc(${ClassName}::get${TreeOrderCap}); + .orderByAsc(${ClassName}::get${TreeOrderCap}) #elseif($enableSort) - lqw.orderByAsc(${ClassName}::get${sortColumn.capJavaField}); + .orderByAsc(${ClassName}::get${sortColumn.capJavaField}) #end - lqw.orderByAsc(${ClassName}::get${pkColumn.capJavaField}); - return lqw; + .orderByAsc(${ClassName}::get${pkColumn.capJavaField}) + .build(); } /** @@ -215,9 +233,10 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { */ @Override public Boolean updateStatus(${pkColumn.javaType} ${pkColumn.javaField}, ${statusColumn.javaType} status) { - return baseMapper.update(null, new LambdaUpdateWrapper<${ClassName}>() + return baseMapper.lambda() .set(${ClassName}::get${statusColumn.capJavaField}, status) - .eq(${ClassName}::get${pkColumn.capJavaField}, ${pkColumn.javaField})) > 0; + .eq(${ClassName}::get${pkColumn.capJavaField}, ${pkColumn.javaField}) + .update(); } #end @@ -231,9 +250,10 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { */ @Override public Boolean updateSort(${pkColumn.javaType} ${pkColumn.javaField}, ${sortColumn.javaType} sortValue) { - return baseMapper.update(null, new LambdaUpdateWrapper<${ClassName}>() + return baseMapper.lambda() .set(${ClassName}::get${sortColumn.capJavaField}, sortValue) - .eq(${ClassName}::get${pkColumn.capJavaField}, ${pkColumn.javaField})) > 0; + .eq(${ClassName}::get${pkColumn.capJavaField}, ${pkColumn.javaField}) + .update(); } #end @@ -296,12 +316,14 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { } private void updateChildrenAncestors(${pkColumn.javaType} currentId, String newAncestors, String oldAncestors) { - List<${ClassName}> children = baseMapper.selectList(new LambdaQueryWrapper<${ClassName}>() - .select(${ClassName}::get${pkColumn.capJavaField}, ${ClassName}::get${TreeAncestorsCap}())); + List<${ClassName}> children = baseMapper.lambda() + .select(${ClassName}::get${pkColumn.capJavaField}, ${ClassName}::get${TreeAncestorsCap}) + .findInSet(currentId, ${ClassName}::get${TreeAncestorsCap}) + .list(); List<${ClassName}> updateList = new ArrayList<>(); for (${ClassName} child : children) { String ancestors = child.get${TreeAncestorsCap}(); - if (StringUtils.isBlank(ancestors) || !containsAncestor(ancestors, currentId)) { + if (StringUtils.isBlank(ancestors)) { continue; } ${ClassName} update = new ${ClassName}(); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java index 5777e6a90..74bbdfe47 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java @@ -1,7 +1,6 @@ package org.dromara.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.base.MPJBaseMapper; import com.github.yulichang.toolkit.JoinWrappers; @@ -9,7 +8,6 @@ import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; -import org.dromara.common.mybatis.helper.DataBaseHelper; import org.dromara.system.domain.SysDept; import org.dromara.system.domain.SysRole; import org.dromara.system.domain.SysRoleDept; @@ -66,7 +64,7 @@ public interface SysDeptMapper extends BaseMapperPlus, MPJBa @DataColumn(key = "deptName", value = "dept_id") }) default long countDeptById(Long deptId) { - return this.selectCount(new LambdaQueryWrapper().eq(SysDept::getDeptId, deptId)); + return this.lambda().eq(SysDept::getDeptId, deptId).count(); } /** @@ -76,9 +74,10 @@ public interface SysDeptMapper extends BaseMapperPlus, MPJBa * @return 包含子部门的列表 */ default List selectListByParentId(Long parentId) { - return this.selectList(new LambdaQueryWrapper() + return this.lambda() .select(SysDept::getDeptId) - .apply(DataBaseHelper.findInSet(parentId, "ancestors"))); + .findInSet(parentId, SysDept::getAncestors) + .list(); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictDataMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictDataMapper.java index 7298db3d0..8d5eba362 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictDataMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDictDataMapper.java @@ -1,6 +1,5 @@ package org.dromara.system.mapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysDictData; import org.dromara.system.domain.vo.SysDictDataVo; @@ -21,9 +20,9 @@ public interface SysDictDataMapper extends BaseMapperPlus selectDictDataByType(String dictType) { - return selectVoList( - new LambdaQueryWrapper() - .eq(SysDictData::getDictType, dictType) - .orderByAsc(SysDictData::getDictSort)); + return this.lambda() + .eq(SysDictData::getDictType, dictType) + .orderByAsc(SysDictData::getDictSort) + .voList(); } } 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 a51b1352a..d0ccca37c 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 @@ -1,7 +1,6 @@ package org.dromara.system.mapper; import cn.hutool.core.collection.CollUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.github.yulichang.base.MPJBaseMapper; import com.github.yulichang.toolkit.JoinWrappers; import org.dromara.common.core.constant.SystemConstants; @@ -97,12 +96,12 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa * @return 菜单列表 */ default List selectMenuTreeAll() { - LambdaQueryWrapper lqw = new LambdaQueryWrapper() + return this.lambda() .in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU) .eq(SysMenu::getStatus, SystemConstants.NORMAL) .orderByAsc(SysMenu::getParentId) - .orderByAsc(SysMenu::getOrderNum); - return this.selectList(lqw); + .orderByAsc(SysMenu::getOrderNum) + .list(); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java index 8e740e0a4..ace0a4829 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java @@ -1,7 +1,6 @@ package org.dromara.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.base.MPJBaseMapper; import com.github.yulichang.toolkit.JoinWrappers; @@ -62,7 +61,7 @@ public interface SysPostMapper extends BaseMapperPlus, MPJBa @DataColumn(key = "userName", value = "create_by") }) default long selectPostCount(Collection postIds) { - return this.selectCount(new LambdaQueryWrapper().in(SysPost::getPostId, postIds)); + return this.lambda().in(SysPost::getPostId, postIds).count(); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java index 96d127a7a..b2242832e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java @@ -1,7 +1,6 @@ package org.dromara.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.base.MPJBaseMapper; @@ -64,7 +63,7 @@ public interface SysRoleMapper extends BaseMapperPlus, MPJBa @DataColumn(key = "userName", value = "create_by") }) default long selectRoleCount(Collection roleIds) { - return this.selectCount(new LambdaQueryWrapper().in(SysRole::getRoleId, roleIds)); + return this.lambda().in(SysRole::getRoleId, roleIds).count(); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java index 6b552965a..e5eb4b883 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMenuMapper.java @@ -1,6 +1,5 @@ package org.dromara.system.mapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysRoleMenu; @@ -20,7 +19,7 @@ public interface SysRoleMenuMapper extends BaseMapperPlus menuIds) { - return this.delete(new LambdaUpdateWrapper().in(SysRoleMenu::getMenuId, menuIds)); + return this.lambda().in(SysRoleMenu::getMenuId, menuIds).deleteCount(); } } 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 5cd3b7b22..b7aedc1bf 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 @@ -1,7 +1,6 @@ package org.dromara.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.base.MPJBaseMapper; @@ -136,7 +135,7 @@ public interface SysUserMapper extends BaseMapperPlus, MPJBa @DataColumn(key = "userName", value = "create_by") }) default long countUserById(Long userId) { - return this.selectCount(new LambdaQueryWrapper().eq(SysUser::getUserId, userId)); + return lambda().eq(SysUser::getUserId, userId).count(); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserRoleMapper.java index 4c1c8b9df..f5d048af2 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserRoleMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysUserRoleMapper.java @@ -1,6 +1,6 @@ package org.dromara.system.mapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import cn.hutool.core.convert.Convert; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.system.domain.SysUserRole; @@ -20,9 +20,10 @@ public interface SysUserRoleMapper extends BaseMapperPlus selectUserIdsByRoleId(Long roleId) { - return this.selectObjs(new LambdaQueryWrapper() - .select(SysUserRole::getUserId).eq(SysUserRole::getRoleId, roleId) - ); + return this.lambda() + .select(SysUserRole::getUserId) + .eq(SysUserRole::getRoleId, roleId) + .objs(Convert::toLong); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java index d03d9bd48..adbf10ac9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java @@ -4,8 +4,6 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import cn.hutool.crypto.SecureUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -14,6 +12,7 @@ import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.system.domain.SysClient; import org.dromara.system.domain.bo.SysClientBo; import org.dromara.system.domain.vo.SysClientVo; @@ -64,7 +63,7 @@ public class SysClientServiceImpl implements ISysClientService { @Cacheable(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") @Override public SysClientVo queryByClientId(String clientId) { - SysClientVo vo = baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysClient::getClientId, clientId)); + SysClientVo vo = baseMapper.lambda().eq(SysClient::getClientId, clientId).voOne(); fillClientRuleFields(vo); return vo; } @@ -105,13 +104,13 @@ public class SysClientServiceImpl implements ISysClientService { * @return 包含 clientId、clientKey、状态等条件的查询包装器 */ private LambdaQueryWrapper buildQueryWrapper(SysClientBo bo) { - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.eq(StringUtils.isNotBlank(bo.getClientId()), SysClient::getClientId, bo.getClientId()); - lqw.eq(StringUtils.isNotBlank(bo.getClientKey()), SysClient::getClientKey, bo.getClientKey()); - lqw.eq(StringUtils.isNotBlank(bo.getClientSecret()), SysClient::getClientSecret, bo.getClientSecret()); - lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysClient::getStatus, bo.getStatus()); - lqw.orderByAsc(SysClient::getId); - return lqw; + return QueryBuilder.lambda(SysClient.class) + .eqIfText(SysClient::getClientId, bo.getClientId()) + .eqIfText(SysClient::getClientKey, bo.getClientKey()) + .eqIfText(SysClient::getClientSecret, bo.getClientSecret()) + .eqIfText(SysClient::getStatus, bo.getStatus()) + .orderByAsc(SysClient::getId) + .build(); } /** @@ -163,10 +162,10 @@ public class SysClientServiceImpl implements ISysClientService { @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") @Override public int updateClientStatus(String clientId, String status) { - return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysClient::getStatus, status) - .eq(SysClient::getClientId, clientId)); + return baseMapper.lambda() + .set(SysClient::getStatus, status) + .eq(SysClient::getClientId, clientId) + .updateCount(); } /** @@ -190,9 +189,10 @@ public class SysClientServiceImpl implements ISysClientService { */ @Override public boolean checkClickKeyUnique(SysClientBo client) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysClient::getClientKey, client.getClientKey()) - .ne(ObjectUtil.isNotNull(client.getId()), SysClient::getId, client.getId())); + .neIfPresent(SysClient::getId, client.getId()) + .exists(); return !exist; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java index f6f93c2e6..049c1d160 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java @@ -4,7 +4,6 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; @@ -17,6 +16,7 @@ import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.redis.utils.CacheUtils; import org.dromara.system.api.ConfigService; import org.dromara.system.domain.SysConfig; @@ -77,8 +77,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { @Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = "#configKey") @Override public String selectConfigByKey(String configKey) { - SysConfig retConfig = baseMapper.selectOne(new LambdaQueryWrapper() - .eq(SysConfig::getConfigKey, configKey)); + SysConfig retConfig = baseMapper.lambda().eq(SysConfig::getConfigKey, configKey).one(); return ObjectUtils.notNullGetter(retConfig, SysConfig::getConfigValue, StringUtils.EMPTY); } @@ -112,14 +111,13 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { */ private LambdaQueryWrapper buildQueryWrapper(SysConfigBo bo) { Map params = bo.getParams(); - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.like(StringUtils.isNotBlank(bo.getConfigName()), SysConfig::getConfigName, bo.getConfigName()); - lqw.eq(StringUtils.isNotBlank(bo.getConfigType()), SysConfig::getConfigType, bo.getConfigType()); - lqw.like(StringUtils.isNotBlank(bo.getConfigKey()), SysConfig::getConfigKey, bo.getConfigKey()); - lqw.between(params.get("beginTime") != null && params.get("endTime") != null, - SysConfig::getCreateTime, params.get("beginTime"), params.get("endTime")); - lqw.orderByAsc(SysConfig::getConfigId); - return lqw; + return QueryBuilder.lambda(SysConfig.class) + .likeIfText(SysConfig::getConfigName, bo.getConfigName()) + .eqIfText(SysConfig::getConfigType, bo.getConfigType()) + .likeIfText(SysConfig::getConfigKey, bo.getConfigKey()) + .betweenParams(SysConfig::getCreateTime, params, "beginTime", "endTime") + .orderByAsc(SysConfig::getConfigId) + .build(); } /** @@ -158,8 +156,9 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { row = baseMapper.updateById(config); } else { CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); - row = baseMapper.update(config, new LambdaQueryWrapper() - .eq(SysConfig::getConfigKey, config.getConfigKey())); + row = baseMapper.lambda() + .eq(SysConfig::getConfigKey, config.getConfigKey()) + .updateCount(config); } if (row > 0) { return config.getConfigValue(); @@ -200,9 +199,10 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { */ @Override public boolean checkConfigKeyUnique(SysConfigBo config) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysConfig::getConfigKey, config.getConfigKey()) - .ne(ObjectUtil.isNotNull(config.getConfigId()), SysConfig::getConfigId, config.getConfigId())); + .neIfPresent(SysConfig::getConfigId, config.getConfigId()) + .exists(); return !exist; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java index 8f6cecc98..e94c5780c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDataScopeServiceImpl.java @@ -3,7 +3,6 @@ package org.dromara.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.utils.StreamUtils; @@ -44,10 +43,10 @@ public class SysDataScopeServiceImpl implements ISysDataScopeService { if (ObjectUtil.isNull(roleId)) { return "-1"; } - List list = roleDeptMapper.selectList( - new LambdaQueryWrapper() - .select(SysRoleDept::getDeptId) - .eq(SysRoleDept::getRoleId, roleId)); + List list = roleDeptMapper.lambda() + .select(SysRoleDept::getDeptId) + .eq(SysRoleDept::getRoleId, roleId) + .list(); if (CollUtil.isNotEmpty(list)) { return StreamUtils.join(list, rd -> Convert.toStr(rd.getDeptId())); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java index d42bfb84f..e5fbd6865 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java @@ -6,8 +6,6 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; @@ -16,7 +14,8 @@ import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.*; import org.dromara.common.mybatis.core.page.PageQuery; -import org.dromara.common.mybatis.helper.DataBaseHelper; +import org.dromara.common.mybatis.core.query.LambdaQueryBuilder; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.redis.utils.CacheUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.api.DeptService; @@ -97,27 +96,23 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ private LambdaQueryWrapper buildQueryWrapper(SysDeptBo bo) { Map params = bo.getParams(); - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.eq(SysDept::getDelFlag, SystemConstants.NORMAL); - lqw.eq(ObjectUtil.isNotNull(bo.getDeptId()), SysDept::getDeptId, bo.getDeptId()); - lqw.eq(ObjectUtil.isNotNull(bo.getParentId()), SysDept::getParentId, bo.getParentId()); - lqw.like(StringUtils.isNotBlank(bo.getDeptName()), SysDept::getDeptName, bo.getDeptName()); - lqw.like(StringUtils.isNotBlank(bo.getDeptCategory()), SysDept::getDeptCategory, bo.getDeptCategory()); - lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysDept::getStatus, bo.getStatus()); - lqw.between(params.get("beginTime") != null && params.get("endTime") != null, - SysDept::getCreateTime, params.get("beginTime"), params.get("endTime")); - lqw.orderByAsc(SysDept::getAncestors); - lqw.orderByAsc(SysDept::getParentId); - lqw.orderByAsc(SysDept::getOrderNum); - lqw.orderByAsc(SysDept::getDeptId); + LambdaQueryBuilder builder = QueryBuilder.lambda(SysDept.class) + .eq(SysDept::getDelFlag, SystemConstants.NORMAL) + .eqIfPresent(SysDept::getDeptId, bo.getDeptId()) + .eqIfPresent(SysDept::getParentId, bo.getParentId()) + .likeIfText(SysDept::getDeptName, bo.getDeptName()) + .likeIfText(SysDept::getDeptCategory, bo.getDeptCategory()) + .eqIfText(SysDept::getStatus, bo.getStatus()) + .betweenParams(SysDept::getCreateTime, params, "beginTime", "endTime") + .orderByAsc(SysDept::getAncestors, SysDept::getParentId, SysDept::getOrderNum, SysDept::getDeptId); if (ObjectUtil.isNotNull(bo.getBelongDeptId())) { //部门树搜索 - lqw.and(x -> { + builder.and(x -> { List deptIds = baseMapper.selectDeptAndChildById(bo.getBelongDeptId()); x.in(SysDept::getDeptId, deptIds); }); } - return lqw; + return builder.build(); } /** @@ -169,8 +164,10 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { if (ObjectUtil.isNull(dept)) { return null; } - SysDeptVo parentDept = baseMapper.selectVoOne(new LambdaQueryWrapper() - .select(SysDept::getDeptName).eq(SysDept::getDeptId, dept.getParentId())); + SysDeptVo parentDept = baseMapper.lambda() + .select(SysDept::getDeptName) + .eq(SysDept::getDeptId, dept.getParentId()) + .voOne(); dept.setParentName(ObjectUtils.notNullGetter(parentDept, SysDeptVo::getDeptName)); return dept; } @@ -183,10 +180,11 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public List selectDeptByIds(Collection deptIds) { - return baseMapper.selectDeptList(new LambdaQueryWrapper() + return baseMapper.selectDeptList(baseMapper.lambda() .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getLeader) .eq(SysDept::getStatus, SystemConstants.NORMAL) - .in(CollUtil.isNotEmpty(deptIds), SysDept::getDeptId, deptIds)); + .in(CollUtil.isNotEmpty(deptIds), SysDept::getDeptId, deptIds) + .build()); } /** @@ -226,9 +224,10 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public List selectDeptsByList() { - List list = baseMapper.selectDeptList(new LambdaQueryWrapper() + List list = baseMapper.selectDeptList(baseMapper.lambda() .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getParentId) - .eq(SysDept::getStatus, SystemConstants.NORMAL)); + .eq(SysDept::getStatus, SystemConstants.NORMAL) + .build()); return BeanUtil.copyToList(list, DeptDTO.class); } @@ -240,9 +239,10 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public long selectNormalChildrenDeptById(Long deptId) { - return baseMapper.selectCount(new LambdaQueryWrapper() + return baseMapper.lambda() .eq(SysDept::getStatus, SystemConstants.NORMAL) - .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); + .findInSet(deptId, SysDept::getAncestors) + .count(); } /** @@ -253,8 +253,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public boolean hasChildByDeptId(Long deptId) { - return baseMapper.exists(new LambdaQueryWrapper() - .eq(SysDept::getParentId, deptId)); + return baseMapper.lambda().eq(SysDept::getParentId, deptId).exists(); } /** @@ -265,8 +264,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public boolean checkDeptExistUser(Long deptId) { - return userMapper.exists(new LambdaQueryWrapper() - .eq(SysUser::getDeptId, deptId)); + return userMapper.lambda().eq(SysUser::getDeptId, deptId).exists(); } /** @@ -277,10 +275,11 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public boolean checkDeptNameUnique(SysDeptBo dept) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysDept::getDeptName, dept.getDeptName()) .eq(SysDept::getParentId, dept.getParentId()) - .ne(ObjectUtil.isNotNull(dept.getDeptId()), SysDept::getDeptId, dept.getDeptId())); + .neIfPresent(SysDept::getDeptId, dept.getDeptId()) + .exists(); return !exist; } @@ -374,9 +373,10 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { private void updateParentDeptStatusNormal(SysDept dept) { String ancestors = dept.getAncestors(); Long[] deptIds = Convert.toLongArray(ancestors); - baseMapper.update(null, new LambdaUpdateWrapper() + baseMapper.lambda() .set(SysDept::getStatus, SystemConstants.NORMAL) - .in(SysDept::getDeptId, Arrays.asList(deptIds))); + .in(SysDept::getDeptId, Arrays.asList(deptIds)) + .update(); } /** @@ -387,8 +387,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { * @param oldAncestors 旧的父ID集合 */ private void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) { - List children = baseMapper.selectList(new LambdaQueryWrapper() - .apply(DataBaseHelper.findInSet(deptId, "ancestors"))); + List children = baseMapper.lambda().findInSet(deptId, SysDept::getAncestors).list(); List list = new ArrayList<>(); for (SysDept child : children) { SysDept dept = new SysDept(); @@ -430,11 +429,10 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { if (CollUtil.isEmpty(deptIds)) { return Collections.emptyMap(); } - List list = baseMapper.selectList( - new LambdaQueryWrapper() - .select(SysDept::getDeptId, SysDept::getDeptName) - .in(SysDept::getDeptId, deptIds) - ); + List list = baseMapper.lambda() + .select(SysDept::getDeptId, SysDept::getDeptName) + .in(SysDept::getDeptId, deptIds) + .list(); return StreamUtils.toMap(list, SysDept::getDeptId, SysDept::getDeptName); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java index 106e2e7e7..22af83a3f 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java @@ -1,16 +1,14 @@ package org.dromara.system.service.impl; -import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; -import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.redis.utils.CacheUtils; import org.dromara.system.domain.SysDictData; import org.dromara.system.domain.bo.SysDictDataBo; @@ -67,12 +65,12 @@ public class SysDictDataServiceImpl implements ISysDictDataService { * @return 包含排序号、标签和字典类型条件的查询包装器 */ private LambdaQueryWrapper buildQueryWrapper(SysDictDataBo bo) { - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.eq(bo.getDictSort() != null, SysDictData::getDictSort, bo.getDictSort()); - lqw.like(StringUtils.isNotBlank(bo.getDictLabel()), SysDictData::getDictLabel, bo.getDictLabel()); - lqw.eq(StringUtils.isNotBlank(bo.getDictType()), SysDictData::getDictType, bo.getDictType()); - lqw.orderByAsc(SysDictData::getDictSort, SysDictData::getDictCode); - return lqw; + return QueryBuilder.lambda(SysDictData.class) + .eqIfPresent(SysDictData::getDictSort, bo.getDictSort()) + .likeIfText(SysDictData::getDictLabel, bo.getDictLabel()) + .eqIfText(SysDictData::getDictType, bo.getDictType()) + .orderByAsc(SysDictData::getDictSort, SysDictData::getDictCode) + .build(); } /** @@ -84,10 +82,11 @@ public class SysDictDataServiceImpl implements ISysDictDataService { */ @Override public String selectDictLabel(String dictType, String dictValue) { - return baseMapper.selectOne(new LambdaQueryWrapper() - .select(SysDictData::getDictLabel) - .eq(SysDictData::getDictType, dictType) - .eq(SysDictData::getDictValue, dictValue)) + return baseMapper.lambda() + .select(SysDictData::getDictLabel) + .eq(SysDictData::getDictType, dictType) + .eq(SysDictData::getDictValue, dictValue) + .one() .getDictLabel(); } @@ -156,10 +155,11 @@ public class SysDictDataServiceImpl implements ISysDictDataService { */ @Override public boolean checkDictDataUnique(SysDictDataBo dict) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysDictData::getDictType, dict.getDictType()) .eq(SysDictData::getDictValue, dict.getDictValue()) - .ne(ObjectUtil.isNotNull(dict.getDictCode()), SysDictData::getDictCode, dict.getDictCode())); + .neIfPresent(SysDictData::getDictCode, dict.getDictCode()) + .exists(); return !exist; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java index 89d6bcb70..d636fec91 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java @@ -4,8 +4,6 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; @@ -19,6 +17,7 @@ import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.redis.utils.CacheUtils; import org.dromara.system.domain.SysDictData; import org.dromara.system.domain.SysDictType; @@ -81,11 +80,11 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService * @return 包含名称、类型与创建时间区间的查询包装器 */ private LambdaQueryWrapper buildQueryWrapper(SysDictTypeBo bo) { - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.like(StringUtils.isNotBlank(bo.getDictName()), SysDictType::getDictName, bo.getDictName()); - lqw.like(StringUtils.isNotBlank(bo.getDictType()), SysDictType::getDictType, bo.getDictType()); - lqw.orderByAsc(SysDictType::getDictId); - return lqw; + return QueryBuilder.lambda(SysDictType.class) + .likeIfText(SysDictType::getDictName, bo.getDictName()) + .likeIfText(SysDictType::getDictType, bo.getDictType()) + .orderByAsc(SysDictType::getDictId) + .build(); } /** @@ -131,7 +130,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService @Cacheable(cacheNames = CacheNames.SYS_DICT_TYPE, key = "#dictType") @Override public SysDictTypeVo selectDictTypeByType(String dictType) { - return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysDictType::getDictType, dictType)); + return baseMapper.lambda().eq(SysDictType::getDictType, dictType).voOne(); } /** @@ -143,8 +142,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService public void deleteDictTypeByIds(Collection dictIds) { List list = baseMapper.selectByIds(dictIds); list.forEach(x -> { - boolean assigned = dictDataMapper.exists(new LambdaQueryWrapper() - .eq(SysDictData::getDictType, x.getDictType())); + boolean assigned = dictDataMapper.lambda().eq(SysDictData::getDictType, x.getDictType()).exists(); if (assigned) { throw new ServiceException("{}已分配,不能删除", x.getDictName()); } @@ -195,9 +193,10 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService public List updateDictType(SysDictTypeBo bo) { SysDictType dict = MapstructUtils.convert(bo, SysDictType.class); SysDictType oldDict = baseMapper.selectById(dict.getDictId()); - dictDataMapper.update(null, new LambdaUpdateWrapper() + dictDataMapper.lambda() .set(SysDictData::getDictType, dict.getDictType()) - .eq(SysDictData::getDictType, oldDict.getDictType())); + .eq(SysDictData::getDictType, oldDict.getDictType()) + .update(); int row = baseMapper.updateById(dict); if (row > 0) { CacheUtils.evict(CacheNames.SYS_DICT, oldDict.getDictType()); @@ -215,9 +214,10 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService */ @Override public boolean checkDictTypeUnique(SysDictTypeBo dictType) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysDictType::getDictType, dictType.getDictType()) - .ne(ObjectUtil.isNotNull(dictType.getDictId()), SysDictType::getDictId, dictType.getDictId())); + .neIfPresent(SysDictType::getDictId, dictType.getDictId()) + .exists(); return !exist; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLoginInfoServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLoginInfoServiceImpl.java index 6b445ca6a..7eae72fef 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLoginInfoServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLoginInfoServiceImpl.java @@ -3,7 +3,6 @@ package org.dromara.system.service.impl; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.useragent.UserAgent; import cn.hutool.http.useragent.UserAgentUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; @@ -16,6 +15,7 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.ip.AddressUtils; import org.dromara.common.log.event.LoginInfoEvent; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.mapper.LambdaCrudChainWrapper; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.domain.SysLoginInfo; import org.dromara.system.domain.bo.SysLoginInfoBo; @@ -122,13 +122,7 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService { */ @Override public PageResult selectPageLoginInfoList(SysLoginInfoBo loginInfo, PageQuery pageQuery) { - Map params = loginInfo.getParams(); - LambdaQueryWrapper lqw = new LambdaQueryWrapper() - .like(StringUtils.isNotBlank(loginInfo.getIpaddr()), SysLoginInfo::getIpaddr, loginInfo.getIpaddr()) - .eq(StringUtils.isNotBlank(loginInfo.getStatus()), SysLoginInfo::getStatus, loginInfo.getStatus()) - .like(StringUtils.isNotBlank(loginInfo.getUserName()), SysLoginInfo::getUserName, loginInfo.getUserName()) - .between(params.get("beginTime") != null && params.get("endTime") != null, - SysLoginInfo::getLoginTime, params.get("beginTime"), params.get("endTime")); + LambdaCrudChainWrapper lqw = buildQueryWrapper(loginInfo); if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { lqw.orderByDesc(SysLoginInfo::getInfoId); } @@ -156,16 +150,25 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService { */ @Override public List selectLoginInfoList(SysLoginInfoBo loginInfo) { - Map params = loginInfo.getParams(); - return baseMapper.selectVoList(new LambdaQueryWrapper() - .like(StringUtils.isNotBlank(loginInfo.getIpaddr()), SysLoginInfo::getIpaddr, loginInfo.getIpaddr()) - .eq(StringUtils.isNotBlank(loginInfo.getStatus()), SysLoginInfo::getStatus, loginInfo.getStatus()) - .like(StringUtils.isNotBlank(loginInfo.getUserName()), SysLoginInfo::getUserName, loginInfo.getUserName()) - .between(params.get("beginTime") != null && params.get("endTime") != null, - SysLoginInfo::getLoginTime, params.get("beginTime"), params.get("endTime")) + return baseMapper.selectVoList(buildQueryWrapper(loginInfo) .orderByDesc(SysLoginInfo::getInfoId)); } + /** + * 构造登录日志列表查询条件。 + * + * @param loginInfo 登录日志筛选条件 + * @return 登录日志查询包装器 + */ + private LambdaCrudChainWrapper buildQueryWrapper(SysLoginInfoBo loginInfo) { + Map params = loginInfo.getParams(); + return baseMapper.lambda() + .likeIfText(SysLoginInfo::getIpaddr, loginInfo.getIpaddr()) + .eqIfText(SysLoginInfo::getStatus, loginInfo.getStatus()) + .likeIfText(SysLoginInfo::getUserName, loginInfo.getUserName()) + .betweenParams(SysLoginInfo::getLoginTime, params, "beginTime", "endTime"); + } + /** * 批量删除系统登录日志 * @@ -182,6 +185,6 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService { */ @Override public void cleanLoginInfo() { - baseMapper.delete(new LambdaQueryWrapper<>()); + baseMapper.lambda().delete(); } } 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 44b19e7ef..ca5483110 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 baseMapper.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 baseMapper.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 baseMapper.selectMenuListByUserId(menu, userId); } @@ -246,7 +245,7 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public boolean hasChildByMenuId(Long menuId) { - return baseMapper.exists(new LambdaQueryWrapper().eq(SysMenu::getParentId, menuId)); + return baseMapper.lambda().eq(SysMenu::getParentId, menuId).exists(); } /** @@ -257,7 +256,10 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public boolean hasChildByMenuId(Collection menuIds) { - return baseMapper.exists(new LambdaQueryWrapper().in(SysMenu::getParentId, menuIds).notIn(SysMenu::getMenuId, menuIds)); + return baseMapper.lambda() + .in(SysMenu::getParentId, menuIds) + .notIn(SysMenu::getMenuId, menuIds) + .exists(); } /** @@ -268,7 +270,7 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public boolean checkMenuExistRole(Long menuId) { - return roleMenuMapper.exists(new LambdaQueryWrapper().eq(SysRoleMenu::getMenuId, menuId)); + return roleMenuMapper.lambda().eq(SysRoleMenu::getMenuId, menuId).exists(); } /** @@ -326,10 +328,11 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public boolean checkMenuNameUnique(SysMenuBo menu) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.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; } @@ -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 = baseMapper.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 = baseMapper.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-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java index 7d2ddfac9..4fab4dafd 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java @@ -2,8 +2,6 @@ package org.dromara.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.dromara.common.core.enums.PushSourceEnum; @@ -11,7 +9,6 @@ import org.dromara.common.core.enums.PushTypeEnum; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; -import org.dromara.common.mybatis.helper.DataBaseHelper; import org.dromara.common.mybatis.utils.IdGeneratorUtil; import org.dromara.common.push.helper.PushHelper; import org.dromara.system.api.MessageService; @@ -207,20 +204,19 @@ public class SysMessageServiceImpl implements ISysMessageService, MessageService * @return 消息VO列表 */ private List selectMessageList(String category, Long userId) { - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - // 分类匹配 - lqw.eq(SysMessage::getCategory, category); - // 仅查询30天内消息 - lqw.ge(SysMessage::getCreateTime, LocalDateTime.now().minusDays(BOX_DAYS)); - // 全局消息 或 当前用户在接收人范围内 - lqw.and(wrapper -> wrapper.eq(SysMessage::getSendUserIds, GLOBAL_USER_IDS) - .or() - .apply(DataBaseHelper.findInSet(userId, "send_user_ids"))); - // 按创建时间+消息ID倒序 - lqw.orderByDesc(SysMessage::getCreateTime, SysMessage::getMessageId); - // 分页查询(只查第一页,最多100条) - List list = baseMapper.selectList(new Page<>(1, BOX_LIMIT, false), lqw); - // 转换为VO并返回 + List list = baseMapper.lambda() + .eq(SysMessage::getCategory, category) + // 仅查询30天内消息 + .ge(SysMessage::getCreateTime, LocalDateTime.now().minusDays(BOX_DAYS)) + // 全局消息 或 当前用户在接收人范围内 + .and(wrapper -> { + wrapper.eq(SysMessage::getSendUserIds, GLOBAL_USER_IDS) + .or() + .findInSet(userId, SysMessage::getSendUserIds); + }) + .orderByDesc(SysMessage::getCreateTime, SysMessage::getMessageId) + // 分页查询(只查第一页,最多100条) + .list(new Page<>(1, BOX_LIMIT, false)); return list.stream().map(this::buildVo).toList(); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java index f201e4fa9..977a5c0bd 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java @@ -1,7 +1,6 @@ package org.dromara.system.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.PageResult; @@ -9,6 +8,8 @@ import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.LambdaQueryBuilder; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.system.domain.SysNotice; import org.dromara.system.domain.SysUser; import org.dromara.system.domain.bo.SysNoticeBo; @@ -78,15 +79,14 @@ public class SysNoticeServiceImpl implements ISysNoticeService { * @return 包含标题、类型、创建人和排序条件的查询包装器 */ private LambdaQueryWrapper buildQueryWrapper(SysNoticeBo bo) { - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.like(StringUtils.isNotBlank(bo.getNoticeTitle()), SysNotice::getNoticeTitle, bo.getNoticeTitle()); - lqw.eq(StringUtils.isNotBlank(bo.getNoticeType()), SysNotice::getNoticeType, bo.getNoticeType()); + LambdaQueryBuilder builder = QueryBuilder.lambda(SysNotice.class) + .likeIfText(SysNotice::getNoticeTitle, bo.getNoticeTitle()) + .eqIfText(SysNotice::getNoticeType, bo.getNoticeType()); if (StringUtils.isNotBlank(bo.getCreateByName())) { - SysUserVo sysUser = userMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, bo.getCreateByName())); - lqw.eq(SysNotice::getCreateBy, ObjectUtils.notNullGetter(sysUser, SysUserVo::getUserId)); + SysUserVo sysUser = userMapper.lambda().eq(SysUser::getUserName, bo.getCreateByName()).voOne(); + builder.eq(SysNotice::getCreateBy, ObjectUtils.notNullGetter(sysUser, SysUserVo::getUserId)); } - lqw.orderByAsc(SysNotice::getNoticeId); - return lqw; + return builder.orderByAsc(SysNotice::getNoticeId).build(); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java index 84ce7eb38..6c8bd86c7 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java @@ -10,6 +10,7 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.ip.AddressUtils; import org.dromara.common.log.event.OperLogEvent; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.system.domain.SysOperLog; import org.dromara.system.domain.bo.SysOperLogBo; import org.dromara.system.domain.vo.SysOperLogVo; @@ -74,9 +75,9 @@ public class SysOperLogServiceImpl implements ISysOperLogService { */ private LambdaQueryWrapper buildQueryWrapper(SysOperLogBo operLog) { Map params = operLog.getParams(); - return new LambdaQueryWrapper() - .like(StringUtils.isNotBlank(operLog.getOperIp()), SysOperLog::getOperIp, operLog.getOperIp()) - .like(StringUtils.isNotBlank(operLog.getTitle()), SysOperLog::getTitle, operLog.getTitle()) + return QueryBuilder.lambda(SysOperLog.class) + .likeIfText(SysOperLog::getOperIp, operLog.getOperIp()) + .likeIfText(SysOperLog::getTitle, operLog.getTitle()) .eq(operLog.getBusinessType() != null && operLog.getBusinessType() > 0, SysOperLog::getBusinessType, operLog.getBusinessType()) .func(f -> { @@ -84,17 +85,16 @@ public class SysOperLogServiceImpl implements ISysOperLogService { f.in(SysOperLog::getBusinessType, Arrays.asList(operLog.getBusinessTypes())); } }) - .eq(operLog.getStatus() != null, - SysOperLog::getStatus, operLog.getStatus()) - .like(StringUtils.isNotBlank(operLog.getOperName()), SysOperLog::getOperName, operLog.getOperName()) - .eq(operLog.getUserId() != null, SysOperLog::getUserId, operLog.getUserId()) - .eq(operLog.getDeptId() != null, SysOperLog::getDeptId, operLog.getDeptId()) - .eq(StringUtils.isNotBlank(operLog.getClientKey()), SysOperLog::getClientKey, operLog.getClientKey()) - .eq(StringUtils.isNotBlank(operLog.getDeviceType()), SysOperLog::getDeviceType, operLog.getDeviceType()) - .like(StringUtils.isNotBlank(operLog.getBrowser()), SysOperLog::getBrowser, operLog.getBrowser()) - .like(StringUtils.isNotBlank(operLog.getOs()), SysOperLog::getOs, operLog.getOs()) - .between(params.get("beginTime") != null && params.get("endTime") != null, - SysOperLog::getOperTime, params.get("beginTime"), params.get("endTime")); + .eqIfPresent(SysOperLog::getStatus, operLog.getStatus()) + .likeIfText(SysOperLog::getOperName, operLog.getOperName()) + .eqIfPresent(SysOperLog::getUserId, operLog.getUserId()) + .eqIfPresent(SysOperLog::getDeptId, operLog.getDeptId()) + .eqIfText(SysOperLog::getClientKey, operLog.getClientKey()) + .eqIfText(SysOperLog::getDeviceType, operLog.getDeviceType()) + .likeIfText(SysOperLog::getBrowser, operLog.getBrowser()) + .likeIfText(SysOperLog::getOs, operLog.getOs()) + .betweenParams(SysOperLog::getOperTime, params, "beginTime", "endTime") + .build(); } /** @@ -148,6 +148,6 @@ public class SysOperLogServiceImpl implements ISysOperLogService { */ @Override public void cleanOperLog() { - baseMapper.delete(new LambdaQueryWrapper<>()); + baseMapper.lambda().delete(); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java index 21196e628..985301940 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java @@ -3,8 +3,6 @@ package org.dromara.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -17,6 +15,7 @@ import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.oss.constant.OssConstant; import org.dromara.common.redis.utils.CacheUtils; import org.dromara.common.redis.utils.RedisUtils; @@ -94,12 +93,12 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { * @return 包含配置标识、桶名称和状态条件的查询包装器 */ private LambdaQueryWrapper buildQueryWrapper(SysOssConfigBo bo) { - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.eq(StringUtils.isNotBlank(bo.getConfigKey()), SysOssConfig::getConfigKey, bo.getConfigKey()); - lqw.like(StringUtils.isNotBlank(bo.getBucketName()), SysOssConfig::getBucketName, bo.getBucketName()); - lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysOssConfig::getStatus, bo.getStatus()); - lqw.orderByAsc(SysOssConfig::getOssConfigId); - return lqw; + return QueryBuilder.lambda(SysOssConfig.class) + .eqIfText(SysOssConfig::getConfigKey, bo.getConfigKey()) + .likeIfText(SysOssConfig::getBucketName, bo.getBucketName()) + .eqIfText(SysOssConfig::getStatus, bo.getStatus()) + .orderByAsc(SysOssConfig::getOssConfigId) + .build(); } /** @@ -131,13 +130,13 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { public Boolean updateByBo(SysOssConfigBo bo) { SysOssConfig config = MapstructUtils.convert(bo, SysOssConfig.class); validEntityBeforeSave(config); - LambdaUpdateWrapper luw = new LambdaUpdateWrapper<>(); - luw.set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, ""); - luw.set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, ""); - luw.set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, ""); - luw.set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, ""); - luw.eq(SysOssConfig::getOssConfigId, config.getOssConfigId()); - boolean flag = baseMapper.update(config, luw) > 0; + boolean flag = baseMapper.lambda() + .set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, "") + .set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, "") + .set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, "") + .set(ObjectUtil.isNull(config.getRemark()), SysOssConfig::getRemark, "") + .eq(SysOssConfig::getOssConfigId, config.getOssConfigId()) + .update(config); if (flag) { // 从数据库查询完整的数据做缓存 config = baseMapper.selectById(config.getOssConfigId()); @@ -193,9 +192,10 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { */ private boolean checkConfigKeyUnique(SysOssConfig sysOssConfig) { long ossConfigId = ObjectUtils.notNull(sysOssConfig.getOssConfigId(), -1L); - SysOssConfig info = baseMapper.selectOne(new LambdaQueryWrapper() + SysOssConfig info = baseMapper.lambda() .select(SysOssConfig::getOssConfigId, SysOssConfig::getConfigKey) - .eq(SysOssConfig::getConfigKey, sysOssConfig.getConfigKey())); + .eq(SysOssConfig::getConfigKey, sysOssConfig.getConfigKey()) + .one(); if (ObjectUtil.isNotNull(info) && info.getOssConfigId() != ossConfigId) { return false; } @@ -212,8 +212,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { @Transactional(rollbackFor = Exception.class) public int updateOssConfigStatus(SysOssConfigBo bo) { SysOssConfig sysOssConfig = MapstructUtils.convert(bo, SysOssConfig.class); - int row = baseMapper.update(null, new LambdaUpdateWrapper() - .set(SysOssConfig::getStatus, SystemConstants.NO)); + int row = baseMapper.lambda().set(SysOssConfig::getStatus, SystemConstants.NO).updateCount(); row += baseMapper.updateById(sysOssConfig); if (row > 0) { RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, sysOssConfig.getConfigKey()); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java index 5606bfabb..7ad27d599 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java @@ -5,7 +5,6 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -19,6 +18,7 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.file.FileUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.oss.client.OssClient; import org.dromara.common.oss.enums.AccessPolicy; import org.dromara.common.oss.factory.OssFactory; @@ -157,17 +157,16 @@ public class SysOssServiceImpl implements ISysOssService, OssService { */ private LambdaQueryWrapper buildQueryWrapper(SysOssBo bo) { Map params = bo.getParams(); - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.like(StringUtils.isNotBlank(bo.getFileName()), SysOss::getFileName, bo.getFileName()); - lqw.like(StringUtils.isNotBlank(bo.getOriginalName()), SysOss::getOriginalName, bo.getOriginalName()); - lqw.eq(StringUtils.isNotBlank(bo.getFileSuffix()), SysOss::getFileSuffix, bo.getFileSuffix()); - lqw.eq(StringUtils.isNotBlank(bo.getUrl()), SysOss::getUrl, bo.getUrl()); - lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null, - SysOss::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime")); - lqw.eq(ObjectUtil.isNotNull(bo.getCreateBy()), SysOss::getCreateBy, bo.getCreateBy()); - lqw.eq(StringUtils.isNotBlank(bo.getService()), SysOss::getService, bo.getService()); - lqw.orderByAsc(SysOss::getOssId); - return lqw; + return QueryBuilder.lambda(SysOss.class) + .likeIfText(SysOss::getFileName, bo.getFileName()) + .likeIfText(SysOss::getOriginalName, bo.getOriginalName()) + .eqIfText(SysOss::getFileSuffix, bo.getFileSuffix()) + .eqIfText(SysOss::getUrl, bo.getUrl()) + .betweenParams(SysOss::getCreateTime, params, "beginCreateTime", "endCreateTime") + .eqIfPresent(SysOss::getCreateBy, bo.getCreateBy()) + .eqIfText(SysOss::getService, bo.getService()) + .orderByAsc(SysOss::getOssId) + .build(); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java index ce3d283a0..924fe2932 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java @@ -2,8 +2,7 @@ package org.dromara.system.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.SystemConstants; @@ -11,7 +10,6 @@ import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StreamUtils; -import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.system.api.PostService; import org.dromara.system.domain.SysPost; @@ -83,15 +81,14 @@ public class SysPostServiceImpl implements ISysPostService, PostService { * @param bo 查询条件对象 * @return 构建好的查询包装器 */ - private LambdaQueryWrapper buildQueryWrapper(SysPostBo bo) { + private Wrapper buildQueryWrapper(SysPostBo bo) { Map params = bo.getParams(); - LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); - wrapper.like(StringUtils.isNotBlank(bo.getPostCode()), SysPost::getPostCode, bo.getPostCode()) - .like(StringUtils.isNotBlank(bo.getPostCategory()), SysPost::getPostCategory, bo.getPostCategory()) - .like(StringUtils.isNotBlank(bo.getPostName()), SysPost::getPostName, bo.getPostName()) - .eq(StringUtils.isNotBlank(bo.getStatus()), SysPost::getStatus, bo.getStatus()) - .between(params.get("beginTime") != null && params.get("endTime") != null, - SysPost::getCreateTime, params.get("beginTime"), params.get("endTime")) + var wrapper = baseMapper.lambda() + .likeIfText(SysPost::getPostCode, bo.getPostCode()) + .likeIfText(SysPost::getPostCategory, bo.getPostCategory()) + .likeIfText(SysPost::getPostName, bo.getPostName()) + .eqIfText(SysPost::getStatus, bo.getStatus()) + .betweenParams(SysPost::getCreateTime, params, "beginTime", "endTime") .orderByAsc(SysPost::getPostSort); if (ObjectUtil.isNotNull(bo.getDeptId())) { //优先单部门搜索 @@ -113,7 +110,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public List selectPostAll() { - return baseMapper.selectVoList(new QueryWrapper<>()); + return baseMapper.lambda().voList(); } /** @@ -147,10 +144,11 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public List selectPostByIds(Collection postIds) { - return baseMapper.selectVoList(new LambdaQueryWrapper() + return baseMapper.lambda() .select(SysPost::getPostId, SysPost::getPostName, SysPost::getPostCode) .eq(SysPost::getStatus, SystemConstants.NORMAL) - .in(CollUtil.isNotEmpty(postIds), SysPost::getPostId, postIds)); + .in(CollUtil.isNotEmpty(postIds), SysPost::getPostId, postIds) + .voList(); } /** @@ -161,10 +159,11 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public boolean checkPostNameUnique(SysPostBo post) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysPost::getPostName, post.getPostName()) .eq(SysPost::getDeptId, post.getDeptId()) - .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId())); + .neIfPresent(SysPost::getPostId, post.getPostId()) + .exists(); return !exist; } @@ -176,9 +175,10 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public boolean checkPostCodeUnique(SysPostBo post) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysPost::getPostCode, post.getPostCode()) - .ne(ObjectUtil.isNotNull(post.getPostId()), SysPost::getPostId, post.getPostId())); + .neIfPresent(SysPost::getPostId, post.getPostId()) + .exists(); return !exist; } @@ -190,7 +190,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public long countUserPostById(Long postId) { - return userPostMapper.selectCount(new LambdaQueryWrapper().eq(SysUserPost::getPostId, postId)); + return userPostMapper.lambda().eq(SysUserPost::getPostId, postId).count(); } /** @@ -201,7 +201,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public long countPostByDeptId(Long deptId) { - return baseMapper.selectCount(new LambdaQueryWrapper().eq(SysPost::getDeptId, deptId)); + return baseMapper.lambda().eq(SysPost::getDeptId, deptId).count(); } /** @@ -267,11 +267,10 @@ public class SysPostServiceImpl implements ISysPostService, PostService { if (CollUtil.isEmpty(postIds)) { return Collections.emptyMap(); } - List list = baseMapper.selectList( - new LambdaQueryWrapper() - .select(SysPost::getPostId, SysPost::getPostName) - .in(SysPost::getPostId, postIds) - ); + List list = baseMapper.lambda() + .select(SysPost::getPostId, SysPost::getPostName) + .in(SysPost::getPostId, postIds) + .list(); return StreamUtils.toMap(list, SysPost::getPostId, SysPost::getPostName); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java index 0ed6aaca6..7f90187b5 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java @@ -6,9 +6,6 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; @@ -19,6 +16,7 @@ import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.api.RoleService; import org.dromara.system.api.model.LoginUser; @@ -85,15 +83,14 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ private Wrapper buildQueryWrapper(SysRoleBo bo) { Map params = bo.getParams(); - LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.eq(ObjectUtil.isNotNull(bo.getRoleId()), SysRole::getRoleId, bo.getRoleId()) - .like(StringUtils.isNotBlank(bo.getRoleName()), SysRole::getRoleName, bo.getRoleName()) - .eq(StringUtils.isNotBlank(bo.getStatus()), SysRole::getStatus, bo.getStatus()) - .like(StringUtils.isNotBlank(bo.getRoleKey()), SysRole::getRoleKey, bo.getRoleKey()) - .between(params.get("beginTime") != null && params.get("endTime") != null, - SysRole::getCreateTime, params.get("beginTime"), params.get("endTime")) - .orderByAsc(SysRole::getRoleSort).orderByAsc(SysRole::getCreateTime); - return wrapper; + return QueryBuilder.lambda(SysRole.class) + .eqIfPresent(SysRole::getRoleId, bo.getRoleId()) + .likeIfText(SysRole::getRoleName, bo.getRoleName()) + .eqIfText(SysRole::getStatus, bo.getStatus()) + .likeIfText(SysRole::getRoleKey, bo.getRoleKey()) + .betweenParams(SysRole::getCreateTime, params, "beginTime", "endTime") + .orderByAsc(SysRole::getRoleSort, SysRole::getCreateTime) + .build(); } /** @@ -186,9 +183,10 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public List selectRoleByIds(Collection roleIds) { - return baseMapper.selectRoleList(new LambdaQueryWrapper() + return baseMapper.selectRoleList(baseMapper.lambda() .eq(SysRole::getStatus, SystemConstants.NORMAL) - .in(CollUtil.isNotEmpty(roleIds), SysRole::getRoleId, roleIds)); + .inIfNotEmpty(SysRole::getRoleId, roleIds) + .build()); } /** @@ -199,9 +197,10 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public boolean checkRoleNameUnique(SysRoleBo role) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysRole::getRoleName, role.getRoleName()) - .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId())); + .neIfPresent(SysRole::getRoleId, role.getRoleId()) + .exists(); return !exist; } @@ -213,9 +212,10 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public boolean checkRoleKeyUnique(SysRoleBo role) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysRole::getRoleKey, role.getRoleKey()) - .ne(ObjectUtil.isNotNull(role.getRoleId()), SysRole::getRoleId, role.getRoleId())); + .neIfPresent(SysRole::getRoleId, role.getRoleId()) + .exists(); return !exist; } @@ -286,7 +286,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public long countUserRoleByRoleId(Long roleId) { - return userRoleMapper.selectCount(new LambdaQueryWrapper().eq(SysUserRole::getRoleId, roleId)); + return userRoleMapper.lambda().eq(SysUserRole::getRoleId, roleId).count(); } /** @@ -337,10 +337,10 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { // 更新权限相关配置字段(数据范围、树联动)。 baseMapper.updateById(role); // 先清理旧菜单权限,再重建。 - roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, role.getRoleId())); + roleMenuMapper.lambda().eq(SysRoleMenu::getRoleId, role.getRoleId()).delete(); insertRoleMenu(bo); // 先清理旧数据权限,再按当前配置重建。 - roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, role.getRoleId())); + roleDeptMapper.lambda().eq(SysRoleDept::getRoleId, role.getRoleId()).delete(); return insertRoleDept(bo); } @@ -356,10 +356,10 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { if (SystemConstants.DISABLE.equals(status) && this.countUserRoleByRoleId(roleId) > 0) { throw new ServiceException("角色已分配,不能禁用!"); } - return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysRole::getStatus, status) - .eq(SysRole::getRoleId, roleId)); + return baseMapper.lambda() + .set(SysRole::getStatus, status) + .eq(SysRole::getRoleId, roleId) + .updateCount(); } @@ -417,9 +417,9 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { @Transactional(rollbackFor = Exception.class) public int deleteRoleById(Long roleId) { // 删除角色与菜单关联 - roleMenuMapper.delete(new LambdaQueryWrapper().eq(SysRoleMenu::getRoleId, roleId)); + roleMenuMapper.lambda().eq(SysRoleMenu::getRoleId, roleId).delete(); // 删除角色与部门关联 - roleDeptMapper.delete(new LambdaQueryWrapper().eq(SysRoleDept::getRoleId, roleId)); + roleDeptMapper.lambda().eq(SysRoleDept::getRoleId, roleId).delete(); return baseMapper.deleteById(roleId); } @@ -442,9 +442,9 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { } } // 删除角色与菜单关联 - roleMenuMapper.delete(new LambdaQueryWrapper().in(SysRoleMenu::getRoleId, roleIds)); + roleMenuMapper.lambda().in(SysRoleMenu::getRoleId, roleIds).delete(); // 删除角色与部门关联 - roleDeptMapper.delete(new LambdaQueryWrapper().in(SysRoleDept::getRoleId, roleIds)); + roleDeptMapper.lambda().in(SysRoleDept::getRoleId, roleIds).delete(); return baseMapper.deleteByIds(roleIds); } @@ -459,9 +459,10 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { if (LoginHelper.getUserId().equals(userRole.getUserId())) { throw new ServiceException("不允许修改当前用户角色!"); } - int rows = userRoleMapper.delete(new LambdaQueryWrapper() + int rows = userRoleMapper.lambda() .eq(SysUserRole::getRoleId, userRole.getRoleId()) - .eq(SysUserRole::getUserId, userRole.getUserId())); + .eq(SysUserRole::getUserId, userRole.getUserId()) + .deleteCount(); if (rows > 0) { cleanOnlineUser(List.of(userRole.getUserId())); } @@ -480,9 +481,10 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { if (userIds.contains(LoginHelper.getUserId())) { throw new ServiceException("不允许修改当前用户角色!"); } - int rows = userRoleMapper.delete(new LambdaQueryWrapper() + int rows = userRoleMapper.lambda() .eq(SysUserRole::getRoleId, roleId) - .in(SysUserRole::getUserId, userIds)); + .in(SysUserRole::getUserId, userIds) + .deleteCount(); if (rows > 0) { cleanOnlineUser(userIds); } @@ -532,7 +534,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { @Override public void cleanOnlineUserByRole(Long roleId) { // 如果角色未绑定用户 直接返回 - Long num = userRoleMapper.selectCount(new LambdaQueryWrapper().eq(SysUserRole::getRoleId, roleId)); + Long num = userRoleMapper.lambda().eq(SysUserRole::getRoleId, roleId).count(); if (num == 0) { return; } @@ -607,11 +609,10 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { if (CollUtil.isEmpty(roleIds)) { return Collections.emptyMap(); } - List list = baseMapper.selectList( - new LambdaQueryWrapper() - .select(SysRole::getRoleId, SysRole::getRoleName) - .in(SysRole::getRoleId, roleIds) - ); + List list = baseMapper.lambda() + .select(SysRole::getRoleId, SysRole::getRoleName) + .in(SysRole::getRoleId, roleIds) + .list(); return StreamUtils.toMap(list, SysRole::getRoleId, SysRole::getRoleName); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java index d3441b67e..b58b1d187 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java @@ -1,10 +1,7 @@ package org.dromara.system.service.impl; -import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import lombok.RequiredArgsConstructor; import org.dromara.common.core.utils.MapstructUtils; -import org.dromara.common.core.utils.StringUtils; import org.dromara.system.domain.SysSocial; import org.dromara.system.domain.bo.SysSocialBo; import org.dromara.system.domain.vo.SysSocialVo; @@ -46,11 +43,11 @@ public class SysSocialServiceImpl implements ISysSocialService { */ @Override public List queryList(SysSocialBo bo) { - LambdaQueryWrapper lqw = new LambdaQueryWrapper() - .eq(ObjectUtil.isNotNull(bo.getUserId()), SysSocial::getUserId, bo.getUserId()) - .eq(StringUtils.isNotBlank(bo.getAuthId()), SysSocial::getAuthId, bo.getAuthId()) - .eq(StringUtils.isNotBlank(bo.getSource()), SysSocial::getSource, bo.getSource()); - return baseMapper.selectVoList(lqw); + return baseMapper.lambda() + .eqIfPresent(SysSocial::getUserId, bo.getUserId()) + .eqIfText(SysSocial::getAuthId, bo.getAuthId()) + .eqIfText(SysSocial::getSource, bo.getSource()) + .voList(); } /** @@ -61,7 +58,7 @@ public class SysSocialServiceImpl implements ISysSocialService { */ @Override public List queryListByUserId(Long userId) { - return baseMapper.selectVoList(new LambdaQueryWrapper().eq(SysSocial::getUserId, userId)); + return baseMapper.lambda().eq(SysSocial::getUserId, userId).voList(); } @@ -129,7 +126,7 @@ public class SysSocialServiceImpl implements ISysSocialService { */ @Override public List selectByAuthId(String authId) { - return baseMapper.selectVoList(new LambdaQueryWrapper().eq(SysSocial::getAuthId, authId)); + return baseMapper.lambda().eq(SysSocial::getAuthId, authId).voList(); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index a73217bb6..52a09c868 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -7,8 +7,6 @@ import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -18,6 +16,7 @@ import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.*; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.api.UserService; import org.dromara.system.api.domain.UserDTO; @@ -88,20 +87,21 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ private Wrapper buildQueryWrapper(SysUserBo user) { Map params = user.getParams(); - LambdaQueryWrapper wrapper = Wrappers.lambdaQuery(); - wrapper.eq(SysUser::getDelFlag, SystemConstants.NORMAL) - .eq(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId()) + LambdaQueryWrapper wrapper = QueryBuilder.lambda(SysUser.class) + .eq(SysUser::getDelFlag, SystemConstants.NORMAL) + .eqIfPresent(SysUser::getUserId, user.getUserId()) .in(StringUtils.isNotBlank(user.getUserIds()), SysUser::getUserId, StringUtils.splitTo(user.getUserIds(), Convert::toLong)) - .like(StringUtils.isNotBlank(user.getUserName()), SysUser::getUserName, user.getUserName()) - .like(StringUtils.isNotBlank(user.getNickName()), SysUser::getNickName, user.getNickName()) - .eq(StringUtils.isNotBlank(user.getStatus()), SysUser::getStatus, user.getStatus()) - .like(StringUtils.isNotBlank(user.getPhoneNumber()), SysUser::getPhoneNumber, user.getPhoneNumber()) - .between(params.get("beginTime") != null && params.get("endTime") != null, - SysUser::getCreateTime, params.get("beginTime"), params.get("endTime")) + .likeIfText(SysUser::getUserName, user.getUserName()) + .likeIfText(SysUser::getNickName, user.getNickName()) + .eqIfText(SysUser::getStatus, user.getStatus()) + .likeIfText(SysUser::getPhoneNumber, user.getPhoneNumber()) + .betweenParams(SysUser::getCreateTime, params, "beginTime", "endTime") .and(ObjectUtil.isNotNull(user.getDeptId()), w -> { List ids = deptMapper.selectDeptAndChildById(user.getDeptId()); w.in(SysUser::getDeptId, ids); - }).orderByAsc(SysUser::getUserId); + }) + .orderByAsc(SysUser::getUserId) + .build(); if (StringUtils.isNotBlank(user.getExcludeUserIds())) { wrapper.notIn(SysUser::getUserId, StringUtils.splitTo(user.getExcludeUserIds(), Convert::toLong)); } @@ -141,7 +141,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserByUserName(String userName) { - return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getUserName, userName)); + return baseMapper.lambda().eq(SysUser::getUserName, userName).voOne(); } /** @@ -152,7 +152,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserByPhoneNumber(String phoneNumber) { - return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(SysUser::getPhoneNumber, phoneNumber)); + return baseMapper.lambda().eq(SysUser::getPhoneNumber, phoneNumber).voOne(); } /** @@ -180,11 +180,12 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public List selectUserByIds(Collection userIds, Long deptId) { - return baseMapper.selectUserList(new LambdaQueryWrapper() + return baseMapper.selectUserList(baseMapper.lambda() .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName) .eq(SysUser::getStatus, SystemConstants.NORMAL) - .eq(ObjectUtil.isNotNull(deptId), SysUser::getDeptId, deptId) - .in(CollUtil.isNotEmpty(userIds), SysUser::getUserId, userIds)); + .eqIfPresent(SysUser::getDeptId, deptId) + .inIfNotEmpty(SysUser::getUserId, userIds) + .build()); } /** @@ -225,9 +226,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public boolean checkUserNameUnique(SysUserBo user) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysUser::getUserName, user.getUserName()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .neIfPresent(SysUser::getUserId, user.getUserId()) + .exists(); return !exist; } @@ -238,9 +240,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public boolean checkPhoneUnique(SysUserBo user) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysUser::getPhoneNumber, user.getPhoneNumber()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .neIfPresent(SysUser::getUserId, user.getUserId()) + .exists(); return !exist; } @@ -251,9 +254,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public boolean checkEmailUnique(SysUserBo user) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(SysUser::getEmail, user.getEmail()) - .ne(ObjectUtil.isNotNull(user.getUserId()), SysUser::getUserId, user.getUserId())); + .neIfPresent(SysUser::getUserId, user.getUserId()) + .exists(); return !exist; } @@ -365,10 +369,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public int updateUserStatus(Long userId, String status) { - return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getStatus, status) - .eq(SysUser::getUserId, userId)); + return baseMapper.lambda() + .set(SysUser::getStatus, status) + .eq(SysUser::getUserId, userId) + .updateCount(); } /** @@ -380,13 +384,13 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @CacheEvict(cacheNames = CacheNames.SYS_NICKNAME, key = "#user.userId") @Override public int updateUserProfile(SysUserBo user) { - return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(ObjectUtil.isNotNull(user.getNickName()), SysUser::getNickName, user.getNickName()) - .set(SysUser::getPhoneNumber, user.getPhoneNumber()) - .set(SysUser::getEmail, user.getEmail()) - .set(SysUser::getGender, user.getGender()) - .eq(SysUser::getUserId, user.getUserId())); + return baseMapper.lambda() + .setIfPresent(SysUser::getNickName, user.getNickName()) + .set(SysUser::getPhoneNumber, user.getPhoneNumber()) + .set(SysUser::getEmail, user.getEmail()) + .set(SysUser::getGender, user.getGender()) + .eq(SysUser::getUserId, user.getUserId()) + .updateCount(); } /** @@ -398,10 +402,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public boolean updateUserAvatar(Long userId, Long avatar) { - return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getAvatar, avatar) - .eq(SysUser::getUserId, userId)) > 0; + return baseMapper.lambda() + .set(SysUser::getAvatar, avatar) + .eq(SysUser::getUserId, userId) + .update(); } /** @@ -413,10 +417,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public int resetUserPwd(Long userId, String password) { - return baseMapper.update(null, - new LambdaUpdateWrapper() - .set(SysUser::getPassword, password) - .eq(SysUser::getUserId, userId)); + return baseMapper.lambda() + .set(SysUser::getPassword, password) + .eq(SysUser::getUserId, userId) + .updateCount(); } /** @@ -449,7 +453,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { // 是否清除旧的用户岗位绑定 if (clear) { - userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId, user.getUserId())); + userPostMapper.lambda().eq(SysUserPost::getUserId, user.getUserId()).delete(); } // 构建用户岗位关联列表并批量插入 @@ -494,7 +498,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { // 是否清除原有绑定 if (clear) { - userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); + userRoleMapper.lambda().eq(SysUserRole::getUserId, userId).delete(); } // 批量插入用户-角色关联 @@ -518,9 +522,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Transactional(rollbackFor = Exception.class) public int deleteUserById(Long userId) { // 删除用户与角色关联 - userRoleMapper.delete(new LambdaQueryWrapper().eq(SysUserRole::getUserId, userId)); + userRoleMapper.lambda().eq(SysUserRole::getUserId, userId).delete(); // 删除用户与岗位表 - userPostMapper.delete(new LambdaQueryWrapper().eq(SysUserPost::getUserId, userId)); + userPostMapper.lambda().eq(SysUserPost::getUserId, userId).delete(); // 防止更新失败导致的数据删除 int flag = baseMapper.deleteById(userId); if (flag < 1) { @@ -544,9 +548,9 @@ public class SysUserServiceImpl implements ISysUserService, UserService { } List ids = List.of(userIds); // 删除用户与角色关联 - userRoleMapper.delete(new LambdaQueryWrapper().in(SysUserRole::getUserId, ids)); + userRoleMapper.lambda().in(SysUserRole::getUserId, ids).delete(); // 删除用户与岗位表 - userPostMapper.delete(new LambdaQueryWrapper().in(SysUserPost::getUserId, ids)); + userPostMapper.lambda().in(SysUserPost::getUserId, ids).delete(); // 防止更新失败导致的数据删除 int flag = baseMapper.deleteByIds(ids); if (flag < 1) { @@ -563,10 +567,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public List selectUserListByDept(Long deptId) { - LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); - lqw.eq(SysUser::getDeptId, deptId); - lqw.orderByAsc(SysUser::getUserId); - return baseMapper.selectVoList(lqw); + return baseMapper.lambda() + .eq(SysUser::getDeptId, deptId) + .orderByAsc(SysUser::getUserId) + .voList(); } /** @@ -578,8 +582,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId") @Override public String selectUserNameById(Long userId) { - SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getUserName).eq(SysUser::getUserId, userId)); + SysUser sysUser = baseMapper.lambda() + .select(SysUser::getUserName) + .eq(SysUser::getUserId, userId) + .one(); return ObjectUtils.notNullGetter(sysUser, SysUser::getUserName); } @@ -592,8 +598,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override @Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId") public String selectNicknameById(Long userId) { - SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getNickName).eq(SysUser::getUserId, userId)); + SysUser sysUser = baseMapper.lambda() + .select(SysUser::getNickName) + .eq(SysUser::getUserId, userId) + .one(); return ObjectUtils.notNullGetter(sysUser, SysUser::getNickName); } @@ -623,8 +631,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public String selectPhonenumberById(Long userId) { - SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getPhoneNumber).eq(SysUser::getUserId, userId)); + SysUser sysUser = baseMapper.lambda() + .select(SysUser::getPhoneNumber) + .eq(SysUser::getUserId, userId) + .one(); return ObjectUtils.notNullGetter(sysUser, SysUser::getPhoneNumber); } @@ -636,8 +646,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public String selectEmailById(Long userId) { - SysUser sysUser = baseMapper.selectOne(new LambdaQueryWrapper() - .select(SysUser::getEmail).eq(SysUser::getUserId, userId)); + SysUser sysUser = baseMapper.lambda() + .select(SysUser::getEmail) + .eq(SysUser::getUserId, userId) + .one(); return ObjectUtils.notNullGetter(sysUser, SysUser::getEmail); } @@ -649,13 +661,14 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public UserDTO selectById(Long userId) { - SysUserVo vo = baseMapper.selectVoOne(new LambdaQueryWrapper() + SysUserVo vo = baseMapper.lambda() .select(SysUser::getUserId, SysUser::getDeptId, SysUser::getUserName, SysUser::getNickName, SysUser::getUserType, SysUser::getEmail, SysUser::getPhoneNumber, SysUser::getGender, SysUser::getStatus, SysUser::getCreateTime) .eq(SysUser::getStatus, SystemConstants.NORMAL) - .eq(SysUser::getUserId, userId)); + .eq(SysUser::getUserId, userId) + .voOne(); return BeanUtil.copyProperties(vo, UserDTO.class); } @@ -670,13 +683,14 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (CollUtil.isEmpty(userIds)) { return List.of(); } - List list = baseMapper.selectVoList(new LambdaQueryWrapper() + List list = baseMapper.lambda() .select(SysUser::getUserId, SysUser::getDeptId, SysUser::getUserName, SysUser::getNickName, SysUser::getUserType, SysUser::getEmail, SysUser::getPhoneNumber, SysUser::getGender, SysUser::getStatus, SysUser::getCreateTime) .eq(SysUser::getStatus, SystemConstants.NORMAL) - .in(SysUser::getUserId, userIds)); + .in(SysUser::getUserId, userIds) + .voList(); return BeanUtil.copyToList(list, UserDTO.class); } @@ -691,8 +705,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (CollUtil.isEmpty(roleIds)) { return List.of(); } - List userRoles = userRoleMapper.selectList( - new LambdaQueryWrapper().in(SysUserRole::getRoleId, roleIds)); + List userRoles = userRoleMapper.lambda().in(SysUserRole::getRoleId, roleIds).list(); return StreamUtils.toList(userRoles, SysUserRole::getUserId); } @@ -709,8 +722,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { } // 通过角色ID获取用户角色信息 - List userRoles = userRoleMapper.selectList( - new LambdaQueryWrapper().in(SysUserRole::getRoleId, roleIds)); + List userRoles = userRoleMapper.lambda().in(SysUserRole::getRoleId, roleIds).list(); // 获取用户ID列表 Set userIds = StreamUtils.toSet(userRoles, SysUserRole::getUserId); @@ -729,10 +741,11 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (CollUtil.isEmpty(deptIds)) { return List.of(); } - List list = baseMapper.selectVoList(new LambdaQueryWrapper() + List list = baseMapper.lambda() .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhoneNumber) .eq(SysUser::getStatus, SystemConstants.NORMAL) - .in(SysUser::getDeptId, deptIds)); + .in(SysUser::getDeptId, deptIds) + .voList(); return BeanUtil.copyToList(list, UserDTO.class); } @@ -749,8 +762,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { } // 通过岗位ID获取用户岗位信息 - List userPosts = userPostMapper.selectList( - new LambdaQueryWrapper().in(SysUserPost::getPostId, postIds)); + List userPosts = userPostMapper.lambda().in(SysUserPost::getPostId, postIds).list(); // 获取用户ID列表 Set userIds = StreamUtils.toSet(userPosts, SysUserPost::getUserId); @@ -769,11 +781,10 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (CollUtil.isEmpty(userIds)) { return Collections.emptyMap(); } - List list = baseMapper.selectList( - new LambdaQueryWrapper() - .select(SysUser::getUserId, SysUser::getNickName) - .in(SysUser::getUserId, userIds) - ); + List list = baseMapper.lambda() + .select(SysUser::getUserId, SysUser::getNickName) + .in(SysUser::getUserId, userIds) + .list(); return StreamUtils.toMap(list, SysUser::getUserId, SysUser::getNickName); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwCategoryMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwCategoryMapper.java index c7a3d2642..d618fafea 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwCategoryMapper.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwCategoryMapper.java @@ -1,8 +1,6 @@ package org.dromara.workflow.mapper; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; -import org.dromara.common.mybatis.helper.DataBaseHelper; import org.dromara.workflow.domain.FlowCategory; import org.dromara.workflow.domain.vo.FlowCategoryVo; @@ -25,9 +23,10 @@ public interface FlwCategoryMapper extends BaseMapperPlus selectListByParentId(Long parentId) { - return this.selectList(new LambdaQueryWrapper() + return this.lambda() .select(FlowCategory::getCategoryId) - .apply(DataBaseHelper.findInSet(parentId, "ancestors"))); + .findInSet(parentId, FlowCategory::getAncestors) + .list(); } /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceBizExtMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceBizExtMapper.java index 4b9a9785e..a895f57e6 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceBizExtMapper.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwInstanceBizExtMapper.java @@ -1,7 +1,6 @@ package org.dromara.workflow.mapper; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.workflow.domain.FlowInstanceBizExt; @@ -23,8 +22,9 @@ public interface FlwInstanceBizExtMapper extends BaseMapperPlus() - .eq(FlowInstanceBizExt::getInstanceId, entity.getInstanceId())); + FlowInstanceBizExt exist = this.lambda() + .eq(FlowInstanceBizExt::getInstanceId, entity.getInstanceId()) + .one(); if (ObjectUtil.isNotNull(exist)) { // 存在就带上主键更新 @@ -43,8 +43,7 @@ public interface FlwInstanceBizExtMapper extends BaseMapperPlus() - .eq(FlowInstanceBizExt::getInstanceId, instanceId)); + return this.lambda().eq(FlowInstanceBizExt::getInstanceId, instanceId).deleteCount(); } /** @@ -54,8 +53,7 @@ public interface FlwInstanceBizExtMapper extends BaseMapperPlus instanceIds) { - return this.delete(new LambdaQueryWrapper() - .in(FlowInstanceBizExt::getInstanceId, instanceIds)); + return this.lambda().in(FlowInstanceBizExt::getInstanceId, instanceIds).deleteCount(); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java index 8ca553673..3b3eeaabb 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java @@ -10,7 +10,6 @@ import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.*; -import org.dromara.common.mybatis.helper.DataBaseHelper; import org.dromara.warm.flow.core.service.DefService; import org.dromara.warm.flow.orm.entity.FlowDefinition; import org.dromara.warm.flow.ui.service.CategoryService; @@ -64,8 +63,10 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ if (ObjectUtil.isNull(categoryId)) { return null; } - FlowCategory category = baseMapper.selectOne(new LambdaQueryWrapper() - .select(FlowCategory::getCategoryName).eq(FlowCategory::getCategoryId, categoryId)); + FlowCategory category = baseMapper.lambda() + .select(FlowCategory::getCategoryName) + .eq(FlowCategory::getCategoryId, categoryId) + .one(); return ObjectUtils.notNullGetter(category, FlowCategory::getCategoryName); } @@ -80,9 +81,10 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ if (CollUtil.isEmpty(categoryIds)) { return Collections.emptyMap(); } - List list = baseMapper.selectList(new LambdaQueryWrapper() + List list = baseMapper.lambda() .select(FlowCategory::getCategoryId, FlowCategory::getCategoryName) - .in(FlowCategory::getCategoryId, categoryIds)); + .in(FlowCategory::getCategoryId, categoryIds) + .list(); return StreamUtils.toMap(list, FlowCategory::getCategoryId, FlowCategory::getCategoryName); } @@ -145,10 +147,11 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ */ @Override public boolean checkCategoryNameUnique(FlowCategoryBo category) { - boolean exist = baseMapper.exists(new LambdaQueryWrapper() + boolean exist = baseMapper.lambda() .eq(FlowCategory::getCategoryName, category.getCategoryName()) .eq(FlowCategory::getParentId, category.getParentId()) - .ne(ObjectUtil.isNotNull(category.getCategoryId()), FlowCategory::getCategoryId, category.getCategoryId())); + .neIfPresent(FlowCategory::getCategoryId, category.getCategoryId()) + .exists(); return !exist; } @@ -173,8 +176,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ */ @Override public boolean hasChildByCategoryId(Long categoryId) { - return baseMapper.exists(new LambdaQueryWrapper() - .eq(FlowCategory::getParentId, categoryId)); + return baseMapper.lambda().eq(FlowCategory::getParentId, categoryId).exists(); } /** @@ -255,8 +257,9 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ * @param oldAncestors 旧的父ID集合 */ private void updateCategoryChildren(Long categoryId, String newAncestors, String oldAncestors) { - List children = baseMapper.selectList(new LambdaQueryWrapper() - .apply(DataBaseHelper.findInSet(categoryId, "ancestors"))); + List children = baseMapper.lambda() + .findInSet(categoryId, FlowCategory::getAncestors) + .list(); List list = new ArrayList<>(); for (FlowCategory child : children) { FlowCategory category = new FlowCategory(); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java index 69ff0de74..537ab0786 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwDefinitionServiceImpl.java @@ -16,6 +16,7 @@ import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.warm.flow.core.dto.DefJson; import org.dromara.warm.flow.core.enums.NodeType; import org.dromara.warm.flow.core.enums.PublishStatus; @@ -121,7 +122,8 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService { @Override @Transactional(rollbackFor = Exception.class) public boolean publish(Long id) { - List flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper().eq(FlowNode::getDefinitionId, id)); + List flowNodes = flowNodeMapper.selectList( + QueryBuilder.lambda(FlowNode.class).eq(FlowNode::getDefinitionId, id).build()); List errorMsg = new ArrayList<>(); if (CollUtil.isNotEmpty(flowNodes)) { String applyNodeCode = flwCommonService.applyNodeCode(id); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index a4a10c1f2..87be5598a 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -4,8 +4,7 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.toolkit.JoinWrappers; import com.github.yulichang.wrapper.MPJLambdaWrapper; @@ -17,6 +16,7 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.constant.ExceptionCons; @@ -185,7 +185,10 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { */ @Override public FlowInstance selectInstByBusinessId(String businessId) { - return flowInstanceMapper.selectOne(new LambdaQueryWrapper().eq(FlowInstance::getBusinessId, businessId)); + return flowInstanceMapper.selectOne( + QueryBuilder.lambda(FlowInstance.class) + .eq(FlowInstance::getBusinessId, businessId) + .build()); } /** @@ -219,7 +222,10 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { @Override @Transactional(rollbackFor = Exception.class) public boolean deleteByBusinessIds(List businessIds) { - List flowInstances = flowInstanceMapper.selectList(new LambdaQueryWrapper().in(FlowInstance::getBusinessId, businessIds)); + List flowInstances = flowInstanceMapper.selectList( + QueryBuilder.lambda(FlowInstance.class) + .in(FlowInstance::getBusinessId, businessIds) + .build()); if (CollUtil.isEmpty(flowInstances)) { log.warn("未找到对应的流程实例信息,无法执行删除操作。"); return false; @@ -393,10 +399,11 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { // 再组装历史任务(已处理任务) List hisTaskVos = new ArrayList<>(); List hisTasks = flowHisTaskMapper.selectList( - new LambdaQueryWrapper() + QueryBuilder.lambda(FlowHisTask.class) .eq(FlowHisTask::getInstanceId, instanceId) .eq(FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()) .orderByDesc(FlowHisTask::getUpdateTime) + .build() ); if (CollUtil.isNotEmpty(hisTasks)) { hisTaskVos = BeanUtil.copyToList(hisTasks, FlowHisTaskVo.class); @@ -418,10 +425,9 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { */ @Override public void updateStatus(Long instanceId, String status) { - LambdaUpdateWrapper wrapper = new LambdaUpdateWrapper<>(); - wrapper.set(FlowInstance::getFlowStatus, status); - wrapper.eq(FlowInstance::getId, instanceId); - flowInstanceMapper.update(wrapper); + flowInstanceMapper.update(Wrappers.lambdaUpdate(FlowInstance.class) + .set(FlowInstance::getFlowStatus, status) + .eq(FlowInstance::getId, instanceId)); } /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java index 535e3cdd4..5745df8bf 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java @@ -1,7 +1,6 @@ package org.dromara.workflow.service.impl; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -135,9 +134,10 @@ public class FlwSpelServiceImpl implements IFlwSpelService { */ private void validEntityBeforeSave(FlowSpel entity){ if (StringUtils.isNotBlank(entity.getViewSpel())) { - boolean exists = baseMapper.exists(new LambdaQueryWrapper() + boolean exists = baseMapper.lambda() .eq(FlowSpel::getViewSpel, entity.getViewSpel()) - .ne(ObjectUtil.isNotNull(entity.getId()), FlowSpel::getId, entity.getId())); + .neIfPresent(FlowSpel::getId, entity.getId()) + .exists(); if (exists) { throw new ServiceException("SpEL表达式已存在,请勿重复添加"); } @@ -193,11 +193,10 @@ public class FlwSpelServiceImpl implements IFlwSpelService { if (CollUtil.isEmpty(viewSpels)) { return Collections.emptyMap(); } - List list = baseMapper.selectList( - new LambdaQueryWrapper() - .select(FlowSpel::getViewSpel, FlowSpel::getRemark) - .in(FlowSpel::getViewSpel, viewSpels) - ); + List list = baseMapper.lambda() + .select(FlowSpel::getViewSpel, FlowSpel::getRemark) + .in(FlowSpel::getViewSpel, viewSpels) + .list(); return StreamUtils.toMap(list, FlowSpel::getViewSpel, x -> StringUtils.isEmpty(x.getRemark()) ? "" : x.getRemark() ); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 9fdf929b2..42773bcd7 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -7,7 +7,6 @@ import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.lock.annotation.Lock4j; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -21,6 +20,7 @@ import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.mybatis.utils.IdGeneratorUtil; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.api.domain.UserDTO; @@ -121,8 +121,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { FlowInstanceBizExt bizExt = startProcessBo.getBizExt(); // 获取已有流程实例 - FlowInstance flowInstance = flowInstanceMapper.selectOne(new LambdaQueryWrapper<>(FlowInstance.class) - .eq(FlowInstance::getBusinessId, businessId)); + FlowInstance flowInstance = flowInstanceMapper.selectOne(QueryBuilder.lambda(FlowInstance.class) + .eq(FlowInstance::getBusinessId, businessId) + .build()); if (ObjectUtil.isNotNull(flowInstance)) { // 已存在流程 @@ -339,8 +340,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { } // 添加抄送人记录 FlowHisTask flowHisTask = flowHisTaskMapper.selectList( - new LambdaQueryWrapper<>(FlowHisTask.class) - .eq(FlowHisTask::getTaskId, task.getId())).get(0); + QueryBuilder.lambda(FlowHisTask.class) + .eq(FlowHisTask::getTaskId, task.getId()) + .build()).get(0); FlowNode flowNode = new FlowNode(); flowNode.setNodeCode(flowHisTask.getTargetNodeCode()); flowNode.setNodeName(flowHisTask.getTargetNodeName()); @@ -599,7 +601,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public List selectByIdList(Collection taskIdList) { - return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class).in(FlowTask::getId, taskIdList)); + return flowTaskMapper.selectList(QueryBuilder.lambda(FlowTask.class) + .in(FlowTask::getId, taskIdList) + .build()); } /** @@ -713,7 +717,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public FlowHisTask selectHisTaskById(Long taskId) { - return flowHisTaskMapper.selectOne(new LambdaQueryWrapper<>(FlowHisTask.class).eq(FlowHisTask::getId, taskId)); + return flowHisTaskMapper.selectOne(QueryBuilder.lambda(FlowHisTask.class) + .eq(FlowHisTask::getId, taskId) + .build()); } /** @@ -724,7 +730,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public List selectByInstId(Long instanceId) { - return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class).eq(FlowTask::getInstanceId, instanceId)); + return flowTaskMapper.selectList(QueryBuilder.lambda(FlowTask.class) + .eq(FlowTask::getInstanceId, instanceId) + .build()); } /** @@ -735,7 +743,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public List selectByInstIds(Collection instanceIds) { - return flowTaskMapper.selectList(new LambdaQueryWrapper<>(FlowTask.class).in(FlowTask::getInstanceId, instanceIds)); + return flowTaskMapper.selectList(QueryBuilder.lambda(FlowTask.class) + .in(FlowTask::getInstanceId, instanceIds) + .build()); } /** @@ -746,7 +756,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public boolean isTaskEnd(Long instanceId) { - boolean exists = flowTaskMapper.exists(new LambdaQueryWrapper().eq(FlowTask::getInstanceId, instanceId)); + boolean exists = flowTaskMapper.exists(QueryBuilder.lambda(FlowTask.class) + .eq(FlowTask::getInstanceId, instanceId) + .build()); return !exists; } @@ -897,9 +909,10 @@ public class FlwTaskServiceImpl implements IFlwTaskService { */ @Override public FlowNode getByNodeCode(String nodeCode, Long definitionId) { - return flowNodeMapper.selectOne(new LambdaQueryWrapper() + return flowNodeMapper.selectOne(QueryBuilder.lambda(FlowNode.class) .eq(FlowNode::getNodeCode, nodeCode) - .eq(FlowNode::getDefinitionId, definitionId)); + .eq(FlowNode::getDefinitionId, definitionId) + .build()); } /** From 0d8a5f9224af17848e661057a5291f9598718391 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 12:25:57 +0800 Subject: [PATCH 03/35] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E8=A7=84?= =?UTF-8?q?=E8=8C=83=E5=8C=96mapper=E5=91=BD=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/agents/backend-crud.md | 2 +- .codex/skills/ruoyi-plus-ai-coding/SKILL.md | 3 +- .../references/backend.md | 6 +- .../core/mapper/LambdaCrudChainWrapper.java | 46 +++++++------- .../service/impl/TestDemoServiceImpl.java | 20 +++--- .../service/impl/TestTreeServiceImpl.java | 12 ++-- .../gen/service/GenTableServiceImpl.java | 14 ++--- .../resources/vm/java/serviceImpl.java.vm | 28 ++++----- .../service/impl/SysClientServiceImpl.java | 20 +++--- .../service/impl/SysConfigServiceImpl.java | 24 +++---- .../service/impl/SysDeptServiceImpl.java | 48 +++++++------- .../service/impl/SysDictDataServiceImpl.java | 24 +++---- .../service/impl/SysDictTypeServiceImpl.java | 24 +++---- .../service/impl/SysLoginInfoServiceImpl.java | 14 ++--- .../service/impl/SysMenuServiceImpl.java | 36 +++++------ .../service/impl/SysMessageServiceImpl.java | 6 +- .../service/impl/SysNoticeServiceImpl.java | 16 ++--- .../service/impl/SysOperLogServiceImpl.java | 14 ++--- .../service/impl/SysOssConfigServiceImpl.java | 26 ++++---- .../service/impl/SysOssServiceImpl.java | 12 ++-- .../service/impl/SysPostServiceImpl.java | 36 +++++------ .../service/impl/SysRoleServiceImpl.java | 42 ++++++------- .../service/impl/SysSocialServiceImpl.java | 16 ++--- .../service/impl/SysUserServiceImpl.java | 62 +++++++++---------- .../service/impl/FlwCategoryServiceImpl.java | 30 ++++----- .../service/impl/FlwSpelServiceImpl.java | 18 +++--- .../service/impl/TestLeaveServiceImpl.java | 24 +++---- 27 files changed, 313 insertions(+), 310 deletions(-) diff --git a/.claude/agents/backend-crud.md b/.claude/agents/backend-crud.md index 0dc8b334c..26d732bf8 100644 --- a/.claude/agents/backend-crud.md +++ b/.claude/agents/backend-crud.md @@ -18,7 +18,7 @@ description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRU - mapper 默认继承 `BaseMapperPlus` - BO 使用 `@AutoMapper(target = Entity.class, reverseConvertGenerate = false)` - VO 使用 `@AutoMapper(target = Entity.class)` -- service 使用 `baseMapper` +- 代码生成器模板按类名首字母小写命名 Mapper 字段,例如 `SysRoleMapper` -> `sysRoleMapper`;手写业务代码可使用具体业务短名 ## 默认方法集合 diff --git a/.codex/skills/ruoyi-plus-ai-coding/SKILL.md b/.codex/skills/ruoyi-plus-ai-coding/SKILL.md index cea7f02d7..a71dc8edf 100644 --- a/.codex/skills/ruoyi-plus-ai-coding/SKILL.md +++ b/.codex/skills/ruoyi-plus-ai-coding/SKILL.md @@ -129,7 +129,8 @@ Vue 3、TypeScript API 文件、生成式列表页、表单状态、字典和日 - 包路径和 `@RequestMapping` 与模块保持一致。 - 权限标识遵循 `${module}:${business}:${action}`。 - Mapper 继承 `BaseMapperPlus`。 -- Service 使用 `baseMapper`,并按场景返回 `PageResult` 或 `List`。 +- 手写 Service 注入 Mapper 时使用具体业务短名;代码生成器模板按类名首字母小写命名,例如 `SysRoleMapper` 生成 `sysRoleMapper`。 +- Service 按场景返回 `PageResult` 或 `List`。 - 查询代码优先使用 `LambdaQueryWrapper`,复杂模块沿用既有 MPJ 联表风格。 - BO 使用 `@AutoMapper(target = Entity.class, reverseConvertGenerate = false)`。 - VO 使用 `@AutoMapper(target = Entity.class)`。 diff --git a/.codex/skills/ruoyi-plus-ai-coding/references/backend.md b/.codex/skills/ruoyi-plus-ai-coding/references/backend.md index 061361d4f..999284194 100644 --- a/.codex/skills/ruoyi-plus-ai-coding/references/backend.md +++ b/.codex/skills/ruoyi-plus-ai-coding/references/backend.md @@ -86,13 +86,15 @@ ## Service 规则 - 类声明通常是 `@RequiredArgsConstructor`、`@Service`,按需补 `@Slf4j`。 -- mapper 注入字段命名为 `private final XxxMapper baseMapper;`。 +- 手写 mapper 注入字段使用具体业务短名;代码生成器模板按类名首字母小写命名。 +- 命名时去掉清晰的模块/系统前缀后使用 lowerCamel + `Mapper`,例如 `SysRoleMapper` -> `roleMapper`、`SysDictDataMapper` -> `dictDataMapper`。 +- 如果去掉前缀会产生歧义或命名冲突,保留必要前缀。 - 读操作通常返回 `Vo`、`List` 或 `PageResult`。 - BO 转实体用 `MapstructUtils.convert(bo, Entity.class)`。 - 查询条件优先用 `LambdaQueryWrapper` 和 `Wrappers.lambdaQuery()`。 - 在 wrapper 条件里直接写 `StringUtils.isNotBlank(...)` 和 null 判断。 - 分页查询优先采用: - `Page result = baseMapper.selectVoPage(pageQuery.build(), lqw);` + `Page result = entityMapper.selectVoPage(pageQuery.build(), lqw);` `return PageResult.build(result.getRecords(), result.getTotal());` - 生成器风格模块保留 `validEntityBeforeSave(...)` 这种扩展点。 - 多表写操作使用 `@Transactional(rollbackFor = Exception.class)`。 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 3ca49382a..48d37d5b9 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 @@ -36,22 +36,22 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper, SFunction>, LambdaQueryCondition> { - private final BaseMapperPlus baseMapper; + private final BaseMapperPlus crudMapper; private final List sqlSet; private SharedString sqlSelect = new SharedString(); - public LambdaCrudChainWrapper(BaseMapperPlus baseMapper) { - this.baseMapper = baseMapper; - super.setEntityClass(baseMapper.currentModelClass()); + public LambdaCrudChainWrapper(BaseMapperPlus crudMapper) { + this.crudMapper = crudMapper; + super.setEntityClass(crudMapper.currentModelClass()); super.initNeed(); this.sqlSet = new ArrayList<>(); } - LambdaCrudChainWrapper(BaseMapperPlus baseMapper, T entity, Class entityClass, SharedString sqlSelect, + LambdaCrudChainWrapper(BaseMapperPlus crudMapper, T entity, Class entityClass, SharedString sqlSelect, List sqlSet, AtomicInteger paramNameSeq, Map paramNameValuePairs, MergeSegments mergeSegments, SharedString paramAlias, SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) { - this.baseMapper = baseMapper; + this.crudMapper = crudMapper; super.setEntity(entity); super.setEntityClass(entityClass); this.sqlSelect = sqlSelect == null ? new SharedString() : sqlSelect; @@ -197,7 +197,7 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper list() { - return baseMapper.selectList(typedThis); + return crudMapper.selectList(typedThis); } /** @@ -207,7 +207,7 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper list(IPage page) { - return baseMapper.selectList(page, typedThis); + return crudMapper.selectList(page, typedThis); } /** @@ -216,7 +216,7 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper voList() { - return baseMapper.selectVoList(typedThis); + return crudMapper.selectVoList(typedThis); } /** @@ -225,7 +225,7 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper objs() { - return baseMapper.selectObjs(typedThis); + return crudMapper.selectObjs(typedThis); } /** @@ -236,7 +236,7 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper List objs(Function mapper) { - return baseMapper.selectObjs(typedThis, mapper); + return crudMapper.selectObjs(typedThis, mapper); } /** @@ -245,7 +245,7 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper> P page(P page) { - return baseMapper.selectPage(page, typedThis); + return crudMapper.selectPage(page, typedThis); } /** @@ -323,7 +323,7 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper> P voPage(IPage page) { - return baseMapper.selectVoPage(page, typedThis); + return crudMapper.selectVoPage(page, typedThis); } /** @@ -341,7 +341,7 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper extends AbstractLambdaWrapper instance() { - return new LambdaCrudChainWrapper<>(baseMapper, getEntity(), getEntityClass(), null, null, paramNameSeq, + return new LambdaCrudChainWrapper<>(crudMapper, getEntity(), getEntityClass(), null, null, paramNameSeq, paramNameValuePairs, new MergeSegments(), paramAlias, SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString()); } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java index 014b7e81d..872246dbf 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestDemoServiceImpl.java @@ -29,7 +29,7 @@ import java.util.List; @Service public class TestDemoServiceImpl implements ITestDemoService { - private final TestDemoMapper baseMapper; + private final TestDemoMapper demoMapper; /** * 根据主键查询测试单表详情。 @@ -39,7 +39,7 @@ public class TestDemoServiceImpl implements ITestDemoService { */ @Override public TestDemoVo queryById(Long id) { - return baseMapper.selectVoById(id); + return demoMapper.selectVoById(id); } /** @@ -52,7 +52,7 @@ public class TestDemoServiceImpl implements ITestDemoService { @Override public PageResult queryPageList(TestDemoBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page result = demoMapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(result.getRecords(), result.getTotal()); } @@ -66,7 +66,7 @@ public class TestDemoServiceImpl implements ITestDemoService { @Override public PageResult customPageList(TestDemoBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.customPageList(pageQuery.build(), lqw); + Page result = demoMapper.customPageList(pageQuery.build(), lqw); return PageResult.build(result.getRecords(), result.getTotal()); } @@ -78,7 +78,7 @@ public class TestDemoServiceImpl implements ITestDemoService { */ @Override public List queryList(TestDemoBo bo) { - return baseMapper.selectVoList(buildQueryWrapper(bo)); + return demoMapper.selectVoList(buildQueryWrapper(bo)); } /** @@ -107,7 +107,7 @@ public class TestDemoServiceImpl implements ITestDemoService { public Boolean insertByBo(TestDemoBo bo) { TestDemo add = MapstructUtils.convert(bo, TestDemo.class); validEntityBeforeSave(add); - boolean flag = baseMapper.insert(add) > 0; + boolean flag = demoMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); } @@ -124,7 +124,7 @@ public class TestDemoServiceImpl implements ITestDemoService { public Boolean updateByBo(TestDemoBo bo) { TestDemo update = MapstructUtils.convert(bo, TestDemo.class); validEntityBeforeSave(update); - return baseMapper.updateById(update) > 0; + return demoMapper.updateById(update) > 0; } /** @@ -147,12 +147,12 @@ public class TestDemoServiceImpl implements ITestDemoService { public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { if (isValid) { // 做一些业务上的校验,判断是否需要校验 - List list = baseMapper.selectByIds(ids); + List list = demoMapper.selectByIds(ids); if (list.size() != ids.size()) { throw new ServiceException("您没有删除权限!"); } } - return baseMapper.deleteByIds(ids) > 0; + return demoMapper.deleteByIds(ids) > 0; } /** @@ -163,6 +163,6 @@ public class TestDemoServiceImpl implements ITestDemoService { */ @Override public Boolean saveBatch(List list) { - return baseMapper.insertBatch(list); + return demoMapper.insertBatch(list); } } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java index 4671e4876..acf87a699 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/TestTreeServiceImpl.java @@ -26,7 +26,7 @@ import java.util.List; @Service public class TestTreeServiceImpl implements ITestTreeService { - private final TestTreeMapper baseMapper; + private final TestTreeMapper treeMapper; /** * 根据主键查询测试树表详情。 @@ -36,7 +36,7 @@ public class TestTreeServiceImpl implements ITestTreeService { */ @Override public TestTreeVo queryById(Long id) { - return baseMapper.selectVoById(id); + return treeMapper.selectVoById(id); } // @DS("slave") // 切换从库查询 @@ -49,7 +49,7 @@ public class TestTreeServiceImpl implements ITestTreeService { @Override public List queryList(TestTreeBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); + return treeMapper.selectVoList(lqw); } /** @@ -77,7 +77,7 @@ public class TestTreeServiceImpl implements ITestTreeService { public Boolean insertByBo(TestTreeBo bo) { TestTree add = MapstructUtils.convert(bo, TestTree.class); validEntityBeforeSave(add); - boolean flag = baseMapper.insert(add) > 0; + boolean flag = treeMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); } @@ -94,7 +94,7 @@ public class TestTreeServiceImpl implements ITestTreeService { public Boolean updateByBo(TestTreeBo bo) { TestTree update = MapstructUtils.convert(bo, TestTree.class); validEntityBeforeSave(update); - return baseMapper.updateById(update) > 0; + return treeMapper.updateById(update) > 0; } /** @@ -118,6 +118,6 @@ public class TestTreeServiceImpl implements ITestTreeService { if (isValid) { //TODO 做一些业务上的校验,判断是否需要校验 } - return baseMapper.deleteByIds(ids) > 0; + return treeMapper.deleteByIds(ids) > 0; } } diff --git a/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java b/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java index ad1c5022c..a06e9459c 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java +++ b/ruoyi-modules/ruoyi-gen/src/main/java/org/dromara/gen/service/GenTableServiceImpl.java @@ -52,7 +52,7 @@ import java.util.zip.ZipOutputStream; @Service public class GenTableServiceImpl implements IGenTableService { - private final GenTableMapper baseMapper; + private final GenTableMapper tableMapper; private final GenTableColumnMapper genTableColumnMapper; private static final String[] TABLE_IGNORE = new String[]{"sj_", "flow_", "gen_"}; @@ -93,7 +93,7 @@ public class GenTableServiceImpl implements IGenTableService { */ @Override public PageResult selectPageGenTableList(GenTable genTable, PageQuery pageQuery) { - Page page = baseMapper.selectPage(pageQuery.build(), this.buildGenTableQueryWrapper(genTable)); + Page page = tableMapper.selectPage(pageQuery.build(), this.buildGenTableQueryWrapper(genTable)); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -134,7 +134,7 @@ public class GenTableServiceImpl implements IGenTableService { if (CollUtil.isEmpty(tablesMap)) { return PageResult.build(); } - List tableNames = baseMapper.selectTableNameList(genTable.getDataName()); + List tableNames = tableMapper.selectTableNameList(genTable.getDataName()); String[] tableArrays; if (CollUtil.isNotEmpty(tableNames)) { tableArrays = tableNames.toArray(new String[0]); @@ -230,7 +230,7 @@ public class GenTableServiceImpl implements IGenTableService { normalizeColumnOptions(genTable.getColumns()); String options = JsonUtils.toJsonString(genTable.getParams()); genTable.setOptions(options); - int row = baseMapper.updateById(genTable); + int row = tableMapper.updateById(genTable); if (row > 0) { genTableColumnMapper.updateBatchById(genTable.getColumns()); } @@ -245,7 +245,7 @@ public class GenTableServiceImpl implements IGenTableService { @Override public void deleteGenTableByIds(Long[] tableIds) { List ids = Arrays.asList(tableIds); - baseMapper.deleteByIds(ids); + tableMapper.deleteByIds(ids); genTableColumnMapper.lambda() .in(GenTableColumn::getTableId, ids) .deleteCount(); @@ -265,7 +265,7 @@ public class GenTableServiceImpl implements IGenTableService { String tableName = table.getTableName(); GenUtils.initTable(table); table.setDataName(dataName); - int row = baseMapper.insert(table); + int row = tableMapper.insert(table); if (row > 0) { // 保存列信息 List genTableColumns = SpringUtils.getAopProxy(this).selectDbTableColumnsByName(tableName, dataName); @@ -534,7 +534,7 @@ public class GenTableServiceImpl implements IGenTableService { * @return 包含字段集合的业务表实体 */ private GenTable getGenTable(Long tableId) { - GenTable table = baseMapper.selectById(tableId); + GenTable table = tableMapper.selectById(tableId); if (ObjectUtil.isNull(table)) { throw new ServiceException("业务表不存在"); } diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm index b7dc57b3d..d6e15e721 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/serviceImpl.java.vm @@ -41,7 +41,7 @@ import java.util.Collection; @Service public class ${ClassName}ServiceImpl implements I${ClassName}Service { - private final ${ClassName}Mapper baseMapper; + private final ${ClassName}Mapper ${className}Mapper; #set($TreeParentCap = "") #if("" != $treeParentCode) @@ -64,7 +64,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { */ @Override public ${ClassName}Vo queryById(${pkColumn.javaType} ${pkColumn.javaField}) { - return baseMapper.selectVoById(${pkColumn.javaField}); + return ${className}Mapper.selectVoById(${pkColumn.javaField}); } #if($table.crud) @@ -78,7 +78,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { @Override public PageResult<${ClassName}Vo> queryPageList(${ClassName}Bo bo, PageQuery pageQuery) { LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); - Page<${ClassName}Vo> result = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page<${ClassName}Vo> result = ${className}Mapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(result.getRecords(), result.getTotal()); } #end @@ -92,7 +92,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { @Override public List<${ClassName}Vo> queryList(${ClassName}Bo bo) { LambdaQueryWrapper<${ClassName}> lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); + return ${className}Mapper.selectVoList(lqw); } #if($enableUnique) @@ -120,7 +120,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { lqw.eq(${ClassName}::get${column.capJavaField}, bo.get${column.capJavaField}()); #end lqw.ne(bo.get${pkColumn.capJavaField}() != null, ${ClassName}::get${pkColumn.capJavaField}, bo.get${pkColumn.capJavaField}()); - return !baseMapper.exists(lqw); + return !${className}Mapper.exists(lqw); } #end @@ -200,7 +200,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { fillTreeMetaBeforeSave(add, false); #end validEntityBeforeSave(add); - boolean flag = baseMapper.insert(add) > 0; + boolean flag = ${className}Mapper.insert(add) > 0; if (flag) { bo.set${pkColumn.capJavaField}(add.get${pkColumn.capJavaField}()); } @@ -220,7 +220,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { fillTreeMetaBeforeSave(update, true); #end validEntityBeforeSave(update); - return baseMapper.updateById(update) > 0; + return ${className}Mapper.updateById(update) > 0; } #if($enableStatus) @@ -233,7 +233,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { */ @Override public Boolean updateStatus(${pkColumn.javaType} ${pkColumn.javaField}, ${statusColumn.javaType} status) { - return baseMapper.lambda() + return ${className}Mapper.lambda() .set(${ClassName}::get${statusColumn.capJavaField}, status) .eq(${ClassName}::get${pkColumn.capJavaField}, ${pkColumn.javaField}) .update(); @@ -250,7 +250,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { */ @Override public Boolean updateSort(${pkColumn.javaType} ${pkColumn.javaField}, ${sortColumn.javaType} sortValue) { - return baseMapper.lambda() + return ${className}Mapper.lambda() .set(${ClassName}::get${sortColumn.capJavaField}, sortValue) .eq(${ClassName}::get${pkColumn.capJavaField}, ${pkColumn.javaField}) .update(); @@ -276,7 +276,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { #if("" != $treeAncestorsField) ${ClassName} parent = null; if (!ObjectUtil.equal(entity.get${TreeParentCap}(), ${treeRootValueJavaLiteral})) { - parent = baseMapper.selectById(entity.get${TreeParentCap}()); + parent = ${className}Mapper.selectById(entity.get${TreeParentCap}()); if (ObjectUtil.isNull(parent)) { throw new ServiceException("${functionName}父节点不存在"); } @@ -287,7 +287,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { } String newAncestors = resolveAncestors(entity.get${TreeParentCap}(), parent); if (updateMode && entity.get${pkColumn.capJavaField}() != null) { - ${ClassName} oldEntity = baseMapper.selectById(entity.get${pkColumn.capJavaField}()); + ${ClassName} oldEntity = ${className}Mapper.selectById(entity.get${pkColumn.capJavaField}()); if (ObjectUtil.isNull(oldEntity)) { throw new ServiceException("${functionName}不存在,无法修改"); } @@ -316,7 +316,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { } private void updateChildrenAncestors(${pkColumn.javaType} currentId, String newAncestors, String oldAncestors) { - List<${ClassName}> children = baseMapper.lambda() + List<${ClassName}> children = ${className}Mapper.lambda() .select(${ClassName}::get${pkColumn.capJavaField}, ${ClassName}::get${TreeAncestorsCap}) .findInSet(currentId, ${ClassName}::get${TreeAncestorsCap}) .list(); @@ -332,7 +332,7 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { updateList.add(update); } if (!updateList.isEmpty()) { - baseMapper.updateBatchById(updateList); + ${className}Mapper.updateBatchById(updateList); } } @@ -359,6 +359,6 @@ public class ${ClassName}ServiceImpl implements I${ClassName}Service { if (isValid) { // 可在此扩展删除前业务校验 } - return baseMapper.deleteByIds(ids) > 0; + return ${className}Mapper.deleteByIds(ids) > 0; } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java index adbf10ac9..5c94e350d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysClientServiceImpl.java @@ -39,7 +39,7 @@ public class SysClientServiceImpl implements ISysClientService { private static final String CLIENT_RULE_SEPARATOR_REGEX = "[,;\\r\\n]+"; - private final SysClientMapper baseMapper; + private final SysClientMapper clientMapper; /** * 查询客户端管理 @@ -49,7 +49,7 @@ public class SysClientServiceImpl implements ISysClientService { */ @Override public SysClientVo queryById(Long id) { - SysClientVo vo = baseMapper.selectVoById(id); + SysClientVo vo = clientMapper.selectVoById(id); fillClientRuleFields(vo); return vo; } @@ -63,7 +63,7 @@ public class SysClientServiceImpl implements ISysClientService { @Cacheable(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") @Override public SysClientVo queryByClientId(String clientId) { - SysClientVo vo = baseMapper.lambda().eq(SysClient::getClientId, clientId).voOne(); + SysClientVo vo = clientMapper.lambda().eq(SysClient::getClientId, clientId).voOne(); fillClientRuleFields(vo); return vo; } @@ -78,7 +78,7 @@ public class SysClientServiceImpl implements ISysClientService { @Override public PageResult queryPageList(SysClientBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page result = clientMapper.selectVoPage(pageQuery.build(), lqw); result.getRecords().forEach(this::fillClientRuleFields); return PageResult.build(result.getRecords(), result.getTotal()); } @@ -92,7 +92,7 @@ public class SysClientServiceImpl implements ISysClientService { @Override public List queryList(SysClientBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - List list = baseMapper.selectVoList(lqw); + List list = clientMapper.selectVoList(lqw); list.forEach(this::fillClientRuleFields); return list; } @@ -129,7 +129,7 @@ public class SysClientServiceImpl implements ISysClientService { String clientKey = bo.getClientKey(); String clientSecret = bo.getClientSecret(); add.setClientId(SecureUtil.md5(clientKey + clientSecret)); - boolean flag = baseMapper.insert(add) > 0; + boolean flag = clientMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); } @@ -149,7 +149,7 @@ public class SysClientServiceImpl implements ISysClientService { update.setGrantType(StringUtils.joinComma(bo.getGrantTypeList())); update.setAccessPath(resolveRuleValue(bo.getAccessPath(), bo.getAccessPathList(), this::normalizeAccessPath)); update.setIpWhitelist(resolveRuleValue(bo.getIpWhitelist(), bo.getIpWhitelistList(), UnaryOperator.identity())); - return baseMapper.updateById(update) > 0; + return clientMapper.updateById(update) > 0; } /** @@ -162,7 +162,7 @@ public class SysClientServiceImpl implements ISysClientService { @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, key = "#clientId") @Override public int updateClientStatus(String clientId, String status) { - return baseMapper.lambda() + return clientMapper.lambda() .set(SysClient::getStatus, status) .eq(SysClient::getClientId, clientId) .updateCount(); @@ -178,7 +178,7 @@ public class SysClientServiceImpl implements ISysClientService { @CacheEvict(cacheNames = CacheNames.SYS_CLIENT, allEntries = true) @Override public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { - return baseMapper.deleteByIds(ids) > 0; + return clientMapper.deleteByIds(ids) > 0; } /** @@ -189,7 +189,7 @@ public class SysClientServiceImpl implements ISysClientService { */ @Override public boolean checkClickKeyUnique(SysClientBo client) { - boolean exist = baseMapper.lambda() + boolean exist = clientMapper.lambda() .eq(SysClient::getClientKey, client.getClientKey()) .neIfPresent(SysClient::getId, client.getId()) .exists(); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java index 049c1d160..56862ff6e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java @@ -41,7 +41,7 @@ import java.util.Map; @Service public class SysConfigServiceImpl implements ISysConfigService, ConfigService { - private final SysConfigMapper baseMapper; + private final SysConfigMapper configMapper; /** * 分页查询参数配置列表 @@ -53,7 +53,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { @Override public PageResult selectPageConfigList(SysConfigBo config, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(config); - Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page page = configMapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -65,7 +65,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { */ @Override public SysConfigVo selectConfigById(Long configId) { - return baseMapper.selectVoById(configId); + return configMapper.selectVoById(configId); } /** @@ -77,7 +77,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { @Cacheable(cacheNames = CacheNames.SYS_CONFIG, key = "#configKey") @Override public String selectConfigByKey(String configKey) { - SysConfig retConfig = baseMapper.lambda().eq(SysConfig::getConfigKey, configKey).one(); + SysConfig retConfig = configMapper.lambda().eq(SysConfig::getConfigKey, configKey).one(); return ObjectUtils.notNullGetter(retConfig, SysConfig::getConfigValue, StringUtils.EMPTY); } @@ -100,7 +100,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { @Override public List selectConfigList(SysConfigBo config) { LambdaQueryWrapper lqw = buildQueryWrapper(config); - return baseMapper.selectVoList(lqw); + return configMapper.selectVoList(lqw); } /** @@ -130,7 +130,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { @Override public String insertConfig(SysConfigBo bo) { SysConfig config = MapstructUtils.convert(bo, SysConfig.class); - int row = baseMapper.insert(config); + int row = configMapper.insert(config); if (row > 0) { return config.getConfigValue(); } @@ -149,14 +149,14 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { int row = 0; SysConfig config = MapstructUtils.convert(bo, SysConfig.class); if (config.getConfigId() != null) { - SysConfig temp = baseMapper.selectById(config.getConfigId()); + SysConfig temp = configMapper.selectById(config.getConfigId()); if (ObjectUtil.isNotNull(temp) && !StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) { CacheUtils.evict(CacheNames.SYS_CONFIG, temp.getConfigKey()); } - row = baseMapper.updateById(config); + row = configMapper.updateById(config); } else { CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); - row = baseMapper.lambda() + row = configMapper.lambda() .eq(SysConfig::getConfigKey, config.getConfigKey()) .updateCount(config); } @@ -173,14 +173,14 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { */ @Override public void deleteConfigByIds(Collection configIds) { - List list = baseMapper.selectByIds(configIds); + List list = configMapper.selectByIds(configIds); list.forEach(config -> { if (StringUtils.equals(SystemConstants.YES, config.getConfigType())) { throw new ServiceException("内置参数【{}】不能删除", config.getConfigKey()); } CacheUtils.evict(CacheNames.SYS_CONFIG, config.getConfigKey()); }); - baseMapper.deleteByIds(configIds); + configMapper.deleteByIds(configIds); } /** @@ -199,7 +199,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { */ @Override public boolean checkConfigKeyUnique(SysConfigBo config) { - boolean exist = baseMapper.lambda() + boolean exist = configMapper.lambda() .eq(SysConfig::getConfigKey, config.getConfigKey()) .neIfPresent(SysConfig::getConfigId, config.getConfigId()) .exists(); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java index e5fbd6865..899cadb18 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDeptServiceImpl.java @@ -46,7 +46,7 @@ import java.util.*; @Service public class SysDeptServiceImpl implements ISysDeptService, DeptService { - private final SysDeptMapper baseMapper; + private final SysDeptMapper deptMapper; private final SysRoleMapper roleMapper; private final SysUserMapper userMapper; @@ -59,7 +59,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public PageResult selectPageDeptList(SysDeptBo dept, PageQuery pageQuery) { - Page page = baseMapper.selectPageDeptList(pageQuery.build(), buildQueryWrapper(dept)); + Page page = deptMapper.selectPageDeptList(pageQuery.build(), buildQueryWrapper(dept)); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -72,7 +72,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { @Override public List selectDeptList(SysDeptBo dept) { LambdaQueryWrapper lqw = buildQueryWrapper(dept); - return baseMapper.selectDeptList(lqw); + return deptMapper.selectDeptList(lqw); } /** @@ -84,7 +84,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { @Override public List> selectDeptTreeList(SysDeptBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - List depts = baseMapper.selectDeptList(lqw); + List depts = deptMapper.selectDeptList(lqw); return buildDeptTreeSelect(depts); } @@ -108,7 +108,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { if (ObjectUtil.isNotNull(bo.getBelongDeptId())) { //部门树搜索 builder.and(x -> { - List deptIds = baseMapper.selectDeptAndChildById(bo.getBelongDeptId()); + List deptIds = deptMapper.selectDeptAndChildById(bo.getBelongDeptId()); x.in(SysDept::getDeptId, deptIds); }); } @@ -148,7 +148,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { @Override public List selectDeptListByRoleId(Long roleId) { SysRole role = roleMapper.selectById(roleId); - return baseMapper.selectDeptListByRoleId(roleId, role.getDeptCheckStrictly()); + return deptMapper.selectDeptListByRoleId(roleId, role.getDeptCheckStrictly()); } /** @@ -160,11 +160,11 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { @Cacheable(cacheNames = CacheNames.SYS_DEPT, key = "#deptId") @Override public SysDeptVo selectDeptById(Long deptId) { - SysDeptVo dept = baseMapper.selectVoById(deptId); + SysDeptVo dept = deptMapper.selectVoById(deptId); if (ObjectUtil.isNull(dept)) { return null; } - SysDeptVo parentDept = baseMapper.lambda() + SysDeptVo parentDept = deptMapper.lambda() .select(SysDept::getDeptName) .eq(SysDept::getDeptId, dept.getParentId()) .voOne(); @@ -180,7 +180,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public List selectDeptByIds(Collection deptIds) { - return baseMapper.selectDeptList(baseMapper.lambda() + return deptMapper.selectDeptList(deptMapper.lambda() .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getLeader) .eq(SysDept::getStatus, SystemConstants.NORMAL) .in(CollUtil.isNotEmpty(deptIds), SysDept::getDeptId, deptIds) @@ -224,7 +224,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public List selectDeptsByList() { - List list = baseMapper.selectDeptList(baseMapper.lambda() + List list = deptMapper.selectDeptList(deptMapper.lambda() .select(SysDept::getDeptId, SysDept::getDeptName, SysDept::getParentId) .eq(SysDept::getStatus, SystemConstants.NORMAL) .build()); @@ -239,7 +239,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public long selectNormalChildrenDeptById(Long deptId) { - return baseMapper.lambda() + return deptMapper.lambda() .eq(SysDept::getStatus, SystemConstants.NORMAL) .findInSet(deptId, SysDept::getAncestors) .count(); @@ -253,7 +253,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public boolean hasChildByDeptId(Long deptId) { - return baseMapper.lambda().eq(SysDept::getParentId, deptId).exists(); + return deptMapper.lambda().eq(SysDept::getParentId, deptId).exists(); } /** @@ -275,7 +275,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { */ @Override public boolean checkDeptNameUnique(SysDeptBo dept) { - boolean exist = baseMapper.lambda() + boolean exist = deptMapper.lambda() .eq(SysDept::getDeptName, dept.getDeptName()) .eq(SysDept::getParentId, dept.getParentId()) .neIfPresent(SysDept::getDeptId, dept.getDeptId()) @@ -296,7 +296,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { if (LoginHelper.isSuperAdmin()) { return; } - if (baseMapper.countDeptById(deptId) == 0) { + if (deptMapper.countDeptById(deptId) == 0) { throw new ServiceException("没有权限访问部门数据!"); } } @@ -310,7 +310,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { @CacheEvict(cacheNames = CacheNames.SYS_DEPT_AND_CHILD, allEntries = true) @Override public int insertDept(SysDeptBo bo) { - SysDept info = baseMapper.selectById(bo.getParentId()); + SysDept info = deptMapper.selectById(bo.getParentId()); // 如果父节点不存在或不为正常状态,则不允许新增子节点 if (ObjectUtil.isNull(info)) { throw new ServiceException("父部门不存在"); @@ -320,7 +320,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { } SysDept dept = MapstructUtils.convert(bo, SysDept.class); dept.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + dept.getParentId()); - return baseMapper.insert(dept); + return deptMapper.insert(dept); } /** @@ -337,14 +337,14 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { @Transactional(rollbackFor = Exception.class) public int updateDept(SysDeptBo bo) { SysDept dept = MapstructUtils.convert(bo, SysDept.class); - SysDept oldDept = baseMapper.selectById(dept.getDeptId()); + SysDept oldDept = deptMapper.selectById(dept.getDeptId()); if (ObjectUtil.isNull(oldDept)) { throw new ServiceException("部门不存在,无法修改"); } if (!oldDept.getParentId().equals(dept.getParentId())) { // 如果是新父部门 则校验是否具有新父部门权限 避免越权 this.checkDeptDataScope(dept.getParentId()); - SysDept newParentDept = baseMapper.selectById(dept.getParentId()); + SysDept newParentDept = deptMapper.selectById(dept.getParentId()); if (ObjectUtil.isNotNull(newParentDept)) { String newAncestors = newParentDept.getAncestors() + StringUtils.SEPARATOR + newParentDept.getDeptId(); String oldAncestors = oldDept.getAncestors(); @@ -354,7 +354,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { } else { dept.setAncestors(oldDept.getAncestors()); } - int result = baseMapper.updateById(dept); + int result = deptMapper.updateById(dept); // 如果部门状态为启用,且部门祖级列表不为空,且部门祖级列表不等于根部门祖级列表(如果部门祖级列表不等于根部门祖级列表,则说明存在上级部门) if (SystemConstants.NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors()) @@ -373,7 +373,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { private void updateParentDeptStatusNormal(SysDept dept) { String ancestors = dept.getAncestors(); Long[] deptIds = Convert.toLongArray(ancestors); - baseMapper.lambda() + deptMapper.lambda() .set(SysDept::getStatus, SystemConstants.NORMAL) .in(SysDept::getDeptId, Arrays.asList(deptIds)) .update(); @@ -387,7 +387,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { * @param oldAncestors 旧的父ID集合 */ private void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors) { - List children = baseMapper.lambda().findInSet(deptId, SysDept::getAncestors).list(); + List children = deptMapper.lambda().findInSet(deptId, SysDept::getAncestors).list(); List list = new ArrayList<>(); for (SysDept child : children) { SysDept dept = new SysDept(); @@ -396,7 +396,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { list.add(dept); } if (CollUtil.isNotEmpty(list)) { - if (baseMapper.updateBatchById(list)) { + if (deptMapper.updateBatchById(list)) { list.forEach(dept -> CacheUtils.evict(CacheNames.SYS_DEPT, dept.getDeptId())); } } @@ -414,7 +414,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { }) @Override public int deleteDeptById(Long deptId) { - return baseMapper.deleteById(deptId); + return deptMapper.deleteById(deptId); } @@ -429,7 +429,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService { if (CollUtil.isEmpty(deptIds)) { return Collections.emptyMap(); } - List list = baseMapper.lambda() + List list = deptMapper.lambda() .select(SysDept::getDeptId, SysDept::getDeptName) .in(SysDept::getDeptId, deptIds) .list(); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java index 22af83a3f..1dba696a6 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictDataServiceImpl.java @@ -30,7 +30,7 @@ import java.util.List; @Service public class SysDictDataServiceImpl implements ISysDictDataService { - private final SysDictDataMapper baseMapper; + private final SysDictDataMapper dictDataMapper; /** * 分页查询字典数据列表 @@ -42,7 +42,7 @@ public class SysDictDataServiceImpl implements ISysDictDataService { @Override public PageResult selectPageDictDataList(SysDictDataBo dictData, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(dictData); - Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page page = dictDataMapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -55,7 +55,7 @@ public class SysDictDataServiceImpl implements ISysDictDataService { @Override public List selectDictDataList(SysDictDataBo dictData) { LambdaQueryWrapper lqw = buildQueryWrapper(dictData); - return baseMapper.selectVoList(lqw); + return dictDataMapper.selectVoList(lqw); } /** @@ -82,7 +82,7 @@ public class SysDictDataServiceImpl implements ISysDictDataService { */ @Override public String selectDictLabel(String dictType, String dictValue) { - return baseMapper.lambda() + return dictDataMapper.lambda() .select(SysDictData::getDictLabel) .eq(SysDictData::getDictType, dictType) .eq(SysDictData::getDictValue, dictValue) @@ -98,7 +98,7 @@ public class SysDictDataServiceImpl implements ISysDictDataService { */ @Override public SysDictDataVo selectDictDataById(Long dictCode) { - return baseMapper.selectVoById(dictCode); + return dictDataMapper.selectVoById(dictCode); } /** @@ -108,8 +108,8 @@ public class SysDictDataServiceImpl implements ISysDictDataService { */ @Override public void deleteDictDataByIds(Collection dictCodes) { - List list = baseMapper.selectByIds(dictCodes); - baseMapper.deleteByIds(dictCodes); + List list = dictDataMapper.selectByIds(dictCodes); + dictDataMapper.deleteByIds(dictCodes); list.forEach(x -> CacheUtils.evict(CacheNames.SYS_DICT, x.getDictType())); } @@ -123,9 +123,9 @@ public class SysDictDataServiceImpl implements ISysDictDataService { @Override public List insertDictData(SysDictDataBo bo) { SysDictData data = MapstructUtils.convert(bo, SysDictData.class); - int row = baseMapper.insert(data); + int row = dictDataMapper.insert(data); if (row > 0) { - return baseMapper.selectDictDataByType(data.getDictType()); + return dictDataMapper.selectDictDataByType(data.getDictType()); } throw new ServiceException("操作失败"); } @@ -140,9 +140,9 @@ public class SysDictDataServiceImpl implements ISysDictDataService { @Override public List updateDictData(SysDictDataBo bo) { SysDictData data = MapstructUtils.convert(bo, SysDictData.class); - int row = baseMapper.updateById(data); + int row = dictDataMapper.updateById(data); if (row > 0) { - return baseMapper.selectDictDataByType(data.getDictType()); + return dictDataMapper.selectDictDataByType(data.getDictType()); } throw new ServiceException("操作失败"); } @@ -155,7 +155,7 @@ public class SysDictDataServiceImpl implements ISysDictDataService { */ @Override public boolean checkDictDataUnique(SysDictDataBo dict) { - boolean exist = baseMapper.lambda() + boolean exist = dictDataMapper.lambda() .eq(SysDictData::getDictType, dict.getDictType()) .eq(SysDictData::getDictValue, dict.getDictValue()) .neIfPresent(SysDictData::getDictCode, dict.getDictCode()) diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java index d636fec91..7652efecd 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysDictTypeServiceImpl.java @@ -44,7 +44,7 @@ import java.util.stream.Collectors; @Service public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService { - private final SysDictTypeMapper baseMapper; + private final SysDictTypeMapper dictTypeMapper; private final SysDictDataMapper dictDataMapper; /** @@ -57,7 +57,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService @Override public PageResult selectPageDictTypeList(SysDictTypeBo dictType, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(dictType); - Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page page = dictTypeMapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -70,7 +70,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService @Override public List selectDictTypeList(SysDictTypeBo dictType) { LambdaQueryWrapper lqw = buildQueryWrapper(dictType); - return baseMapper.selectVoList(lqw); + return dictTypeMapper.selectVoList(lqw); } /** @@ -94,7 +94,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService */ @Override public List selectDictTypeAll() { - return baseMapper.selectVoList(); + return dictTypeMapper.selectVoList(); } /** @@ -118,7 +118,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService */ @Override public SysDictTypeVo selectDictTypeById(Long dictId) { - return baseMapper.selectVoById(dictId); + return dictTypeMapper.selectVoById(dictId); } /** @@ -130,7 +130,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService @Cacheable(cacheNames = CacheNames.SYS_DICT_TYPE, key = "#dictType") @Override public SysDictTypeVo selectDictTypeByType(String dictType) { - return baseMapper.lambda().eq(SysDictType::getDictType, dictType).voOne(); + return dictTypeMapper.lambda().eq(SysDictType::getDictType, dictType).voOne(); } /** @@ -140,14 +140,14 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService */ @Override public void deleteDictTypeByIds(Collection dictIds) { - List list = baseMapper.selectByIds(dictIds); + List list = dictTypeMapper.selectByIds(dictIds); list.forEach(x -> { boolean assigned = dictDataMapper.lambda().eq(SysDictData::getDictType, x.getDictType()).exists(); if (assigned) { throw new ServiceException("{}已分配,不能删除", x.getDictName()); } }); - baseMapper.deleteByIds(dictIds); + dictTypeMapper.deleteByIds(dictIds); list.forEach(x -> { CacheUtils.evict(CacheNames.SYS_DICT, x.getDictType()); CacheUtils.evict(CacheNames.SYS_DICT_TYPE, x.getDictType()); @@ -173,7 +173,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService @Override public List insertDictType(SysDictTypeBo bo) { SysDictType dict = MapstructUtils.convert(bo, SysDictType.class); - int row = baseMapper.insert(dict); + int row = dictTypeMapper.insert(dict); if (row > 0) { // 新增 type 下无 data 数据 返回空防止缓存穿透 return new ArrayList<>(); @@ -192,12 +192,12 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService @Transactional(rollbackFor = Exception.class) public List updateDictType(SysDictTypeBo bo) { SysDictType dict = MapstructUtils.convert(bo, SysDictType.class); - SysDictType oldDict = baseMapper.selectById(dict.getDictId()); + SysDictType oldDict = dictTypeMapper.selectById(dict.getDictId()); dictDataMapper.lambda() .set(SysDictData::getDictType, dict.getDictType()) .eq(SysDictData::getDictType, oldDict.getDictType()) .update(); - int row = baseMapper.updateById(dict); + int row = dictTypeMapper.updateById(dict); if (row > 0) { CacheUtils.evict(CacheNames.SYS_DICT, oldDict.getDictType()); CacheUtils.evict(CacheNames.SYS_DICT_TYPE, oldDict.getDictType()); @@ -214,7 +214,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService */ @Override public boolean checkDictTypeUnique(SysDictTypeBo dictType) { - boolean exist = baseMapper.lambda() + boolean exist = dictTypeMapper.lambda() .eq(SysDictType::getDictType, dictType.getDictType()) .neIfPresent(SysDictType::getDictId, dictType.getDictId()) .exists(); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLoginInfoServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLoginInfoServiceImpl.java index 7eae72fef..c8ae64de9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLoginInfoServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysLoginInfoServiceImpl.java @@ -43,7 +43,7 @@ import java.util.Map; @Service public class SysLoginInfoServiceImpl implements ISysLoginInfoService { - private final SysLoginInfoMapper baseMapper; + private final SysLoginInfoMapper loginInfoMapper; private final ISysClientService clientService; @@ -126,7 +126,7 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService { if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { lqw.orderByDesc(SysLoginInfo::getInfoId); } - Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page page = loginInfoMapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -139,7 +139,7 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService { public void insertLoginInfo(SysLoginInfoBo bo) { SysLoginInfo loginInfo = MapstructUtils.convert(bo, SysLoginInfo.class); loginInfo.setLoginTime(LocalDateTime.now()); - baseMapper.insert(loginInfo); + loginInfoMapper.insert(loginInfo); } /** @@ -150,7 +150,7 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService { */ @Override public List selectLoginInfoList(SysLoginInfoBo loginInfo) { - return baseMapper.selectVoList(buildQueryWrapper(loginInfo) + return loginInfoMapper.selectVoList(buildQueryWrapper(loginInfo) .orderByDesc(SysLoginInfo::getInfoId)); } @@ -162,7 +162,7 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService { */ private LambdaCrudChainWrapper buildQueryWrapper(SysLoginInfoBo loginInfo) { Map params = loginInfo.getParams(); - return baseMapper.lambda() + return loginInfoMapper.lambda() .likeIfText(SysLoginInfo::getIpaddr, loginInfo.getIpaddr()) .eqIfText(SysLoginInfo::getStatus, loginInfo.getStatus()) .likeIfText(SysLoginInfo::getUserName, loginInfo.getUserName()) @@ -177,7 +177,7 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService { */ @Override public int deleteLoginInfoByIds(Long[] infoIds) { - return baseMapper.deleteByIds(Arrays.asList(infoIds)); + return loginInfoMapper.deleteByIds(Arrays.asList(infoIds)); } /** @@ -185,6 +185,6 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService { */ @Override public void cleanLoginInfo() { - baseMapper.lambda().delete(); + loginInfoMapper.lambda().delete(); } } 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 ca5483110..1eace2e7a 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 @@ -37,7 +37,7 @@ import java.util.*; @Service public class SysMenuServiceImpl implements ISysMenuService { - private final SysMenuMapper baseMapper; + private final SysMenuMapper menuMapper; private final SysRoleMapper roleMapper; private final SysRoleMenuMapper roleMenuMapper; @@ -63,7 +63,7 @@ public class SysMenuServiceImpl implements ISysMenuService { public List selectMenuList(SysMenuBo menu, Long userId) { // 管理员显示所有菜单信息 不是管理员 按用户id过滤菜单 if (LoginHelper.isSuperAdmin(userId)) { - return baseMapper.lambda() + return menuMapper.lambda() .likeIfText(SysMenu::getMenuName, menu.getMenuName()) .eqIfText(SysMenu::getVisible, menu.getVisible()) .eqIfText(SysMenu::getStatus, menu.getStatus()) @@ -73,7 +73,7 @@ public class SysMenuServiceImpl implements ISysMenuService { .orderByAsc(SysMenu::getOrderNum) .voList(); } - return baseMapper.selectMenuListByUserId(menu, userId); + return menuMapper.selectMenuListByUserId(menu, userId); } /** @@ -84,7 +84,7 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public Set selectMenuPermsByUserId(Long userId) { - return baseMapper.selectMenuPermsByUserId(userId); + return menuMapper.selectMenuPermsByUserId(userId); } /** @@ -95,7 +95,7 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public Set selectMenuPermsByRoleId(Long roleId) { - return baseMapper.selectMenuPermsByRoleId(roleId); + return menuMapper.selectMenuPermsByRoleId(roleId); } /** @@ -106,7 +106,7 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public Map> selectMenuPermsByRoleIds(Collection roleIds) { - return baseMapper.selectMenuPermsByRoleIds(roleIds); + return menuMapper.selectMenuPermsByRoleIds(roleIds); } /** @@ -119,9 +119,9 @@ public class SysMenuServiceImpl implements ISysMenuService { public List selectMenuTreeByUserId(Long userId) { List menus; if (LoginHelper.isSuperAdmin(userId)) { - menus = baseMapper.selectMenuTreeAll(); + menus = menuMapper.selectMenuTreeAll(); } else { - menus = baseMapper.selectMenuTreeByUserId(userId); + menus = menuMapper.selectMenuTreeByUserId(userId); } return TreeBuildUtils.build(menus, Constants.TOP_PARENT_ID, SysMenu::getParentId, (menu, nodeTreeMaps) -> { @@ -145,7 +145,7 @@ public class SysMenuServiceImpl implements ISysMenuService { @Override public List selectMenuListByRoleId(Long roleId) { SysRole role = roleMapper.selectById(roleId); - return baseMapper.selectMenuListByRoleId(roleId, role.getMenuCheckStrictly()); + return menuMapper.selectMenuListByRoleId(roleId, role.getMenuCheckStrictly()); } /** @@ -234,7 +234,7 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public SysMenuVo selectMenuById(Long menuId) { - return baseMapper.selectVoById(menuId); + return menuMapper.selectVoById(menuId); } /** @@ -245,7 +245,7 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public boolean hasChildByMenuId(Long menuId) { - return baseMapper.lambda().eq(SysMenu::getParentId, menuId).exists(); + return menuMapper.lambda().eq(SysMenu::getParentId, menuId).exists(); } /** @@ -256,7 +256,7 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public boolean hasChildByMenuId(Collection menuIds) { - return baseMapper.lambda() + return menuMapper.lambda() .in(SysMenu::getParentId, menuIds) .notIn(SysMenu::getMenuId, menuIds) .exists(); @@ -282,7 +282,7 @@ public class SysMenuServiceImpl implements ISysMenuService { @Override public int insertMenu(SysMenuBo bo) { SysMenu menu = MapstructUtils.convert(bo, SysMenu.class); - return baseMapper.insert(menu); + return menuMapper.insert(menu); } /** @@ -294,7 +294,7 @@ public class SysMenuServiceImpl implements ISysMenuService { @Override public int updateMenu(SysMenuBo bo) { SysMenu menu = MapstructUtils.convert(bo, SysMenu.class); - return baseMapper.updateById(menu); + return menuMapper.updateById(menu); } /** @@ -305,7 +305,7 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public int deleteMenuById(Long menuId) { - return baseMapper.deleteById(menuId); + return menuMapper.deleteById(menuId); } /** @@ -316,7 +316,7 @@ public class SysMenuServiceImpl implements ISysMenuService { @Override @Transactional(rollbackFor = Exception.class) public void deleteMenuById(Collection menuIds) { - baseMapper.deleteByIds(menuIds); + menuMapper.deleteByIds(menuIds); roleMenuMapper.deleteByMenuIds(menuIds); } @@ -328,7 +328,7 @@ public class SysMenuServiceImpl implements ISysMenuService { */ @Override public boolean checkMenuNameUnique(SysMenuBo menu) { - boolean exist = baseMapper.lambda() + boolean exist = menuMapper.lambda() .eq(SysMenu::getMenuName, menu.getMenuName()) .eq(SysMenu::getParentId, menu.getParentId()) .neIfPresent(SysMenu::getMenuId, menu.getMenuId()) @@ -352,7 +352,7 @@ public class SysMenuServiceImpl implements ISysMenuService { Long parentId = menu.getParentId(); String path = menu.getPath(); String routeName = StringUtils.isEmpty(menu.getRouteName()) ? path : menu.getRouteName(); - List sysMenuList = baseMapper.lambda() + 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); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java index 4fab4dafd..9c8f2f19e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java @@ -63,7 +63,7 @@ public class SysMessageServiceImpl implements ISysMessageService, MessageService */ private static final long BOX_DAYS = 30L; - private final SysMessageMapper baseMapper; + private final SysMessageMapper messageMapper; /** * 查询当前用户消息盒子数据 @@ -190,7 +190,7 @@ public class SysMessageServiceImpl implements ISysMessageService, MessageService return payload; } SysMessage message = buildMessage(userIds, payload); - baseMapper.insert(message); + messageMapper.insert(message); payload.setMessageId(message.getMessageId()); return payload; } @@ -204,7 +204,7 @@ public class SysMessageServiceImpl implements ISysMessageService, MessageService * @return 消息VO列表 */ private List selectMessageList(String category, Long userId) { - List list = baseMapper.lambda() + List list = messageMapper.lambda() .eq(SysMessage::getCategory, category) // 仅查询30天内消息 .ge(SysMessage::getCreateTime, LocalDateTime.now().minusDays(BOX_DAYS)) diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java index 977a5c0bd..f48eb566b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysNoticeServiceImpl.java @@ -32,7 +32,7 @@ import java.util.List; @Service public class SysNoticeServiceImpl implements ISysNoticeService { - private final SysNoticeMapper baseMapper; + private final SysNoticeMapper noticeMapper; private final SysUserMapper userMapper; /** @@ -45,7 +45,7 @@ public class SysNoticeServiceImpl implements ISysNoticeService { @Override public PageResult selectPageNoticeList(SysNoticeBo notice, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(notice); - Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page page = noticeMapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -57,7 +57,7 @@ public class SysNoticeServiceImpl implements ISysNoticeService { */ @Override public SysNoticeVo selectNoticeById(Long noticeId) { - return baseMapper.selectVoById(noticeId); + return noticeMapper.selectVoById(noticeId); } /** @@ -69,7 +69,7 @@ public class SysNoticeServiceImpl implements ISysNoticeService { @Override public List selectNoticeList(SysNoticeBo notice) { LambdaQueryWrapper lqw = buildQueryWrapper(notice); - return baseMapper.selectVoList(lqw); + return noticeMapper.selectVoList(lqw); } /** @@ -98,7 +98,7 @@ public class SysNoticeServiceImpl implements ISysNoticeService { @Override public int insertNotice(SysNoticeBo bo) { SysNotice notice = MapstructUtils.convert(bo, SysNotice.class); - int rows = baseMapper.insert(notice); + int rows = noticeMapper.insert(notice); bo.setNoticeId(notice.getNoticeId()); return rows; } @@ -112,7 +112,7 @@ public class SysNoticeServiceImpl implements ISysNoticeService { @Override public int updateNotice(SysNoticeBo bo) { SysNotice notice = MapstructUtils.convert(bo, SysNotice.class); - return baseMapper.updateById(notice); + return noticeMapper.updateById(notice); } /** @@ -123,7 +123,7 @@ public class SysNoticeServiceImpl implements ISysNoticeService { */ @Override public int deleteNoticeById(Long noticeId) { - return baseMapper.deleteById(noticeId); + return noticeMapper.deleteById(noticeId); } /** @@ -134,6 +134,6 @@ public class SysNoticeServiceImpl implements ISysNoticeService { */ @Override public int deleteNoticeByIds(Long[] noticeIds) { - return baseMapper.deleteByIds(Arrays.asList(noticeIds)); + return noticeMapper.deleteByIds(Arrays.asList(noticeIds)); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java index 6c8bd86c7..153c5d997 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOperLogServiceImpl.java @@ -34,7 +34,7 @@ import java.util.Map; @Service public class SysOperLogServiceImpl implements ISysOperLogService { - private final SysOperLogMapper baseMapper; + private final SysOperLogMapper operLogMapper; /** * 操作日志记录 @@ -63,7 +63,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService { if (StringUtils.isBlank(pageQuery.getOrderByColumn())) { lqw.orderByDesc(SysOperLog::getOperId); } - Page page = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page page = operLogMapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -106,7 +106,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService { public void insertOperlog(SysOperLogBo bo) { SysOperLog operLog = MapstructUtils.convert(bo, SysOperLog.class); operLog.setOperTime(LocalDateTime.now()); - baseMapper.insert(operLog); + operLogMapper.insert(operLog); } /** @@ -118,7 +118,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService { @Override public List selectOperLogList(SysOperLogBo operLog) { LambdaQueryWrapper lqw = buildQueryWrapper(operLog); - return baseMapper.selectVoList(lqw.orderByDesc(SysOperLog::getOperId)); + return operLogMapper.selectVoList(lqw.orderByDesc(SysOperLog::getOperId)); } /** @@ -129,7 +129,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService { */ @Override public int deleteOperLogByIds(Long[] operIds) { - return baseMapper.deleteByIds(Arrays.asList(operIds)); + return operLogMapper.deleteByIds(Arrays.asList(operIds)); } /** @@ -140,7 +140,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService { */ @Override public SysOperLogVo selectOperLogById(Long operId) { - return baseMapper.selectVoById(operId); + return operLogMapper.selectVoById(operId); } /** @@ -148,6 +148,6 @@ public class SysOperLogServiceImpl implements ISysOperLogService { */ @Override public void cleanOperLog() { - baseMapper.lambda().delete(); + operLogMapper.lambda().delete(); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java index 985301940..26773808b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java @@ -42,14 +42,14 @@ import java.util.List; @Service public class SysOssConfigServiceImpl implements ISysOssConfigService { - private final SysOssConfigMapper baseMapper; + private final SysOssConfigMapper ossConfigMapper; /** * 项目启动时,初始化参数到缓存,加载配置类 */ @Override public void init() { - List list = baseMapper.selectList(); + List list = ossConfigMapper.selectList(); // 加载OSS初始化配置 for (SysOssConfig config : list) { String configKey = config.getConfigKey(); @@ -68,7 +68,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { */ @Override public SysOssConfigVo queryById(Long ossConfigId) { - return baseMapper.selectVoById(ossConfigId); + return ossConfigMapper.selectVoById(ossConfigId); } /** @@ -81,7 +81,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { @Override public PageResult queryPageList(SysOssConfigBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page result = ossConfigMapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(result.getRecords(), result.getTotal()); } @@ -111,10 +111,10 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { public Boolean insertByBo(SysOssConfigBo bo) { SysOssConfig config = MapstructUtils.convert(bo, SysOssConfig.class); validEntityBeforeSave(config); - boolean flag = baseMapper.insert(config) > 0; + boolean flag = ossConfigMapper.insert(config) > 0; if (flag) { // 从数据库查询完整的数据做缓存 - config = baseMapper.selectById(config.getOssConfigId()); + config = ossConfigMapper.selectById(config.getOssConfigId()); CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); } return flag; @@ -130,7 +130,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { public Boolean updateByBo(SysOssConfigBo bo) { SysOssConfig config = MapstructUtils.convert(bo, SysOssConfig.class); validEntityBeforeSave(config); - boolean flag = baseMapper.lambda() + boolean flag = ossConfigMapper.lambda() .set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, "") .set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, "") .set(ObjectUtil.isNull(config.getExt1()), SysOssConfig::getExt1, "") @@ -139,7 +139,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { .update(config); if (flag) { // 从数据库查询完整的数据做缓存 - config = baseMapper.selectById(config.getOssConfigId()); + config = ossConfigMapper.selectById(config.getOssConfigId()); CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); } return flag; @@ -173,10 +173,10 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { } List list = CollUtil.newArrayList(); for (Long configId : ids) { - SysOssConfig config = baseMapper.selectById(configId); + SysOssConfig config = ossConfigMapper.selectById(configId); list.add(config); } - boolean flag = baseMapper.deleteByIds(ids) > 0; + boolean flag = ossConfigMapper.deleteByIds(ids) > 0; if (flag) { list.forEach(sysOssConfig -> CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey())); @@ -192,7 +192,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { */ private boolean checkConfigKeyUnique(SysOssConfig sysOssConfig) { long ossConfigId = ObjectUtils.notNull(sysOssConfig.getOssConfigId(), -1L); - SysOssConfig info = baseMapper.lambda() + SysOssConfig info = ossConfigMapper.lambda() .select(SysOssConfig::getOssConfigId, SysOssConfig::getConfigKey) .eq(SysOssConfig::getConfigKey, sysOssConfig.getConfigKey()) .one(); @@ -212,8 +212,8 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { @Transactional(rollbackFor = Exception.class) public int updateOssConfigStatus(SysOssConfigBo bo) { SysOssConfig sysOssConfig = MapstructUtils.convert(bo, SysOssConfig.class); - int row = baseMapper.lambda().set(SysOssConfig::getStatus, SystemConstants.NO).updateCount(); - row += baseMapper.updateById(sysOssConfig); + int row = ossConfigMapper.lambda().set(SysOssConfig::getStatus, SystemConstants.NO).updateCount(); + row += ossConfigMapper.updateById(sysOssConfig); if (row > 0) { RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, sysOssConfig.getConfigKey()); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java index 7ad27d599..69cee9e22 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java @@ -59,7 +59,7 @@ import java.util.Map; @Service public class SysOssServiceImpl implements ISysOssService, OssService { - private final SysOssMapper baseMapper; + private final SysOssMapper ossMapper; /** * 查询OSS对象存储列表 @@ -71,7 +71,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { @Override public PageResult queryPageList(SysOssBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page result = ossMapper.selectVoPage(pageQuery.build(), lqw); List filterResult = StreamUtils.toList(result.getRecords(), this::matchingUrl); result.setRecords(filterResult); return PageResult.build(result.getRecords(), result.getTotal()); @@ -178,7 +178,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { @Cacheable(cacheNames = CacheNames.SYS_OSS, key = "#ossId") @Override public SysOssVo getById(Long ossId) { - return baseMapper.selectVoById(ossId); + return ossMapper.selectVoById(ossId); } @@ -284,7 +284,7 @@ public class SysOssServiceImpl implements ISysOssService, OssService { oss.setOriginalName(originalfileName); oss.setService(configKey); oss.setExt1(JsonUtils.toJsonString(ext1)); - baseMapper.insert(oss); + ossMapper.insert(oss); SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class); return this.matchingUrl(sysOssVo); } @@ -301,11 +301,11 @@ public class SysOssServiceImpl implements ISysOssService, OssService { if (isValid) { // 做一些业务上的校验,判断是否需要校验 } - List list = baseMapper.selectByIds(ids); + List list = ossMapper.selectByIds(ids); for (SysOss sysOss : list) { OssFactory.instance(sysOss.getService()).delete(sysOss.getFileName()); } - return baseMapper.deleteByIds(ids) > 0; + return ossMapper.deleteByIds(ids) > 0; } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java index 924fe2932..e811a798f 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPostServiceImpl.java @@ -36,7 +36,7 @@ import java.util.Map; @Service public class SysPostServiceImpl implements ISysPostService, PostService { - private final SysPostMapper baseMapper; + private final SysPostMapper postMapper; private final SysDeptMapper deptMapper; private final SysUserPostMapper userPostMapper; @@ -49,7 +49,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public PageResult selectPagePostList(SysPostBo post, PageQuery pageQuery) { - Page page = baseMapper.selectPagePostList(pageQuery.build(), buildQueryWrapper(post)); + Page page = postMapper.selectPagePostList(pageQuery.build(), buildQueryWrapper(post)); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -61,7 +61,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public List selectPostList(SysPostBo post) { - return baseMapper.selectVoList(buildQueryWrapper(post)); + return postMapper.selectVoList(buildQueryWrapper(post)); } /** @@ -72,7 +72,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public List selectPostsByUserId(Long userId) { - return baseMapper.selectPostsByUserId(userId); + return postMapper.selectPostsByUserId(userId); } /** @@ -83,7 +83,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ private Wrapper buildQueryWrapper(SysPostBo bo) { Map params = bo.getParams(); - var wrapper = baseMapper.lambda() + var wrapper = postMapper.lambda() .likeIfText(SysPost::getPostCode, bo.getPostCode()) .likeIfText(SysPost::getPostCategory, bo.getPostCategory()) .likeIfText(SysPost::getPostName, bo.getPostName()) @@ -110,7 +110,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public List selectPostAll() { - return baseMapper.lambda().voList(); + return postMapper.lambda().voList(); } /** @@ -121,7 +121,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public SysPostVo selectPostById(Long postId) { - return baseMapper.selectVoById(postId); + return postMapper.selectVoById(postId); } /** @@ -132,7 +132,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public List selectPostListByUserId(Long userId) { - List list = baseMapper.selectPostsByUserId(userId); + List list = postMapper.selectPostsByUserId(userId); return StreamUtils.toList(list, SysPostVo::getPostId); } @@ -144,7 +144,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public List selectPostByIds(Collection postIds) { - return baseMapper.lambda() + return postMapper.lambda() .select(SysPost::getPostId, SysPost::getPostName, SysPost::getPostCode) .eq(SysPost::getStatus, SystemConstants.NORMAL) .in(CollUtil.isNotEmpty(postIds), SysPost::getPostId, postIds) @@ -159,7 +159,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public boolean checkPostNameUnique(SysPostBo post) { - boolean exist = baseMapper.lambda() + boolean exist = postMapper.lambda() .eq(SysPost::getPostName, post.getPostName()) .eq(SysPost::getDeptId, post.getDeptId()) .neIfPresent(SysPost::getPostId, post.getPostId()) @@ -175,7 +175,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public boolean checkPostCodeUnique(SysPostBo post) { - boolean exist = baseMapper.lambda() + boolean exist = postMapper.lambda() .eq(SysPost::getPostCode, post.getPostCode()) .neIfPresent(SysPost::getPostId, post.getPostId()) .exists(); @@ -201,7 +201,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public long countPostByDeptId(Long deptId) { - return baseMapper.lambda().eq(SysPost::getDeptId, deptId).count(); + return postMapper.lambda().eq(SysPost::getDeptId, deptId).count(); } /** @@ -212,7 +212,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public int deletePostById(Long postId) { - return baseMapper.deleteById(postId); + return postMapper.deleteById(postId); } /** @@ -223,13 +223,13 @@ public class SysPostServiceImpl implements ISysPostService, PostService { */ @Override public int deletePostByIds(Collection postIds) { - List list = baseMapper.selectByIds(postIds); + List list = postMapper.selectByIds(postIds); for (SysPost post : list) { if (this.countUserPostById(post.getPostId()) > 0) { throw new ServiceException("{}已分配,不能删除!", post.getPostName()); } } - return baseMapper.deleteByIds(postIds); + return postMapper.deleteByIds(postIds); } /** @@ -241,7 +241,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { @Override public int insertPost(SysPostBo bo) { SysPost post = MapstructUtils.convert(bo, SysPost.class); - return baseMapper.insert(post); + return postMapper.insert(post); } /** @@ -253,7 +253,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { @Override public int updatePost(SysPostBo bo) { SysPost post = MapstructUtils.convert(bo, SysPost.class); - return baseMapper.updateById(post); + return postMapper.updateById(post); } /** @@ -267,7 +267,7 @@ public class SysPostServiceImpl implements ISysPostService, PostService { if (CollUtil.isEmpty(postIds)) { return Collections.emptyMap(); } - List list = baseMapper.lambda() + List list = postMapper.lambda() .select(SysPost::getPostId, SysPost::getPostName) .in(SysPost::getPostId, postIds) .list(); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java index 7f90187b5..0b59bf510 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java @@ -46,7 +46,7 @@ import java.util.*; @Service public class SysRoleServiceImpl implements ISysRoleService, RoleService { - private final SysRoleMapper baseMapper; + private final SysRoleMapper roleMapper; private final SysRoleMenuMapper roleMenuMapper; private final SysUserRoleMapper userRoleMapper; private final SysRoleDeptMapper roleDeptMapper; @@ -60,7 +60,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public PageResult selectPageRoleList(SysRoleBo role, PageQuery pageQuery) { - Page page = baseMapper.selectPageRoleList(pageQuery.build(), this.buildQueryWrapper(role)); + Page page = roleMapper.selectPageRoleList(pageQuery.build(), this.buildQueryWrapper(role)); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -72,7 +72,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public List selectRoleList(SysRoleBo role) { - return baseMapper.selectRoleList(this.buildQueryWrapper(role)); + return roleMapper.selectRoleList(this.buildQueryWrapper(role)); } /** @@ -101,7 +101,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public List selectRolesByUserId(Long userId) { - return baseMapper.selectRolesByUserId(userId); + return roleMapper.selectRolesByUserId(userId); } /** @@ -112,7 +112,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public List selectRolesAuthByUserId(Long userId) { - List userRoles = baseMapper.selectRolesByUserId(userId); + List userRoles = roleMapper.selectRolesByUserId(userId); List roles = selectRoleAll(); // 使用HashSet提高查找效率 Set userRoleIds = StreamUtils.toSet(userRoles, SysRoleVo::getRoleId); @@ -132,7 +132,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public Set selectRolePermissionByUserId(Long userId) { - List perms = baseMapper.selectRolesByUserId(userId); + List perms = roleMapper.selectRolesByUserId(userId); Set permsSet = new HashSet<>(); for (SysRoleVo perm : perms) { if (ObjectUtil.isNotNull(perm)) { @@ -160,7 +160,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public List selectRoleListByUserId(Long userId) { - List list = baseMapper.selectRolesByUserId(userId); + List list = roleMapper.selectRolesByUserId(userId); return StreamUtils.toList(list, SysRoleVo::getRoleId); } @@ -172,7 +172,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public SysRoleVo selectRoleById(Long roleId) { - return baseMapper.selectRoleById(roleId); + return roleMapper.selectRoleById(roleId); } /** @@ -183,7 +183,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public List selectRoleByIds(Collection roleIds) { - return baseMapper.selectRoleList(baseMapper.lambda() + return roleMapper.selectRoleList(roleMapper.lambda() .eq(SysRole::getStatus, SystemConstants.NORMAL) .inIfNotEmpty(SysRole::getRoleId, roleIds) .build()); @@ -197,7 +197,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public boolean checkRoleNameUnique(SysRoleBo role) { - boolean exist = baseMapper.lambda() + boolean exist = roleMapper.lambda() .eq(SysRole::getRoleName, role.getRoleName()) .neIfPresent(SysRole::getRoleId, role.getRoleId()) .exists(); @@ -212,7 +212,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { */ @Override public boolean checkRoleKeyUnique(SysRoleBo role) { - boolean exist = baseMapper.lambda() + boolean exist = roleMapper.lambda() .eq(SysRole::getRoleKey, role.getRoleKey()) .neIfPresent(SysRole::getRoleId, role.getRoleId()) .exists(); @@ -237,7 +237,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { } // 修改不允许修改 管理员标识符 if (ObjectUtil.isNotNull(role.getRoleId())) { - SysRole sysRole = baseMapper.selectById(role.getRoleId()); + SysRole sysRole = roleMapper.selectById(role.getRoleId()); // 如果标识符不相等 判断为修改了管理员标识符 if (!StringUtils.equals(sysRole.getRoleKey(), role.getRoleKey())) { if (StringUtils.equalsAny(sysRole.getRoleKey(), keys)) { @@ -272,7 +272,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { if (CollUtil.isEmpty(roleIds) || LoginHelper.isSuperAdmin()) { return; } - long count = baseMapper.selectRoleCount(roleIds); + long count = roleMapper.selectRoleCount(roleIds); if (count != roleIds.size()) { throw new ServiceException("没有权限访问部分角色数据!"); } @@ -300,7 +300,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { public int insertRole(SysRoleBo bo) { SysRole role = MapstructUtils.convert(bo, SysRole.class); // 新增角色信息 - baseMapper.insert(role); + roleMapper.insert(role); bo.setRoleId(role.getRoleId()); return insertRoleMenu(bo); } @@ -320,7 +320,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { throw new ServiceException("角色已分配,不能禁用!"); } // 仅更新角色基础字段,避免影响权限分配。 - return baseMapper.updateById(role); + return roleMapper.updateById(role); } /** @@ -335,7 +335,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { public int updateRolePermission(SysRoleBo bo) { SysRole role = MapstructUtils.convert(bo, SysRole.class); // 更新权限相关配置字段(数据范围、树联动)。 - baseMapper.updateById(role); + roleMapper.updateById(role); // 先清理旧菜单权限,再重建。 roleMenuMapper.lambda().eq(SysRoleMenu::getRoleId, role.getRoleId()).delete(); insertRoleMenu(bo); @@ -356,7 +356,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { if (SystemConstants.DISABLE.equals(status) && this.countUserRoleByRoleId(roleId) > 0) { throw new ServiceException("角色已分配,不能禁用!"); } - return baseMapper.lambda() + return roleMapper.lambda() .set(SysRole::getStatus, status) .eq(SysRole::getRoleId, roleId) .updateCount(); @@ -420,7 +420,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { roleMenuMapper.lambda().eq(SysRoleMenu::getRoleId, roleId).delete(); // 删除角色与部门关联 roleDeptMapper.lambda().eq(SysRoleDept::getRoleId, roleId).delete(); - return baseMapper.deleteById(roleId); + return roleMapper.deleteById(roleId); } /** @@ -434,7 +434,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { @Transactional(rollbackFor = Exception.class) public int deleteRoleByIds(Collection roleIds) { this.checkRoleDataScope(roleIds); - List roles = baseMapper.selectByIds(roleIds); + List roles = roleMapper.selectByIds(roleIds); for (SysRole role : roles) { checkRoleAllowed(BeanUtil.toBean(role, SysRoleBo.class)); if (countUserRoleByRoleId(role.getRoleId()) > 0) { @@ -445,7 +445,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { roleMenuMapper.lambda().in(SysRoleMenu::getRoleId, roleIds).delete(); // 删除角色与部门关联 roleDeptMapper.lambda().in(SysRoleDept::getRoleId, roleIds).delete(); - return baseMapper.deleteByIds(roleIds); + return roleMapper.deleteByIds(roleIds); } /** @@ -609,7 +609,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { if (CollUtil.isEmpty(roleIds)) { return Collections.emptyMap(); } - List list = baseMapper.lambda() + List list = roleMapper.lambda() .select(SysRole::getRoleId, SysRole::getRoleName) .in(SysRole::getRoleId, roleIds) .list(); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java index b58b1d187..b03870f3e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysSocialServiceImpl.java @@ -21,7 +21,7 @@ import java.util.List; @Service public class SysSocialServiceImpl implements ISysSocialService { - private final SysSocialMapper baseMapper; + private final SysSocialMapper socialMapper; /** @@ -32,7 +32,7 @@ public class SysSocialServiceImpl implements ISysSocialService { */ @Override public SysSocialVo queryById(String id) { - return baseMapper.selectVoById(id); + return socialMapper.selectVoById(id); } /** @@ -43,7 +43,7 @@ public class SysSocialServiceImpl implements ISysSocialService { */ @Override public List queryList(SysSocialBo bo) { - return baseMapper.lambda() + return socialMapper.lambda() .eqIfPresent(SysSocial::getUserId, bo.getUserId()) .eqIfText(SysSocial::getAuthId, bo.getAuthId()) .eqIfText(SysSocial::getSource, bo.getSource()) @@ -58,7 +58,7 @@ public class SysSocialServiceImpl implements ISysSocialService { */ @Override public List queryListByUserId(Long userId) { - return baseMapper.lambda().eq(SysSocial::getUserId, userId).voList(); + return socialMapper.lambda().eq(SysSocial::getUserId, userId).voList(); } @@ -72,7 +72,7 @@ public class SysSocialServiceImpl implements ISysSocialService { public Boolean insertByBo(SysSocialBo bo) { SysSocial add = MapstructUtils.convert(bo, SysSocial.class); validEntityBeforeSave(add); - boolean flag = baseMapper.insert(add) > 0; + boolean flag = socialMapper.insert(add) > 0; if (flag) { if (add != null) { bo.setId(add.getId()); @@ -93,7 +93,7 @@ public class SysSocialServiceImpl implements ISysSocialService { public Boolean updateByBo(SysSocialBo bo) { SysSocial update = MapstructUtils.convert(bo, SysSocial.class); validEntityBeforeSave(update); - return baseMapper.updateById(update) > 0; + return socialMapper.updateById(update) > 0; } /** @@ -114,7 +114,7 @@ public class SysSocialServiceImpl implements ISysSocialService { */ @Override public Boolean deleteWithValidById(Long id) { - return baseMapper.deleteById(id) > 0; + return socialMapper.deleteById(id) > 0; } @@ -126,7 +126,7 @@ public class SysSocialServiceImpl implements ISysSocialService { */ @Override public List selectByAuthId(String authId) { - return baseMapper.lambda().eq(SysSocial::getAuthId, authId).voList(); + return socialMapper.lambda().eq(SysSocial::getAuthId, authId).voList(); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index 52a09c868..f49086482 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -47,7 +47,7 @@ import java.util.*; @Service public class SysUserServiceImpl implements ISysUserService, UserService { - private final SysUserMapper baseMapper; + private final SysUserMapper userMapper; private final SysDeptMapper deptMapper; private final SysRoleMapper roleMapper; private final SysPostMapper postMapper; @@ -63,7 +63,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public PageResult selectPageUserList(SysUserBo user, PageQuery pageQuery) { - Page page = baseMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapper(user)); + Page page = userMapper.selectPageUserList(pageQuery.build(), this.buildQueryWrapper(user)); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -76,7 +76,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public List selectUserExportList(SysUserBo user) { List deptIds = ObjectUtil.isNotNull(user.getDeptId()) ? deptMapper.selectDeptAndChildById(user.getDeptId()) : null; - return baseMapper.selectUserExportList(user, deptIds); + return userMapper.selectUserExportList(user, deptIds); } /** @@ -116,7 +116,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public PageResult selectAllocatedList(SysUserBo user, PageQuery pageQuery) { - Page page = baseMapper.selectAllocatedList(pageQuery.build(), user); + Page page = userMapper.selectAllocatedList(pageQuery.build(), user); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -129,7 +129,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override public PageResult selectUnallocatedList(SysUserBo user, PageQuery pageQuery) { List userIds = userRoleMapper.selectUserIdsByRoleId(user.getRoleId()); - Page page = baseMapper.selectUnallocatedList(pageQuery.build(), user, userIds); + Page page = userMapper.selectUnallocatedList(pageQuery.build(), user, userIds); return PageResult.build(page.getRecords(), page.getTotal()); } @@ -141,7 +141,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserByUserName(String userName) { - return baseMapper.lambda().eq(SysUser::getUserName, userName).voOne(); + return userMapper.lambda().eq(SysUser::getUserName, userName).voOne(); } /** @@ -152,7 +152,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserByPhoneNumber(String phoneNumber) { - return baseMapper.lambda().eq(SysUser::getPhoneNumber, phoneNumber).voOne(); + return userMapper.lambda().eq(SysUser::getPhoneNumber, phoneNumber).voOne(); } /** @@ -163,7 +163,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public SysUserVo selectUserById(Long userId) { - SysUserVo user = baseMapper.selectVoById(userId); + SysUserVo user = userMapper.selectVoById(userId); if (ObjectUtil.isNull(user)) { return user; } @@ -180,7 +180,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public List selectUserByIds(Collection userIds, Long deptId) { - return baseMapper.selectUserList(baseMapper.lambda() + return userMapper.selectUserList(userMapper.lambda() .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName) .eq(SysUser::getStatus, SystemConstants.NORMAL) .eqIfPresent(SysUser::getDeptId, deptId) @@ -226,7 +226,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public boolean checkUserNameUnique(SysUserBo user) { - boolean exist = baseMapper.lambda() + boolean exist = userMapper.lambda() .eq(SysUser::getUserName, user.getUserName()) .neIfPresent(SysUser::getUserId, user.getUserId()) .exists(); @@ -240,7 +240,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public boolean checkPhoneUnique(SysUserBo user) { - boolean exist = baseMapper.lambda() + boolean exist = userMapper.lambda() .eq(SysUser::getPhoneNumber, user.getPhoneNumber()) .neIfPresent(SysUser::getUserId, user.getUserId()) .exists(); @@ -254,7 +254,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public boolean checkEmailUnique(SysUserBo user) { - boolean exist = baseMapper.lambda() + boolean exist = userMapper.lambda() .eq(SysUser::getEmail, user.getEmail()) .neIfPresent(SysUser::getUserId, user.getUserId()) .exists(); @@ -286,7 +286,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (LoginHelper.isSuperAdmin()) { return; } - if (baseMapper.countUserById(userId) == 0) { + if (userMapper.countUserById(userId) == 0) { throw new ServiceException("没有权限访问用户数据!"); } } @@ -302,7 +302,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { public int insertUser(SysUserBo user) { SysUser sysUser = MapstructUtils.convert(user, SysUser.class); // 新增用户信息 - int rows = baseMapper.insert(sysUser); + int rows = userMapper.insert(sysUser); user.setUserId(sysUser.getUserId()); // 新增用户岗位关联 insertUserPost(user, false); @@ -322,7 +322,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { user.setCreateBy(0L); user.setUpdateBy(0L); SysUser sysUser = MapstructUtils.convert(user, SysUser.class); - return baseMapper.insert(sysUser) > 0; + return userMapper.insert(sysUser) > 0; } /** @@ -341,7 +341,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { insertUserPost(user, true); SysUser sysUser = MapstructUtils.convert(user, SysUser.class); // 防止错误更新后导致的数据误删除 - int flag = baseMapper.updateById(sysUser); + int flag = userMapper.updateById(sysUser); if (flag < 1) { throw new ServiceException("修改用户{}信息失败", user.getUserName()); } @@ -369,7 +369,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public int updateUserStatus(Long userId, String status) { - return baseMapper.lambda() + return userMapper.lambda() .set(SysUser::getStatus, status) .eq(SysUser::getUserId, userId) .updateCount(); @@ -384,7 +384,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @CacheEvict(cacheNames = CacheNames.SYS_NICKNAME, key = "#user.userId") @Override public int updateUserProfile(SysUserBo user) { - return baseMapper.lambda() + return userMapper.lambda() .setIfPresent(SysUser::getNickName, user.getNickName()) .set(SysUser::getPhoneNumber, user.getPhoneNumber()) .set(SysUser::getEmail, user.getEmail()) @@ -402,7 +402,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public boolean updateUserAvatar(Long userId, Long avatar) { - return baseMapper.lambda() + return userMapper.lambda() .set(SysUser::getAvatar, avatar) .eq(SysUser::getUserId, userId) .update(); @@ -417,7 +417,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public int resetUserPwd(Long userId, String password) { - return baseMapper.lambda() + return userMapper.lambda() .set(SysUser::getPassword, password) .eq(SysUser::getUserId, userId) .updateCount(); @@ -526,7 +526,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { // 删除用户与岗位表 userPostMapper.lambda().eq(SysUserPost::getUserId, userId).delete(); // 防止更新失败导致的数据删除 - int flag = baseMapper.deleteById(userId); + int flag = userMapper.deleteById(userId); if (flag < 1) { throw new ServiceException("删除用户失败!"); } @@ -552,7 +552,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { // 删除用户与岗位表 userPostMapper.lambda().in(SysUserPost::getUserId, ids).delete(); // 防止更新失败导致的数据删除 - int flag = baseMapper.deleteByIds(ids); + int flag = userMapper.deleteByIds(ids); if (flag < 1) { throw new ServiceException("删除用户失败!"); } @@ -567,7 +567,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public List selectUserListByDept(Long deptId) { - return baseMapper.lambda() + return userMapper.lambda() .eq(SysUser::getDeptId, deptId) .orderByAsc(SysUser::getUserId) .voList(); @@ -582,7 +582,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Cacheable(cacheNames = CacheNames.SYS_USER_NAME, key = "#userId") @Override public String selectUserNameById(Long userId) { - SysUser sysUser = baseMapper.lambda() + SysUser sysUser = userMapper.lambda() .select(SysUser::getUserName) .eq(SysUser::getUserId, userId) .one(); @@ -598,7 +598,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { @Override @Cacheable(cacheNames = CacheNames.SYS_NICKNAME, key = "#userId") public String selectNicknameById(Long userId) { - SysUser sysUser = baseMapper.lambda() + SysUser sysUser = userMapper.lambda() .select(SysUser::getNickName) .eq(SysUser::getUserId, userId) .one(); @@ -631,7 +631,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public String selectPhonenumberById(Long userId) { - SysUser sysUser = baseMapper.lambda() + SysUser sysUser = userMapper.lambda() .select(SysUser::getPhoneNumber) .eq(SysUser::getUserId, userId) .one(); @@ -646,7 +646,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public String selectEmailById(Long userId) { - SysUser sysUser = baseMapper.lambda() + SysUser sysUser = userMapper.lambda() .select(SysUser::getEmail) .eq(SysUser::getUserId, userId) .one(); @@ -661,7 +661,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { */ @Override public UserDTO selectById(Long userId) { - SysUserVo vo = baseMapper.lambda() + SysUserVo vo = userMapper.lambda() .select(SysUser::getUserId, SysUser::getDeptId, SysUser::getUserName, SysUser::getNickName, SysUser::getUserType, SysUser::getEmail, SysUser::getPhoneNumber, SysUser::getGender, SysUser::getStatus, @@ -683,7 +683,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (CollUtil.isEmpty(userIds)) { return List.of(); } - List list = baseMapper.lambda() + List list = userMapper.lambda() .select(SysUser::getUserId, SysUser::getDeptId, SysUser::getUserName, SysUser::getNickName, SysUser::getUserType, SysUser::getEmail, SysUser::getPhoneNumber, SysUser::getGender, SysUser::getStatus, @@ -741,7 +741,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (CollUtil.isEmpty(deptIds)) { return List.of(); } - List list = baseMapper.lambda() + List list = userMapper.lambda() .select(SysUser::getUserId, SysUser::getUserName, SysUser::getNickName, SysUser::getEmail, SysUser::getPhoneNumber) .eq(SysUser::getStatus, SystemConstants.NORMAL) .in(SysUser::getDeptId, deptIds) @@ -781,7 +781,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService { if (CollUtil.isEmpty(userIds)) { return Collections.emptyMap(); } - List list = baseMapper.lambda() + List list = userMapper.lambda() .select(SysUser::getUserId, SysUser::getNickName) .in(SysUser::getUserId, userIds) .list(); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java index 3b3eeaabb..13d182ea1 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCategoryServiceImpl.java @@ -38,7 +38,7 @@ import java.util.*; public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryService { private final DefService defService; - private final FlwCategoryMapper baseMapper; + private final FlwCategoryMapper categoryMapper; /** * 查询流程分类 @@ -48,7 +48,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ */ @Override public FlowCategoryVo queryById(Long categoryId) { - return baseMapper.selectVoById(categoryId); + return categoryMapper.selectVoById(categoryId); } /** @@ -63,7 +63,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ if (ObjectUtil.isNull(categoryId)) { return null; } - FlowCategory category = baseMapper.lambda() + FlowCategory category = categoryMapper.lambda() .select(FlowCategory::getCategoryName) .eq(FlowCategory::getCategoryId, categoryId) .one(); @@ -81,7 +81,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ if (CollUtil.isEmpty(categoryIds)) { return Collections.emptyMap(); } - List list = baseMapper.lambda() + List list = categoryMapper.lambda() .select(FlowCategory::getCategoryId, FlowCategory::getCategoryName) .in(FlowCategory::getCategoryId, categoryIds) .list(); @@ -97,7 +97,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ @Override public List queryList(FlowCategoryBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); + return categoryMapper.selectVoList(lqw); } /** @@ -147,7 +147,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ */ @Override public boolean checkCategoryNameUnique(FlowCategoryBo category) { - boolean exist = baseMapper.lambda() + boolean exist = categoryMapper.lambda() .eq(FlowCategory::getCategoryName, category.getCategoryName()) .eq(FlowCategory::getParentId, category.getParentId()) .neIfPresent(FlowCategory::getCategoryId, category.getCategoryId()) @@ -176,7 +176,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ */ @Override public boolean hasChildByCategoryId(Long categoryId) { - return baseMapper.lambda().eq(FlowCategory::getParentId, categoryId).exists(); + return categoryMapper.lambda().eq(FlowCategory::getParentId, categoryId).exists(); } /** @@ -206,13 +206,13 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ */ @Override public int insertByBo(FlowCategoryBo bo) { - FlowCategory info = baseMapper.selectById(bo.getParentId()); + FlowCategory info = categoryMapper.selectById(bo.getParentId()); if (ObjectUtil.isNull(info)) { throw new ServiceException("父级流程分类不存在!"); } FlowCategory category = MapstructUtils.convert(bo, FlowCategory.class); category.setAncestors(info.getAncestors() + StringUtils.SEPARATOR + category.getParentId()); - return baseMapper.insert(category); + return categoryMapper.insert(category); } /** @@ -226,7 +226,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ @Transactional(rollbackFor = Exception.class) public int updateByBo(FlowCategoryBo bo) { FlowCategory category = MapstructUtils.convert(bo, FlowCategory.class); - FlowCategory oldCategory = baseMapper.selectById(category.getCategoryId()); + FlowCategory oldCategory = categoryMapper.selectById(category.getCategoryId()); if (ObjectUtil.isNull(oldCategory)) { throw new ServiceException("流程分类不存在,无法修改"); } @@ -234,7 +234,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ throw new ServiceException("不允许修改顶级分类的父级节点"); } if (!oldCategory.getParentId().equals(category.getParentId())) { - FlowCategory newParentCategory = baseMapper.selectById(category.getParentId()); + FlowCategory newParentCategory = categoryMapper.selectById(category.getParentId()); if (ObjectUtil.isNotNull(newParentCategory)) { String newAncestors = newParentCategory.getAncestors() + StringUtils.SEPARATOR + newParentCategory.getCategoryId(); String oldAncestors = oldCategory.getAncestors(); @@ -246,7 +246,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ } else { category.setAncestors(oldCategory.getAncestors()); } - return baseMapper.updateById(category); + return categoryMapper.updateById(category); } /** @@ -257,7 +257,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ * @param oldAncestors 旧的父ID集合 */ private void updateCategoryChildren(Long categoryId, String newAncestors, String oldAncestors) { - List children = baseMapper.lambda() + List children = categoryMapper.lambda() .findInSet(categoryId, FlowCategory::getAncestors) .list(); List list = new ArrayList<>(); @@ -268,7 +268,7 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ list.add(category); } if (CollUtil.isNotEmpty(list)) { - baseMapper.updateBatchById(list); + categoryMapper.updateBatchById(list); } } @@ -281,6 +281,6 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ @CacheEvict(cacheNames = FlowConstant.FLOW_CATEGORY_NAME, key = "#categoryId") @Override public int deleteWithValidById(Long categoryId) { - return baseMapper.deleteById(categoryId); + return categoryMapper.deleteById(categoryId); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java index 5745df8bf..79c6839fa 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwSpelServiceImpl.java @@ -40,7 +40,7 @@ import java.util.Map; @Service public class FlwSpelServiceImpl implements IFlwSpelService { - private final FlwSpelMapper baseMapper; + private final FlwSpelMapper spelMapper; /** * 查询流程spel表达式定义 @@ -50,7 +50,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService { */ @Override public FlowSpelVo queryById(Long id){ - return baseMapper.selectVoById(id); + return spelMapper.selectVoById(id); } /** @@ -63,7 +63,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService { @Override public PageResult queryPageList(FlowSpelBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page result = spelMapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(result.getRecords(), result.getTotal()); } @@ -76,7 +76,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService { @Override public List queryList(FlowSpelBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); + return spelMapper.selectVoList(lqw); } /** @@ -107,7 +107,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService { public Boolean insertByBo(FlowSpelBo bo) { FlowSpel add = MapstructUtils.convert(bo, FlowSpel.class); validEntityBeforeSave(add); - boolean flag = baseMapper.insert(add) > 0; + boolean flag = spelMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); } @@ -124,7 +124,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService { public Boolean updateByBo(FlowSpelBo bo) { FlowSpel update = MapstructUtils.convert(bo, FlowSpel.class); validEntityBeforeSave(update); - return baseMapper.updateById(update) > 0; + return spelMapper.updateById(update) > 0; } /** @@ -134,7 +134,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService { */ private void validEntityBeforeSave(FlowSpel entity){ if (StringUtils.isNotBlank(entity.getViewSpel())) { - boolean exists = baseMapper.lambda() + boolean exists = spelMapper.lambda() .eq(FlowSpel::getViewSpel, entity.getViewSpel()) .neIfPresent(FlowSpel::getId, entity.getId()) .exists(); @@ -156,7 +156,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService { if (isValid){ //TODO 做一些业务上的校验,判断是否需要校验 } - return baseMapper.deleteByIds(ids) > 0; + return spelMapper.deleteByIds(ids) > 0; } /** @@ -193,7 +193,7 @@ public class FlwSpelServiceImpl implements IFlwSpelService { if (CollUtil.isEmpty(viewSpels)) { return Collections.emptyMap(); } - List list = baseMapper.lambda() + List list = spelMapper.lambda() .select(FlowSpel::getViewSpel, FlowSpel::getRemark) .in(FlowSpel::getViewSpel, viewSpels) .list(); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java index 29db4112b..84b79a701 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/TestLeaveServiceImpl.java @@ -50,7 +50,7 @@ import java.util.Map; @Slf4j public class TestLeaveServiceImpl implements ITestLeaveService { - private final TestLeaveMapper baseMapper; + private final TestLeaveMapper leaveMapper; private final WorkflowService workflowService; /** @@ -74,7 +74,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { */ @Override public TestLeaveVo queryById(Long id) { - return baseMapper.selectVoById(id); + return leaveMapper.selectVoById(id); } /** @@ -87,7 +87,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { @Override public PageResult queryPageList(TestLeaveBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + Page result = leaveMapper.selectVoPage(pageQuery.build(), lqw); return PageResult.build(result.getRecords(), result.getTotal()); } @@ -100,7 +100,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { @Override public List queryList(TestLeaveBo bo) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); - return baseMapper.selectVoList(lqw); + return leaveMapper.selectVoList(lqw); } /** @@ -134,7 +134,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { if (StringUtils.isBlank(add.getStatus())) { add.setStatus(BusinessStatusEnum.DRAFT.getStatus()); } - boolean flag = baseMapper.insert(add) > 0; + boolean flag = leaveMapper.insert(add) > 0; if (flag) { bo.setId(add.getId()); } @@ -157,7 +157,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { bo.setApplyCode(System.currentTimeMillis() + StrUtil.EMPTY); } TestLeave leave = MapstructUtils.convert(bo, TestLeave.class); - boolean flag = baseMapper.insertOrUpdate(leave); + boolean flag = leaveMapper.insertOrUpdate(leave); if (flag) { bo.setId(leave.getId()); // 后端发起需要忽略权限 @@ -187,7 +187,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { @Override public TestLeaveVo updateByBo(TestLeaveBo bo) { TestLeave update = MapstructUtils.convert(bo, TestLeave.class); - baseMapper.updateById(update); + leaveMapper.updateById(update); return MapstructUtils.convert(update, TestLeaveVo.class); } @@ -201,7 +201,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { @Transactional(rollbackFor = Exception.class) public Boolean deleteWithValidByIds(Collection ids) { workflowService.deleteInstance(StreamUtils.toList(ids, Convert::toStr)); - return baseMapper.deleteByIds(ids) > 0; + return leaveMapper.deleteByIds(ids) > 0; } /** @@ -214,7 +214,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { @EventListener(condition = "#processEvent.flowCode.startsWith('leave')") public void processHandler(ProcessEvent processEvent) { log.info("当前任务执行了{}", processEvent.toString()); - TestLeave testLeave = baseMapper.selectById(Convert.toLong(processEvent.getBusinessId())); + TestLeave testLeave = leaveMapper.selectById(Convert.toLong(processEvent.getBusinessId())); testLeave.setStatus(processEvent.getStatus()); // 用于例如审批附件 审批意见等 存储到业务表内 自行根据业务实现存储流程 Map params = processEvent.getParams(); @@ -236,7 +236,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService { } String status = BusinessStatusEnum.findByStatus(processEvent.getStatus()); log.info("当前流程状态为{}", status); - baseMapper.updateById(testLeave); + leaveMapper.updateById(testLeave); } /** @@ -264,11 +264,11 @@ public class TestLeaveServiceImpl implements ITestLeaveService { @EventListener(condition = "#processDeleteEvent.flowCode.startsWith('leave')") public void processDeleteHandler(ProcessDeleteEvent processDeleteEvent) { log.info("监听删除流程事件,当前任务执行了{}", processDeleteEvent.toString()); - TestLeave testLeave = baseMapper.selectById(Convert.toLong(processDeleteEvent.getBusinessId())); + TestLeave testLeave = leaveMapper.selectById(Convert.toLong(processDeleteEvent.getBusinessId())); if (ObjectUtil.isNull(testLeave)) { return; } - baseMapper.deleteById(testLeave.getId()); + leaveMapper.deleteById(testLeave.getId()); } } From e45436671d095b744050664fbb137ea32d43d6ce Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Fri, 15 May 2026 12:34:02 +0800 Subject: [PATCH 04/35] =?UTF-8?q?update=20=E5=A2=9E=E5=8A=A0=20Lambda=20?= =?UTF-8?q?=E6=9F=A5=E8=AF=A2=E6=9E=84=E9=80=A0=E8=BE=85=E5=8A=A9=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E7=9A=84=E6=9D=A1=E4=BB=B6=E6=96=B9=E6=B3=95=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/query/LambdaQueryBuilder.java | 568 ++++++++++++++++++ .../core/query/LambdaQueryCondition.java | 297 +++++++++ .../mybatis/core/query/QueryBuilder.java | 7 + 3 files changed, 872 insertions(+) diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryBuilder.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryBuilder.java index 2de14879f..a8e77015e 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryBuilder.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaQueryBuilder.java @@ -17,404 +17,972 @@ import java.util.function.Consumer; */ 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 459465e91..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 @@ -16,92 +16,331 @@ import java.util.Map; */ 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 30b710d80..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 @@ -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)); } From 592d998e0f580f292fb67f162ef8874f7ee27458 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Fri, 15 May 2026 12:48:53 +0800 Subject: [PATCH 05/35] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=B3=A8=E9=87=8A=E5=B9=B6=E7=A7=BB=E9=99=A4=E5=86=97?= =?UTF-8?q?=E4=BD=99=E6=9E=84=E9=80=A0=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/system/domain/SysCache.java | 47 ------------------- .../dromara/system/domain/SysDictData.java | 5 -- .../org/dromara/system/domain/SysRole.java | 4 -- .../dromara/system/domain/bo/SysRoleBo.java | 4 -- .../dromara/system/domain/bo/SysSocialBo.java | 2 - .../dromara/system/domain/bo/SysUserBo.java | 4 -- .../system/domain/vo/SysRoleMenuPermVo.java | 6 +++ .../dromara/system/mapper/SysMenuMapper.java | 15 +++++- 8 files changed, 20 insertions(+), 67 deletions(-) delete mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysCache.java diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysCache.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysCache.java deleted file mode 100644 index 6c956d529..000000000 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysCache.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.dromara.system.domain; - -import lombok.Data; -import lombok.NoArgsConstructor; -import org.dromara.common.core.utils.StringUtils; - -/** - * 缓存信息 - * - * @author Lion Li - */ -@Data -@NoArgsConstructor -public class SysCache { - - /** - * 缓存名称 - */ - private String cacheName = ""; - - /** - * 缓存键名 - */ - private String cacheKey = ""; - - /** - * 缓存内容 - */ - private String cacheValue = ""; - - /** - * 备注 - */ - private String remark = ""; - - public SysCache(String cacheName, String remark) { - this.cacheName = cacheName; - this.remark = remark; - } - - public SysCache(String cacheName, String cacheKey, String cacheValue) { - this.cacheName = StringUtils.replace(cacheName, ":", ""); - this.cacheKey = StringUtils.replace(cacheKey, cacheName, ""); - this.cacheValue = cacheValue; - } - -} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictData.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictData.java index 3493662bd..dbf15bd1a 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictData.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/SysDictData.java @@ -4,7 +4,6 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; -import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.mybatis.core.domain.BaseEntity; /** @@ -64,8 +63,4 @@ public class SysDictData extends BaseEntity { */ private String remark; - public boolean getDefault() { - return SystemConstants.YES.equals(this.isDefault); - } - } 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/bo/SysRoleBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysRoleBo.java index b424dc8a3..d09a41f5c 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 @@ -88,10 +88,6 @@ public class SysRoleBo implements Serializable { */ private Long[] deptIds; - public SysRoleBo(Long roleId) { - this.roleId = roleId; - } - 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/domain/bo/SysSocialBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysSocialBo.java index 1f5e48866..e596da022 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 @@ -140,6 +140,4 @@ public class SysSocialBo implements Serializable { */ private String oauthTokenSecret; - - } 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..8ad804e08 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,6 @@ public class SysUserBo implements Serializable { */ private Map params = new HashMap<>(); - public SysUserBo(Long userId) { - this.userId = userId; - } - 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/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() From 4fdf2ea75aac94db8f905052aee842b500388199 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Fri, 15 May 2026 13:04:30 +0800 Subject: [PATCH 06/35] =?UTF-8?q?update=20=E5=A2=9E=E5=8A=A0=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E6=96=B9=E6=B3=95=E7=9A=84=E6=B3=A8=E9=87=8A=E4=BB=A5?= =?UTF-8?q?=E6=8F=90=E5=8D=87=E4=BB=A3=E7=A0=81=E5=8F=AF=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/enums/FormatsType.java | 6 + .../common/core/utils/SpringUtils.java | 5 + .../common/core/utils/ip/AddressUtils.java | 6 + .../common/excel/core/CellMergeHandler.java | 7 + .../excel/utils/ExcelWriterWrapper.java | 127 ++++++++++++++++++ .../json/enhance/JsonEnhancementContext.java | 18 +++ .../common/json/enhance/JsonFieldContext.java | 7 + .../json/enhance/JsonValueEnhancer.java | 18 +++ .../core/domain/DataPermissionAccess.java | 5 + .../core/mapper/LambdaCrudChainWrapper.java | 121 +++++++++++++++++ .../common/mybatis/core/page/PageQuery.java | 11 ++ .../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 ++ .../gen/util/template/PathNamedTemplate.java | 21 +++ .../org/dromara/system/domain/SysUser.java | 10 ++ .../dromara/system/domain/bo/SysRoleBo.java | 5 + .../dromara/system/domain/bo/SysUserBo.java | 5 + .../dromara/system/domain/vo/SysRoleVo.java | 5 + .../dromara/system/mapper/SysUserMapper.java | 6 + .../runner/SystemApplicationRunner.java | 5 + .../system/service/ISysMenuService.java | 5 +- .../service/impl/SysMenuServiceImpl.java | 6 +- 25 files changed, 514 insertions(+), 6 deletions(-) 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 4f53f2592..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 @@ -60,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 694c16462..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 @@ -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 439d04ced..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 @@ -16,15 +16,33 @@ public class JsonEnhancementContext { 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 48d37d5b9..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 @@ -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, () -> { @@ -130,11 +192,27 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper 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 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); } @@ -382,6 +495,11 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper instance() { return new LambdaCrudChainWrapper<>(crudMapper, getEntity(), getEntityClass(), null, null, paramNameSeq, @@ -389,6 +507,9 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper 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-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-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 d09a41f5c..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 @@ -88,6 +88,11 @@ public class SysRoleBo implements Serializable { */ private Long[] deptIds; + /** + * 判断当前角色是否为超级管理员角色。 + * + * @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/domain/bo/SysUserBo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/bo/SysUserBo.java index 8ad804e08..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,6 +133,11 @@ public class SysUserBo implements Serializable { */ private Map params = new HashMap<>(); + /** + * 判断当前用户是否为超级管理员。 + * + * @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/SysRoleVo.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/domain/vo/SysRoleVo.java index cc4e4b14e..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 @@ -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/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/runner/SystemApplicationRunner.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/runner/SystemApplicationRunner.java index 90482ac1b..9830b23cc 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/runner/SystemApplicationRunner.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/runner/SystemApplicationRunner.java @@ -19,6 +19,11 @@ public class SystemApplicationRunner 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/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 1eace2e7a..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 @@ -110,7 +110,7 @@ public class SysMenuServiceImpl implements ISysMenuService { } /** - * 根据用户ID查询菜单 + * 根据用户ID查询菜单树信息 * * @param userId 用户ID * @return 按树结构组织的菜单列表 @@ -251,7 +251,7 @@ public class SysMenuServiceImpl implements ISysMenuService { /** * 是否存在菜单子节点 * - * @param menuIds 菜单ID串 + * @param menuIds 菜单ID列表 * @return 结果 */ @Override @@ -337,7 +337,7 @@ public class SysMenuServiceImpl implements ISysMenuService { } /** - * 校验路由名称是否唯一 + * 校验路由组合是否唯一 * * @param menuBo 菜单信息 * @return 结果 From b971ef628e846ea8e867dfe656f340ca0e2bc202 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Fri, 15 May 2026 13:22:22 +0800 Subject: [PATCH 07/35] =?UTF-8?q?update=20=E5=A2=9E=E5=8A=A0=E5=A4=9A?= =?UTF-8?q?=E4=B8=AA=E7=BF=BB=E8=AF=91=E6=96=B9=E6=B3=95=E7=9A=84=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E4=BB=A5=E6=8F=90=E5=8D=87=E4=BB=A3=E7=A0=81=E5=8F=AF?= =?UTF-8?q?=E8=AF=BB=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TranslationJsonFieldProcessor.java | 58 +++++++++++++++++++ .../core/impl/DeptNameTranslationImpl.java | 14 +++++ .../core/impl/DictTypeTranslationImpl.java | 7 +++ .../core/impl/NicknameTranslationImpl.java | 14 +++++ .../core/impl/OssUrlTranslationImpl.java | 14 +++++ .../core/impl/UserNameTranslationImpl.java | 14 +++++ .../impl/CategoryNameTranslationImpl.java | 21 +++++++ 7 files changed, 142 insertions(+) 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 abb93bcaf..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); @@ -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 1ebfa8de9..b5c968973 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 @@ -39,6 +39,13 @@ public class DeptNameTranslationImpl implements TranslationInterface { return null; } + /** + * 批量将部门 ID 翻译为部门名称。 + * + * @param keys 部门 ID 集合 + * @param other 额外参数 + * @return 部门 ID 与部门名称映射 + */ @Override public Map translationBatch(Set keys, String other) { Set deptIds = collectLongIds(keys); @@ -53,6 +60,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 799d5f9f1..f0d964ac4 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 @@ -39,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 05c63f3ba..7fa4d23fa 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 @@ -39,6 +39,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); @@ -53,6 +60,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 a0c012d1e..ca7f2314c 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 @@ -42,6 +42,13 @@ public class OssUrlTranslationImpl implements TranslationInterface { return null; } + /** + * 批量将 OSS ID 翻译为访问地址。 + * + * @param keys OSS ID 集合 + * @param other 额外参数 + * @return OSS ID 与访问地址映射 + */ @Override public Map translationBatch(Set keys, String other) { Set ossIds = collectLongIds(keys); @@ -57,6 +64,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 11b3eb0bd..4b7d5a05f 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 @@ -36,6 +36,13 @@ public class UserNameTranslationImpl implements TranslationInterface { return userService.selectUserNameById(Convert.toLong(key)); } + /** + * 批量将用户 ID 翻译为用户名。 + * + * @param keys 用户 ID 集合 + * @param other 额外参数 + * @return 用户 ID 与用户名映射 + */ @Override public Map translationBatch(Set keys, String other) { Set userIds = collectLongIds(keys); @@ -50,6 +57,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-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); From 97d86801fac2a26a4d54bb9df8dee3d31484072d 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 13:24:14 +0800 Subject: [PATCH 08/35] update snailjob 1.9.0 => 2.0.0 --- pom.xml | 9 +++++++-- script/sql/oracle/oracle_ry_job.sql | 8 +++++++- script/sql/postgres/postgres_ry_job.sql | 8 +++++++- script/sql/ry_job.sql | 10 +++++++--- script/sql/sqlserver/sqlserver_ry_job.sql | 22 +++++++++++++++++++++- 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/pom.xml b/pom.xml index d4ddb14f3..19b221a43 100644 --- a/pom.xml +++ b/pom.xml @@ -27,12 +27,12 @@ 3.5.16 1.5.6 3.9.1 - 5.8.43 + 5.8.44 4.0.2 4.4.0 2.2.7 4.5.0 - 1.9.0 + 2.0.0 1.5.0 0.2.0 1.18.42 @@ -272,6 +272,11 @@ snail-job-client-starter ${snailjob.version} + + com.aizuda + snail-job-client-retry-core + ${snailjob.version} + com.aizuda snail-job-client-job-core diff --git a/script/sql/oracle/oracle_ry_job.sql b/script/sql/oracle/oracle_ry_job.sql index 2a7f11f9a..e3a7c6f83 100644 --- a/script/sql/oracle/oracle_ry_job.sql +++ b/script/sql/oracle/oracle_ry_job.sql @@ -484,6 +484,7 @@ CREATE TABLE sj_job ( id number GENERATED ALWAYS AS IDENTITY, namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + biz_id varchar2(64) NOT NULL, group_name varchar2(64) NULL, job_name varchar2(64) NULL, args_str clob DEFAULT NULL NULL, @@ -519,9 +520,11 @@ ALTER TABLE sj_job CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name); CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index); CREATE INDEX idx_sj_job_03 ON sj_job (create_dt); +CREATE UNIQUE INDEX uk_sj_job_01 ON sj_job (namespace_id, biz_id); COMMENT ON COLUMN sj_job.id IS '主键'; COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job.biz_id IS '业务ID'; COMMENT ON COLUMN sj_job.group_name IS '组名称'; COMMENT ON COLUMN sj_job.job_name IS '名称'; COMMENT ON COLUMN sj_job.args_str IS '执行方法参数'; @@ -551,7 +554,7 @@ COMMENT ON COLUMN sj_job.create_dt IS '创建时间'; COMMENT ON COLUMN sj_job.update_dt IS '修改时间'; COMMENT ON TABLE sj_job IS '任务信息'; -INSERT INTO sj_job(namespace_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES ('dev', 'ruoyi_group', 'demo-job', NULL, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0, sysdate, sysdate); +INSERT INTO sj_job(namespace_id, biz_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES ('dev', 'demo-job', 'ruoyi_group', 'demo-job', NULL, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0, sysdate, sysdate); -- sj_job_log_message CREATE TABLE sj_job_log_message @@ -774,6 +777,7 @@ CREATE TABLE sj_workflow id number GENERATED ALWAYS AS IDENTITY, workflow_name varchar2(64) NULL, namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL, + biz_id varchar2(64) NOT NULL, group_name varchar2(64) NULL, workflow_status smallint DEFAULT 1 NOT NULL, trigger_type smallint NOT NULL, @@ -799,10 +803,12 @@ ALTER TABLE sj_workflow CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt); CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name); +CREATE UNIQUE INDEX uk_sj_workflow_01 ON sj_workflow (namespace_id, biz_id); COMMENT ON COLUMN sj_workflow.id IS '主键'; COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称'; COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow.biz_id IS '业务ID'; COMMENT ON COLUMN sj_workflow.group_name IS '组名称'; COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启'; COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间'; diff --git a/script/sql/postgres/postgres_ry_job.sql b/script/sql/postgres/postgres_ry_job.sql index 9980c3b43..9b19c0663 100644 --- a/script/sql/postgres/postgres_ry_job.sql +++ b/script/sql/postgres/postgres_ry_job.sql @@ -445,6 +445,7 @@ CREATE TABLE sj_job ( id bigserial PRIMARY KEY, namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + biz_id varchar(64) NOT NULL, group_name varchar(64) NOT NULL, job_name varchar(64) NOT NULL, args_str text NULL DEFAULT NULL, @@ -477,9 +478,11 @@ CREATE TABLE sj_job CREATE INDEX idx_sj_job_01 ON sj_job (namespace_id, group_name); CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index); CREATE INDEX idx_sj_job_03 ON sj_job (create_dt); +CREATE UNIQUE INDEX uk_sj_job_01 ON sj_job (namespace_id, biz_id); COMMENT ON COLUMN sj_job.id IS '主键'; COMMENT ON COLUMN sj_job.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_job.biz_id IS '业务ID'; COMMENT ON COLUMN sj_job.group_name IS '组名称'; COMMENT ON COLUMN sj_job.job_name IS '名称'; COMMENT ON COLUMN sj_job.args_str IS '执行方法参数'; @@ -509,7 +512,7 @@ COMMENT ON COLUMN sj_job.create_dt IS '创建时间'; COMMENT ON COLUMN sj_job.update_dt IS '修改时间'; COMMENT ON TABLE sj_job IS '任务信息'; -INSERT INTO sj_job VALUES (1, 'dev', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '', '', '', 0, now(), now()); +INSERT INTO sj_job VALUES (1, 'dev', 'demo-job', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '', '', '', 0, now(), now()); -- sj_job_log_message CREATE TABLE sj_job_log_message @@ -717,6 +720,7 @@ CREATE TABLE sj_workflow id bigserial PRIMARY KEY, workflow_name varchar(64) NOT NULL, namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + biz_id varchar(64) NOT NULL, group_name varchar(64) NOT NULL, workflow_status smallint NOT NULL DEFAULT 1, trigger_type smallint NOT NULL, @@ -739,10 +743,12 @@ CREATE TABLE sj_workflow CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt); CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name); +CREATE UNIQUE INDEX uk_sj_workflow_01 ON sj_workflow (namespace_id, biz_id); COMMENT ON COLUMN sj_workflow.id IS '主键'; COMMENT ON COLUMN sj_workflow.workflow_name IS '工作流名称'; COMMENT ON COLUMN sj_workflow.namespace_id IS '命名空间id'; +COMMENT ON COLUMN sj_workflow.biz_id IS '业务ID'; COMMENT ON COLUMN sj_workflow.group_name IS '组名称'; COMMENT ON COLUMN sj_workflow.workflow_status IS '工作流状态 0、关闭、1、开启'; COMMENT ON COLUMN sj_workflow.trigger_type IS '触发类型 1.CRON 表达式 2. 固定时间'; diff --git a/script/sql/ry_job.sql b/script/sql/ry_job.sql index dd8124cf5..e7b627f49 100644 --- a/script/sql/ry_job.sql +++ b/script/sql/ry_job.sql @@ -280,6 +280,7 @@ CREATE TABLE `sj_job` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `biz_id` varchar(64) NOT NULL COMMENT '业务ID', `group_name` varchar(64) NOT NULL COMMENT '组名称', `job_name` varchar(64) NOT NULL COMMENT '名称', `args_str` text DEFAULT NULL COMMENT '执行方法参数', @@ -310,12 +311,13 @@ CREATE TABLE `sj_job` PRIMARY KEY (`id`), KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`), KEY `idx_job_status_bucket_index` (`job_status`, `bucket_index`), - KEY `idx_create_dt` (`create_dt`) + KEY `idx_create_dt` (`create_dt`), + UNIQUE KEY `uk_sj_job_01` (`namespace_id`, `biz_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 0 DEFAULT CHARSET = utf8mb4 COMMENT ='任务信息'; -INSERT INTO `sj_job` VALUES (1, 'dev', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0 , now(), now()); +INSERT INTO `sj_job` VALUES (1, 'dev', 'demo-job', 'ruoyi_group', 'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, 'testJobExecutor', 2, '60', 1, 60, 3, 1, 1, 116, 0, '', 1, '','', '', 0 , now(), now()); CREATE TABLE `sj_job_log_message` ( @@ -443,6 +445,7 @@ CREATE TABLE `sj_workflow` `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键', `workflow_name` varchar(64) NOT NULL COMMENT '工作流名称', `namespace_id` varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id', + `biz_id` varchar(64) NOT NULL COMMENT '业务ID', `group_name` varchar(64) NOT NULL COMMENT '组名称', `workflow_status` tinyint(4) NOT NULL DEFAULT 1 COMMENT '工作流状态 0、关闭、1、开启', `trigger_type` tinyint(4) NOT NULL COMMENT '触发类型 1.CRON 表达式 2. 固定时间', @@ -463,7 +466,8 @@ CREATE TABLE `sj_workflow` `update_dt` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间', PRIMARY KEY (`id`), KEY `idx_create_dt` (`create_dt`), - KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`) + KEY `idx_namespace_id_group_name` (`namespace_id`, `group_name`), + UNIQUE KEY `uk_sj_workflow_01` (`namespace_id`, `biz_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 0 DEFAULT CHARSET = utf8mb4 COMMENT ='工作流'; diff --git a/script/sql/sqlserver/sqlserver_ry_job.sql b/script/sql/sqlserver/sqlserver_ry_job.sql index ceb8c459b..759c27afc 100644 --- a/script/sql/sqlserver/sqlserver_ry_job.sql +++ b/script/sql/sqlserver/sqlserver_ry_job.sql @@ -1428,6 +1428,7 @@ CREATE TABLE sj_job ( id bigint NOT NULL PRIMARY KEY IDENTITY, namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + biz_id nvarchar(64) NOT NULL, group_name nvarchar(64) NOT NULL, job_name nvarchar(64) NOT NULL, args_str nvarchar(max) NULL DEFAULT NULL, @@ -1464,6 +1465,8 @@ CREATE INDEX idx_sj_job_02 ON sj_job (job_status, bucket_index) GO CREATE INDEX idx_sj_job_03 ON sj_job (create_dt) GO +CREATE UNIQUE INDEX uk_sj_job_01 ON sj_job (namespace_id, biz_id) +GO EXEC sp_addextendedproperty 'MS_Description', N'主键', @@ -1479,6 +1482,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'namespace_id' GO +EXEC sp_addextendedproperty + 'MS_Description', N'业务ID', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_job', + 'COLUMN', N'biz_id' +GO + EXEC sp_addextendedproperty 'MS_Description', N'组名称', 'SCHEMA', N'dbo', @@ -1674,7 +1684,7 @@ EXEC sp_addextendedproperty 'TABLE', N'sj_job' GO -INSERT INTO sj_job (namespace_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES (N'dev', N'ruoyi_group', N'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, N'testJobExecutor', 2, N'60', 1, 60, 3, 1, 1, 116, 0, N'', 1, N'', N'', N'', 0, getdate(), getdate()) +INSERT INTO sj_job (namespace_id, biz_id, group_name, job_name, args_str, args_type, next_trigger_at, job_status, task_type, route_key, executor_type, executor_info, trigger_type, trigger_interval, block_strategy,executor_timeout, max_retry_times, parallel_num, retry_interval, bucket_index, resident, notify_ids, owner_id, labels, description, ext_attrs, deleted, create_dt, update_dt) VALUES (N'dev', N'demo-job', N'ruoyi_group', N'demo-job', null, 1, 1710344035622, 1, 1, 4, 1, N'testJobExecutor', 2, N'60', 1, 60, 3, 1, 1, 116, 0, N'', 1, N'', N'', N'', 0, getdate(), getdate()) GO -- sj_job_log_message @@ -2354,6 +2364,7 @@ CREATE TABLE sj_workflow id bigint NOT NULL PRIMARY KEY IDENTITY, workflow_name nvarchar(64) NOT NULL, namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a', + biz_id nvarchar(64) NOT NULL, group_name nvarchar(64) NOT NULL, workflow_status tinyint NOT NULL DEFAULT 1, trigger_type tinyint NOT NULL, @@ -2379,6 +2390,8 @@ CREATE INDEX idx_sj_workflow_01 ON sj_workflow (create_dt) GO CREATE INDEX idx_sj_workflow_02 ON sj_workflow (namespace_id, group_name) GO +CREATE UNIQUE INDEX uk_sj_workflow_01 ON sj_workflow (namespace_id, biz_id) +GO EXEC sp_addextendedproperty 'MS_Description', N'主键', @@ -2401,6 +2414,13 @@ EXEC sp_addextendedproperty 'COLUMN', N'namespace_id' GO +EXEC sp_addextendedproperty + 'MS_Description', N'业务ID', + 'SCHEMA', N'dbo', + 'TABLE', N'sj_workflow', + 'COLUMN', N'biz_id' +GO + EXEC sp_addextendedproperty 'MS_Description', N'组名称', 'SCHEMA', N'dbo', From fe3f388ebd083efd458d6e131407e06e9917333a Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Fri, 15 May 2026 13:40:18 +0800 Subject: [PATCH 09/35] =?UTF-8?q?update=20=E5=A2=9E=E5=8A=A0=E5=AF=B9=20Ma?= =?UTF-8?q?pper=20=E8=A7=84=E5=88=99=E3=80=81=E7=BF=BB=E8=AF=91=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E5=92=8C=E6=B3=A8=E9=87=8A=E4=BF=AE=E6=AD=A3=E7=9A=84?= =?UTF-8?q?=E8=AF=A6=E7=BB=86=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .codex/skills/ruoyi-plus-ai-coding/SKILL.md | 19 ++++++++- .../ruoyi-plus-ai-coding/agents/openai.yaml | 4 +- .../references/backend.md | 42 +++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/.codex/skills/ruoyi-plus-ai-coding/SKILL.md b/.codex/skills/ruoyi-plus-ai-coding/SKILL.md index a71dc8edf..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` 骨架。 @@ -34,6 +36,8 @@ description: 在仓库内按代码生成器模板和项目既有约定生成或 `domain` entity、`domain.bo`、`domain.vo`、`mapper`、`service`、`service.impl`、`controller`。 4. 优先在生成器结构上扩展,不要自行发明新的分层。 5. 修改 `ruoyi-system` 这类复杂模块前,先阅读同类现有实现,因为这些模块通常比生成器默认产物多出数据权限、联表、缓存、安全校验等逻辑。 +6. 修改 `ruoyi-common` 公共模块前,先阅读同包接口、实现类和调用点,优先保持已有 API 语义与兼容性。 +7. 只补注释或文档时,不运行无关格式化,不重排 import,不改代码逻辑。 ## 优先级规则 @@ -76,6 +80,8 @@ Vue 3、TypeScript API 文件、生成式列表页、表单状态、字典和日 - 如果目标模块已经存在自定义校验、数据权限、事务、缓存、Excel 导入导出、联表查询等逻辑,应在此基础上扩展,不要为了“简洁”把它们削平。 - 如果附近 controller 接口已经带有权限、日志、防重、加密、分组校验等注解,新接口默认同步保持一致,除非有明确理由不这样做。 - 如果 BO 或 VO 需要字段校验、翻译、Excel 注解,应优先参考同模块同用途对象,不要机械套通用注解。 +- 如果修改公共基础模块,优先保持公开 API 兼容,新增能力要查调用点和同包风格。 +- 如果任务只涉及注释,默认补 JavaDoc 并保持实现不变;框架覆写方法不强行重复注释,除非业务语义不直观。 ## 目录映射规则 @@ -114,6 +120,14 @@ Vue 3、TypeScript API 文件、生成式列表页、表单状态、字典和日 如果涉及数据权限、缓存、事务、导入导出、字典、翻译、加密、分组校验,优先查项目已有做法并复用公共能力。 +### 4. 公共基础模块修改 + +修改 `ruoyi-common` 下的基础能力时,优先保证二进制/API 兼容:不要轻易改公开方法签名、泛型、返回值或异常语义。新增注释和小范围能力时,先查同包现有风格,例如 `common-mybatis` 的链式 wrapper、`common-translation` 的 `TranslationInterface` 实现、`common-json` 的字段处理器。 + +### 5. 注释修正任务 + +只要求“加注释/完善注释”时,默认补 JavaDoc,不改实现。优先补公共 API、接口方法、字段含义、复杂私有辅助方法;覆写框架回调方法只有在当前文件已有注释风格或业务语义不直观时才补。 + ## 输出要求 使用本 skill 时,默认期望产出应满足: @@ -132,6 +146,9 @@ 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 路径与后端路由完全对应。 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 999284194..3a86cc759 100644 --- a/.codex/skills/ruoyi-plus-ai-coding/references/backend.md +++ b/.codex/skills/ruoyi-plus-ai-coding/references/backend.md @@ -64,6 +64,8 @@ - 模块已经使用 `@DataPermission` 时,在重写方法和自定义查询上继续保留。 - 复杂模块里 mapper 可能同时继承 `MPJBaseMapper` 并使用 `JoinWrappers.lambda(...)`,遇到这种风格要延续,不要换一种写法。 - 只有在 `selectVoList/selectVoPage` 不够用时,才补 XML 或自定义 mapper 方法。 +- Mapper 默认方法可以承载短小的 wrapper 查询;涉及复杂业务编排、缓存、事务或跨 mapper 写入时放到 service。 +- `ruoyi-system` 的用户、角色、菜单、部门等模块常带数据权限、MPJ 联表、角色状态过滤,修改前先读对应 mapper/service。 ### Mapper 建议结构 @@ -165,6 +167,46 @@ - 数组转列表按附近代码习惯使用 `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 命名同步。 From 14e444e4bd44b2d335e176d2358af6354e22ccf8 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Fri, 15 May 2026 13:59:44 +0800 Subject: [PATCH 10/35] =?UTF-8?q?update=20=E6=96=B0=E5=A2=9E=E5=85=AC?= =?UTF-8?q?=E5=85=B1=E5=9F=BA=E7=A1=80=E6=A8=A1=E5=9D=97=E6=96=87=E6=A1=A3?= =?UTF-8?q?=EF=BC=8C=E6=98=8E=E7=A1=AE=E5=A2=9E=E9=87=8F=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=8E=9F=E5=88=99=E4=B8=8E=E6=8E=A5=E5=8F=A3=E8=A7=84=E8=8C=83?= 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 | 8 ++++ .claude/agents/backend-engineering.md | 9 +++- .claude/agents/backend-javadoc.md | 42 +++++++++++++++++++ .claude/agents/backend-module-enhancement.md | 6 +++ .claude/agents/backend-query-permission.md | 14 +++++++ 6 files changed, 119 insertions(+), 2 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 26d732bf8..6da0b453d 100644 --- a/.claude/agents/backend-crud.md +++ b/.claude/agents/backend-crud.md @@ -15,9 +15,11 @@ description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRU ## 结构约定 - entity 默认继承 `BaseEntity` +- entity 使用 `@TableName`,主键使用 `@TableId`;存在 `delFlag`、乐观锁字段时保留 `@TableLogic`、`@Version` - mapper 默认继承 `BaseMapperPlus` - BO 使用 `@AutoMapper(target = Entity.class, reverseConvertGenerate = false)` - VO 使用 `@AutoMapper(target = Entity.class)` +- BO/VO/Entity 职责分离:请求、查询扩展字段放 BO;展示派生字段和 `@Translation` 放 VO - 代码生成器模板按类名首字母小写命名 Mapper 字段,例如 `SysRoleMapper` -> `sysRoleMapper`;手写业务代码可使用具体业务短名 ## 默认方法集合 @@ -32,8 +34,11 @@ description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRU ## 查询规则 - 单表查询优先用 `LambdaQueryWrapper` +- 项目公共链式查询可使用 `BaseMapperPlus#lambda()`、`LambdaCrudChainWrapper`、`LambdaQueryCondition` 的 `IfPresent` / `IfText` / `IfNotEmpty` 风格 - 日期范围默认从 `bo.getParams()` 中读取 begin/end - 分页优先返回 `PageResult` +- BO 转实体使用 `MapstructUtils.convert(bo, Entity.class)` +- 写入前校验优先放在 `validEntityBeforeSave(...)` ## 接口规则 @@ -47,6 +52,8 @@ description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRU `PUT` `DELETE /{ids}` - 默认检查是否需要 `@SaCheckPermission`、`@Log`、`@RepeatSubmit` +- 权限标识遵循 `${module}:${business}:${action}` +- 导出接口通常保持 `POST /export` ## 自检 @@ -54,3 +61,4 @@ description: 标准后端 CRUD 专家。用于当前项目中的新增单表 CRU - BO / VO / Entity 是否职责分离 - 导出、分页、删除前校验是否齐全 - 是否只是 generator 裸产物,如果是要继续补齐项目约定 +- 前端 `api/types/index.vue` 如需同步,接口路径、返回结构、日期范围参数要与后端一致 diff --git a/.claude/agents/backend-engineering.md b/.claude/agents/backend-engineering.md index 440d1bfc5..59efb4daf 100644 --- a/.claude/agents/backend-engineering.md +++ b/.claude/agents/backend-engineering.md @@ -1,6 +1,6 @@ --- name: backend-engineering -description: 后端工程总入口。用于在当前项目中识别任务属于标准 CRUD、复杂模块增强、联表与数据权限、或前后端联动,并选择合适的后端子 agent。 +description: 后端工程总入口。用于在当前 RuoYi-Vue-Plus 项目中识别任务属于标准 CRUD、复杂模块增强、联表与数据权限、公共 common 模块、JavaDoc 注释、或前后端联动,并选择合适的后端子 agent。 --- 你是当前后端工程的总入口 agent。 @@ -10,10 +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. 如果同时要求同步前端接口或前端页面骨架,保持后端路由与 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 模板。 - 默认直接产出可落地代码,而不是只给抽象建议。 +- 不要把 `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 440a8f48e..fe25515e2 100644 --- a/.claude/agents/backend-module-enhancement.md +++ b/.claude/agents/backend-module-enhancement.md @@ -11,6 +11,7 @@ description: 复杂后端模块增强专家。用于修改当前项目中已经 2. 增量修改,不重写整块 service/controller。 3. 保留已有的数据权限、事务、缓存、导入导出、唯一性校验、删除前校验。 4. 不能为了“简洁”把复杂模块退化成模板式单表 CRUD。 +5. 修改 workflow 模块时检查同包是否需要 `@ConditionalOnEnable`。 ## 常见任务 @@ -18,6 +19,7 @@ description: 复杂后端模块增强专家。用于修改当前项目中已经 - 新增或调整写入前校验 - 维护角色、岗位、用户等关联数据 - 增加复杂页面所需的特殊接口 +- 增加或调整缓存、翻译、字典、OSS、导入导出能力 ## 约束 @@ -25,9 +27,13 @@ description: 复杂后端模块增强专家。用于修改当前项目中已经 - service 里的旧逻辑要先理解再改 - 如果附近已有 `ServiceException`、缓存注解、事务注解、数据权限判断,新增逻辑默认保持一致 - 如果存在联动前端页面,接口路径与返回结构尽量稳定 +- 已有 `@Cacheable`、`@CacheEvict`、`@Caching` 的 service,新增写操作要同步考虑缓存失效 +- 工作流分类、任务、实例等查询常带分类权限或用户维度过滤,不要绕过旧逻辑 +- 翻译实现仍遵守 `TranslationInterface` + `@TranslationType` + `translationBatch` 批量查询规则 ## 自检 - 是否破坏了原模块的权限边界 - 是否误删了旧逻辑中的事务或校验 - 是否错误简化了复杂关系维护 +- 是否漏掉缓存失效、导入导出回执、翻译字段、前端调用路径等联动点 diff --git a/.claude/agents/backend-query-permission.md b/.claude/agents/backend-query-permission.md index 6bd741d7d..c801d598c 100644 --- a/.claude/agents/backend-query-permission.md +++ b/.claude/agents/backend-query-permission.md @@ -11,18 +11,32 @@ description: 后端查询、联表与数据权限专家。用于当前项目中 2. 涉及数据权限时优先复用 `@DataPermission` 与已有字段映射方式。 3. 复杂联表优先参考 MPJ 风格,不轻易改回手写零散 SQL。 4. 如果 `BaseMapperPlus + wrapper` 足够,不要额外补 XML。 +5. wrapper 查询条件优先使用项目已有工具和命名,不自造查询 DSL。 ## 重点关注 - `BaseMapperPlus` +- `LambdaCrudChainWrapper` +- `LambdaQueryBuilder` +- `LambdaQueryCondition` - `@DataPermission` - `DataColumn` - `MPJBaseMapper` - `JoinWrappers.lambda(...)` - 复杂分页与列表查询 +## 项目写法 + +- 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 风格一致 - 不要让查询参数风格和前端现有调用脱节 +- 不要为了“易读”移除已有数据权限、角色状态、删除标识过滤 From 90c8a5c2f34da4ee4ab7a839bd2a6c923d7a77d2 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 16:23:29 +0800 Subject: [PATCH 11/35] =?UTF-8?q?update=20=E5=A2=9E=E5=8A=A0join=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=20=E7=AE=80=E5=8C=96join=E4=B8=9A=E5=8A=A1=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/query/LambdaJoinQueryBuilder.java | 580 ++++++++++++++++++ .../mybatis/core/query/QueryBuilder.java | 24 + .../dromara/system/mapper/SysDeptMapper.java | 7 +- .../dromara/system/mapper/SysMenuMapper.java | 42 +- .../dromara/system/mapper/SysPostMapper.java | 7 +- .../dromara/system/mapper/SysRoleMapper.java | 7 +- .../dromara/system/mapper/SysUserMapper.java | 33 +- .../workflow/mapper/FlwHisTaskMapper.java | 71 +-- .../workflow/mapper/FlwTaskMapper.java | 111 ++-- .../service/impl/FlwInstanceServiceImpl.java | 55 +- 10 files changed, 738 insertions(+), 199 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaJoinQueryBuilder.java diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaJoinQueryBuilder.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaJoinQueryBuilder.java new file mode 100644 index 000000000..9dbe5cf1a --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/query/LambdaJoinQueryBuilder.java @@ -0,0 +1,580 @@ +package org.dromara.common.mybatis.core.query; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import com.github.yulichang.toolkit.LambdaUtils; +import com.github.yulichang.toolkit.support.ColumnCache; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.github.yulichang.wrapper.segments.SelectCache; +import com.github.yulichang.wrapper.segments.SelectNormal; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.helper.DataBaseHelper; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + * MPJ Lambda 联表查询构造辅助类。 + * + * @param 主表实体类型 + * @author Lion Li + */ +public final class LambdaJoinQueryBuilder { + + /** + * MyBatis-Plus-Join Lambda 查询包装器。 + */ + private final MPJLambdaWrapper wrapper; + + /** + * 构造 MPJ Lambda 联表查询构造辅助对象。 + * + * @param wrapper MPJ Lambda 查询包装器 + */ + LambdaJoinQueryBuilder(MPJLambdaWrapper wrapper) { + this.wrapper = wrapper; + } + + /** + * 添加去重查询。 + * + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder distinct() { + wrapper.distinct(); + return this; + } + + /** + * 指定主表查询字段。 + * + * @param columns 查询字段 + * @return 当前联表查询构造辅助对象 + */ + @SafeVarargs + public final LambdaJoinQueryBuilder select(SFunction... columns) { + wrapper.select(columns); + return this; + } + + /** + * 指定带表别名的同名映射查询字段。 + * + * @param alias 表别名 + * @param columns 查询字段 + * @return 当前联表查询构造辅助对象 + */ + @SafeVarargs + public final LambdaJoinQueryBuilder select(String alias, SFunction... columns) { + if (columns == null || columns.length == 0) { + return this; + } + Class entityClass = LambdaUtils.getEntityClass(columns[0]); + Map cacheMap = ColumnCache.getMapField(entityClass); + for (SFunction column : columns) { + wrapper.getSelectColum().add(new SelectNormal(cacheMap.get(LambdaUtils.getName(column)), wrapper.getIndex(), true, alias)); + } + return this; + } + + /** + * 查询主表全部字段。 + * + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder selectAll() { + wrapper.selectAll(); + return this; + } + + /** + * 查询指定实体全部字段。 + * + * @param entityClass 实体类型 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder selectAll(Class entityClass) { + wrapper.selectAll(entityClass); + return this; + } + + /** + * 查询指定别名实体全部字段。 + * + * @param entityClass 实体类型 + * @param alias 表别名 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder selectAll(Class entityClass, String alias) { + wrapper.selectAll(entityClass, alias); + return this; + } + + /** + * 指定查询字段并映射到返回对象字段。 + * + * @param column 查询字段 + * @param alias 返回对象字段 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder selectAs(SFunction column, SFunction alias) { + wrapper.selectAs(column, alias); + return this; + } + + /** + * 指定带表别名的查询字段并映射到返回对象字段。 + * + * @param tableAlias 表别名 + * @param column 查询字段 + * @param alias 返回对象字段 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder selectAs(String tableAlias, SFunction column, SFunction alias) { + wrapper.selectAs(tableAlias, column, alias); + return this; + } + + /** + * 指定 SQL 查询片段并映射到返回对象字段。 + * + * @param column SQL 查询片段 + * @param alias 返回对象字段 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder selectAs(String column, SFunction alias) { + wrapper.selectAs(column, alias); + return this; + } + + /** + * 添加左联表。 + * + * @param entityClass 关联实体类型 + * @param left 关联实体字段 + * @param right 当前查询字段 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder leftJoin(Class entityClass, SFunction left, SFunction right) { + wrapper.leftJoin(entityClass, left, right); + return this; + } + + /** + * 添加带别名的左联表。 + * + * @param entityClass 关联实体类型 + * @param alias 关联表别名 + * @param left 关联实体字段 + * @param right 当前查询字段 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder leftJoin(Class entityClass, String alias, SFunction left, SFunction right) { + wrapper.leftJoin(entityClass, alias, left, right); + return this; + } + + /** + * 添加等于条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param value 条件值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder eq(String alias, SFunction column, Object value) { + return eq(true, alias, column, value); + } + + /** + * 添加等于条件。 + * + * @param condition 是否添加该条件 + * @param alias 表别名 + * @param column 字段 + * @param value 条件值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder eq(boolean condition, String alias, SFunction column, Object value) { + wrapper.eq(condition, alias, column, value); + return this; + } + + /** + * 值不为空时添加等于条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param value 条件值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder eqIfPresent(String alias, SFunction column, Object value) { + return eq(value != null, alias, column, value); + } + + /** + * 文本不为空白时添加等于条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param value 条件值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder eqIfText(String alias, SFunction column, String value) { + return eq(StringUtils.isNotBlank(value), alias, column, value); + } + + /** + * 添加不等于条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param value 条件值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder ne(String alias, SFunction column, Object value) { + return ne(true, alias, column, value); + } + + /** + * 添加不等于条件。 + * + * @param condition 是否添加该条件 + * @param alias 表别名 + * @param column 字段 + * @param value 条件值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder ne(boolean condition, String alias, SFunction column, Object value) { + wrapper.ne(condition, alias, column, value); + return this; + } + + /** + * 文本不为空白时添加不等于条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param value 条件值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder neIfText(String alias, SFunction column, String value) { + return ne(StringUtils.isNotBlank(value), alias, column, value); + } + + /** + * 添加模糊匹配条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param value 条件值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder like(String alias, SFunction column, Object value) { + return like(true, alias, column, value); + } + + /** + * 添加模糊匹配条件。 + * + * @param condition 是否添加该条件 + * @param alias 表别名 + * @param column 字段 + * @param value 条件值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder like(boolean condition, String alias, SFunction column, Object value) { + wrapper.like(condition, alias, column, value); + return this; + } + + /** + * 文本不为空白时添加模糊匹配条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param value 条件值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder likeIfText(String alias, SFunction column, String value) { + return like(StringUtils.isNotBlank(value), alias, column, value); + } + + /** + * 添加区间条件。 + * + * @param condition 是否添加该条件 + * @param alias 表别名 + * @param column 字段 + * @param begin 起始值 + * @param end 结束值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder between(boolean condition, String alias, SFunction column, Object begin, Object end) { + wrapper.between(condition, alias, column, begin, end); + return this; + } + + /** + * 起止值均不为空时添加区间条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param begin 起始值 + * @param end 结束值 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder betweenIfPresent(String alias, SFunction column, Object begin, Object end) { + return between(begin != null && end != null, alias, column, begin, end); + } + + /** + * 从参数 Map 中读取起止值,均不为空时添加区间条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param params 参数 Map + * @param beginKey 起始值参数名 + * @param endKey 结束值参数名 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder betweenParams(String alias, SFunction column, Map params, String beginKey, String endKey) { + if (params == null) { + return between(false, alias, column, null, null); + } + Object begin = params.get(beginKey); + Object end = params.get(endKey); + return between(begin != null && end != null, alias, column, begin, end); + } + + /** + * 添加包含集合条件。 + * + * @param condition 是否添加该条件 + * @param alias 表别名 + * @param column 字段 + * @param values 条件值集合 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder in(boolean condition, String alias, SFunction column, Collection values) { + wrapper.in(condition, alias, column, values); + return this; + } + + /** + * 添加包含数组条件。 + * + * @param condition 是否添加该条件 + * @param alias 表别名 + * @param column 字段 + * @param values 条件值数组 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder in(boolean condition, String alias, SFunction column, Object... values) { + wrapper.in(condition, alias, column, values); + return this; + } + + /** + * 添加包含集合条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param values 条件值集合 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder in(String alias, SFunction column, Collection values) { + return in(true, alias, column, values); + } + + /** + * 添加包含数组条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param values 条件值数组 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder in(String alias, SFunction column, Object... values) { + return in(true, alias, column, values); + } + + /** + * 集合不为空时添加包含条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param values 条件值集合 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder inIfNotEmpty(String alias, SFunction column, Collection values) { + return in(values != null && !values.isEmpty(), alias, column, values); + } + + /** + * 数组不为空时添加包含条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param values 条件值数组 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder inIfNotEmpty(String alias, SFunction column, Object... values) { + return in(values != null && values.length > 0, alias, column, values); + } + + /** + * 添加不包含集合条件。 + * + * @param condition 是否添加该条件 + * @param alias 表别名 + * @param column 字段 + * @param values 条件值集合 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder notIn(boolean condition, String alias, SFunction column, Collection values) { + wrapper.notIn(condition, alias, column, values); + return this; + } + + /** + * 集合不为空时添加不包含条件。 + * + * @param alias 表别名 + * @param column 字段 + * @param values 条件值集合 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder notInIfNotEmpty(String alias, SFunction column, Collection values) { + return notIn(values != null && !values.isEmpty(), alias, column, values); + } + + /** + * 添加字段非空条件。 + * + * @param alias 表别名 + * @param column 字段 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder isNotNull(String alias, SFunction column) { + return isNotNull(true, alias, column); + } + + /** + * 添加字段非空条件。 + * + * @param condition 是否添加该条件 + * @param alias 表别名 + * @param column 字段 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder isNotNull(boolean condition, String alias, SFunction column) { + wrapper.isNotNull(condition, alias, column); + return this; + } + + /** + * 添加升序排序。 + * + * @param alias 表别名 + * @param column 字段 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder orderByAsc(String alias, SFunction column) { + wrapper.orderByAsc(alias, column); + return this; + } + + /** + * 添加降序排序。 + * + * @param alias 表别名 + * @param column 字段 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder orderByDesc(String alias, SFunction column) { + wrapper.orderByDesc(alias, column); + return this; + } + + /** + * 拼接 SQL 片段条件。 + * + * @param condition 是否添加该条件 + * @param applySql SQL 片段 + * @param values SQL 片段参数 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder apply(boolean condition, String applySql, Object... values) { + wrapper.apply(condition, applySql, values); + return this; + } + + /** + * 添加 FIND_IN_SET 条件。 + * + * @param condition 是否添加该条件 + * @param value 匹配值 + * @param column 字段名 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder findInSet(boolean condition, Object value, String column) { + return apply(condition, DataBaseHelper.findInSet(value, column)); + } + + /** + * 使用函数式方式追加 MPJ 原生能力。 + * + * @param consumer MPJ 查询包装器消费函数 + * @return 当前联表查询构造辅助对象 + */ + public LambdaJoinQueryBuilder apply(Consumer> consumer) { + consumer.accept(wrapper); + return this; + } + + /** + * 查询列表。 + * + * @param resultClass 返回对象类型 + * @param 返回对象类型 + * @return 查询结果 + */ + public List list(Class resultClass) { + return wrapper.list(resultClass); + } + + /** + * 分页查询。 + * + * @param page 分页对象 + * @param resultClass 返回对象类型 + * @param 返回对象类型 + * @param

分页类型 + * @return 分页结果 + */ + public > P page(P page, Class resultClass) { + return wrapper.page(page, resultClass); + } + + /** + * 查询数量。 + * + * @return 数量 + */ + public Long count() { + return wrapper.count(); + } + + /** + * 获取底层 MPJ Lambda 查询包装器。 + * + * @return MPJ Lambda 查询包装器 + */ + public MPJLambdaWrapper build() { + return wrapper; + } + +} 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 69a2e9660..7997603ca 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 @@ -1,6 +1,7 @@ package org.dromara.common.mybatis.core.query; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.github.yulichang.toolkit.JoinWrappers; /** * MyBatis-Plus 查询构造器入口。 @@ -23,4 +24,27 @@ public final class QueryBuilder { return new LambdaQueryBuilder<>(Wrappers.lambdaQuery(entityClass)); } + /** + * 创建 MPJ Lambda 联表查询构造辅助对象。 + * + * @param entityClass 主表实体类型 + * @param 主表实体类型 + * @return MPJ Lambda 联表查询构造辅助对象 + */ + public static LambdaJoinQueryBuilder lambdaJoin(Class entityClass) { + return new LambdaJoinQueryBuilder<>(JoinWrappers.lambda(entityClass)); + } + + /** + * 创建带主表别名的 MPJ Lambda 联表查询构造辅助对象。 + * + * @param alias 主表别名 + * @param entityClass 主表实体类型 + * @param 主表实体类型 + * @return MPJ Lambda 联表查询构造辅助对象 + */ + public static LambdaJoinQueryBuilder lambdaJoin(String alias, Class entityClass) { + return new LambdaJoinQueryBuilder<>(JoinWrappers.lambda(alias, entityClass)); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java index 74bbdfe47..7bb170084 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysDeptMapper.java @@ -3,11 +3,11 @@ package org.dromara.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.base.MPJBaseMapper; -import com.github.yulichang.toolkit.JoinWrappers; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.system.domain.SysDept; import org.dromara.system.domain.SysRole; import org.dromara.system.domain.SysRoleDept; @@ -101,7 +101,7 @@ public interface SysDeptMapper extends BaseMapperPlus, MPJBa * @return 选中部门列表 */ default List selectDeptListByRoleId(Long roleId, boolean deptCheckStrictly) { - List depts = this.selectJoinList(SysDept.class, JoinWrappers.lambda("d", SysDept.class) + List depts = this.selectJoinList(SysDept.class, QueryBuilder.lambdaJoin("d", SysDept.class) .distinct() .select(SysDept::getDeptId, SysDept::getParentId, SysDept::getOrderNum) .leftJoin(SysRoleDept.class, "srd", SysRoleDept::getDeptId, SysDept::getDeptId) @@ -109,7 +109,8 @@ public interface SysDeptMapper extends BaseMapperPlus, MPJBa .eq("srd", SysRoleDept::getRoleId, roleId) .eq("sr", SysRole::getStatus, NORMAL) .orderByAsc("d", SysDept::getParentId) - .orderByAsc("d", SysDept::getOrderNum)); + .orderByAsc("d", SysDept::getOrderNum) + .build()); Set parentIds = deptCheckStrictly ? new HashSet<>(StreamUtils.toList(depts, SysDept::getParentId)) : Collections.emptySet(); return depts.stream() .map(SysDept::getDeptId) 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 cd1539ead..36ea01858 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 @@ -2,11 +2,11 @@ package org.dromara.system.mapper; import cn.hutool.core.collection.CollUtil; import com.github.yulichang.base.MPJBaseMapper; -import com.github.yulichang.toolkit.JoinWrappers; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.system.domain.SysMenu; import org.dromara.system.domain.SysRole; import org.dromara.system.domain.SysRoleMenu; @@ -31,7 +31,7 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa * @return 权限列表 */ default Set selectMenuPermsByUserId(Long userId) { - List list = this.selectJoinList(SysMenu.class, JoinWrappers.lambda("m", SysMenu.class) + List list = this.selectJoinList(SysMenu.class, QueryBuilder.lambdaJoin("m", SysMenu.class) .distinct() .select(SysMenu::getPerms) .leftJoin(SysRoleMenu.class, "srm", SysRoleMenu::getMenuId, SysMenu::getMenuId) @@ -39,7 +39,8 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa .leftJoin(SysRole.class, "sr", SysRole::getRoleId, SysRoleMenu::getRoleId) .eq("sur", SysUserRole::getUserId, userId) .eq("sr", SysRole::getStatus, SystemConstants.NORMAL) - .isNotNull("m", SysMenu::getPerms)); + .isNotNull("m", SysMenu::getPerms) + .build()); return new HashSet<>(StreamUtils.filter(StreamUtils.toList(list, SysMenu::getPerms), StringUtils::isNotBlank)); } @@ -50,14 +51,15 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa * @return 权限列表 */ default Set selectMenuPermsByRoleId(Long roleId) { - List list = this.selectJoinList(SysMenu.class, JoinWrappers.lambda("m", SysMenu.class) + List list = this.selectJoinList(SysMenu.class, QueryBuilder.lambdaJoin("m", SysMenu.class) .distinct() .select(SysMenu::getPerms) .leftJoin(SysRoleMenu.class, "srm", SysRoleMenu::getMenuId, SysMenu::getMenuId) .leftJoin(SysRole.class, "sr", SysRole::getRoleId, SysRoleMenu::getRoleId) .eq("srm", SysRoleMenu::getRoleId, roleId) .eq("sr", SysRole::getStatus, SystemConstants.NORMAL) - .isNotNull("m", SysMenu::getPerms)); + .isNotNull("m", SysMenu::getPerms) + .build()); return new HashSet<>(StreamUtils.filter(StreamUtils.toList(list, SysMenu::getPerms), StringUtils::isNotBlank)); } @@ -71,7 +73,7 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa if (CollUtil.isEmpty(roleIds)) { return Map.of(); } - List list = this.selectJoinList(SysRoleMenuPermVo.class, JoinWrappers.lambda("m", SysMenu.class) + List list = this.selectJoinList(SysRoleMenuPermVo.class, QueryBuilder.lambdaJoin("m", SysMenu.class) .distinct() .selectAs("srm", SysRoleMenu::getRoleId, SysRoleMenuPermVo::getRoleId) .selectAs(SysMenu::getPerms, SysRoleMenuPermVo::getPerms) @@ -79,7 +81,8 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa .leftJoin(SysRole.class, "sr", SysRole::getRoleId, SysRoleMenu::getRoleId) .in("srm", SysRoleMenu::getRoleId, roleIds) .eq("sr", SysRole::getStatus, SystemConstants.NORMAL) - .isNotNull("m", SysMenu::getPerms)); + .isNotNull("m", SysMenu::getPerms) + .build()); Map> result = new LinkedHashMap<>(); for (SysRoleMenuPermVo item : list) { if (StringUtils.isBlank(item.getPerms())) { @@ -112,7 +115,7 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa * @return 选中菜单列表 */ default List selectMenuListByRoleId(Long roleId, boolean menuCheckStrictly) { - List menus = this.selectJoinList(SysMenu.class, JoinWrappers.lambda("m", SysMenu.class) + List menus = this.selectJoinList(SysMenu.class, QueryBuilder.lambdaJoin("m", SysMenu.class) .distinct() .select(SysMenu::getMenuId, SysMenu::getParentId, SysMenu::getOrderNum) .leftJoin(SysRoleMenu.class, "srm", SysRoleMenu::getMenuId, SysMenu::getMenuId) @@ -120,7 +123,8 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa .eq("srm", SysRoleMenu::getRoleId, roleId) .eq("sr", SysRole::getStatus, SystemConstants.NORMAL) .orderByAsc("m", SysMenu::getParentId) - .orderByAsc("m", SysMenu::getOrderNum)); + .orderByAsc("m", SysMenu::getOrderNum) + .build()); Set parentIds = menuCheckStrictly ? new HashSet<>(StreamUtils.toList(menus, SysMenu::getParentId)) : Collections.emptySet(); return menus.stream() .map(SysMenu::getMenuId) @@ -136,7 +140,7 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa * @return 菜单列表 */ default List selectMenuListByUserId(SysMenuBo menu, Long userId) { - return this.selectJoinList(SysMenuVo.class, JoinWrappers.lambda("m", SysMenu.class) + return this.selectJoinList(SysMenuVo.class, QueryBuilder.lambdaJoin("m", SysMenu.class) .distinct() .selectAll(SysMenu.class) .leftJoin(SysRoleMenu.class, "srm", SysRoleMenu::getMenuId, SysMenu::getMenuId) @@ -144,13 +148,14 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa .leftJoin(SysRole.class, "sr", SysRole::getRoleId, SysRoleMenu::getRoleId) .eq("sur", SysUserRole::getUserId, userId) .eq("sr", SysRole::getStatus, SystemConstants.NORMAL) - .like(StringUtils.isNotBlank(menu.getMenuName()), "m", SysMenu::getMenuName, menu.getMenuName()) - .eq(StringUtils.isNotBlank(menu.getVisible()), "m", SysMenu::getVisible, menu.getVisible()) - .eq(StringUtils.isNotBlank(menu.getStatus()), "m", SysMenu::getStatus, menu.getStatus()) - .eq(StringUtils.isNotBlank(menu.getMenuType()), "m", SysMenu::getMenuType, menu.getMenuType()) - .eq(Objects.nonNull(menu.getParentId()), "m", SysMenu::getParentId, menu.getParentId()) + .likeIfText("m", SysMenu::getMenuName, menu.getMenuName()) + .eqIfText("m", SysMenu::getVisible, menu.getVisible()) + .eqIfText("m", SysMenu::getStatus, menu.getStatus()) + .eqIfText("m", SysMenu::getMenuType, menu.getMenuType()) + .eqIfPresent("m", SysMenu::getParentId, menu.getParentId()) .orderByAsc("m", SysMenu::getParentId) - .orderByAsc("m", SysMenu::getOrderNum)); + .orderByAsc("m", SysMenu::getOrderNum) + .build()); } /** @@ -160,7 +165,7 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa * @return 菜单列表 */ default List selectMenuTreeByUserId(Long userId) { - return this.selectJoinList(SysMenu.class, JoinWrappers.lambda("m", SysMenu.class) + return this.selectJoinList(SysMenu.class, QueryBuilder.lambdaJoin("m", SysMenu.class) .distinct() .selectAll(SysMenu.class) .leftJoin(SysRoleMenu.class, "srm", SysRoleMenu::getMenuId, SysMenu::getMenuId) @@ -171,7 +176,8 @@ public interface SysMenuMapper extends BaseMapperPlus, MPJBa .in("m", SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU) .eq("m", SysMenu::getStatus, SystemConstants.NORMAL) .orderByAsc("m", SysMenu::getParentId) - .orderByAsc("m", SysMenu::getOrderNum)); + .orderByAsc("m", SysMenu::getOrderNum) + .build()); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java index ace0a4829..1265c666b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysPostMapper.java @@ -3,10 +3,10 @@ package org.dromara.system.mapper; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.base.MPJBaseMapper; -import com.github.yulichang.toolkit.JoinWrappers; import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.system.domain.SysPost; import org.dromara.system.domain.SysUserPost; import org.dromara.system.domain.vo.SysPostVo; @@ -71,10 +71,11 @@ public interface SysPostMapper extends BaseMapperPlus, MPJBa * @return 岗位信息列表 */ default List selectPostsByUserId(Long userId) { - return this.selectJoinList(SysPostVo.class, JoinWrappers.lambda("p", SysPost.class) + return this.selectJoinList(SysPostVo.class, QueryBuilder.lambdaJoin("p", SysPost.class) .selectAll(SysPost.class) .leftJoin(SysUserPost.class, "sup", SysUserPost::getPostId, SysPost::getPostId) - .eq("sup", SysUserPost::getUserId, userId)); + .eq("sup", SysUserPost::getUserId, userId) + .build()); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java index b2242832e..9c9c6ec22 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/mapper/SysRoleMapper.java @@ -4,11 +4,11 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.base.MPJBaseMapper; -import com.github.yulichang.toolkit.JoinWrappers; import org.apache.ibatis.annotations.Param; import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.system.domain.SysRole; import org.dromara.system.domain.SysUserRole; import org.dromara.system.domain.vo.SysRoleVo; @@ -87,11 +87,12 @@ public interface SysRoleMapper extends BaseMapperPlus, MPJBa * @return 角色列表 */ default List selectRolesByUserId(Long userId) { - return this.selectJoinList(SysRoleVo.class, JoinWrappers.lambda("r", SysRole.class) + return this.selectJoinList(SysRoleVo.class, QueryBuilder.lambdaJoin("r", SysRole.class) .select(SysRole::getRoleId, SysRole::getRoleName, SysRole::getRoleKey, SysRole::getRoleSort, SysRole::getDataScope, SysRole::getStatus) .leftJoin(SysUserRole.class, "sur", SysUserRole::getRoleId, SysRole::getRoleId) - .eq("sur", SysUserRole::getUserId, userId)); + .eq("sur", SysUserRole::getUserId, userId) + .build()); } } 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 a81d0ca31..9c6c6834f 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 @@ -4,14 +4,12 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.base.MPJBaseMapper; -import com.github.yulichang.toolkit.JoinWrappers; import com.github.yulichang.wrapper.MPJLambdaWrapper; import org.apache.ibatis.annotations.Param; -import org.dromara.common.core.constant.SystemConstants; -import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.annotation.DataColumn; import org.dromara.common.mybatis.annotation.DataPermission; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.system.domain.SysDept; import org.dromara.system.domain.SysRole; import org.dromara.system.domain.SysUser; @@ -70,20 +68,19 @@ public interface SysUserMapper extends BaseMapperPlus, MPJBa @DataColumn(key = "userName", value = "u.create_by") }) default List selectUserExportList(SysUserBo user, List deptIds) { - MPJLambdaWrapper wrapper = JoinWrappers.lambda("u", SysUser.class) + MPJLambdaWrapper wrapper = QueryBuilder.lambdaJoin("u", SysUser.class) .selectAll(SysUser.class) .selectAs("u1", SysUser::getUserName, SysUserExportVo::getLeaderName) .leftJoin(SysDept.class, "d", SysDept::getDeptId, SysUser::getDeptId) .leftJoin(SysUser.class, "u1", SysUser::getUserId, SysDept::getLeader) - .eq("u", SysUser::getDelFlag, SystemConstants.NORMAL) - .like(StringUtils.isNotBlank(user.getUserName()), "u", SysUser::getUserName, user.getUserName()) - .like(StringUtils.isNotBlank(user.getNickName()), "u", SysUser::getNickName, user.getNickName()) - .eq(StringUtils.isNotBlank(user.getStatus()), "u", SysUser::getStatus, user.getStatus()) - .like(StringUtils.isNotBlank(user.getPhoneNumber()), "u", SysUser::getPhoneNumber, user.getPhoneNumber()) - .between(user.getParams().get("beginTime") != null && user.getParams().get("endTime") != null, - "u", SysUser::getCreateTime, user.getParams().get("beginTime"), user.getParams().get("endTime")) - .in(deptIds != null && !deptIds.isEmpty(), "u", SysUser::getDeptId, deptIds) - .orderByAsc("u", SysUser::getUserId); + .likeIfText("u", SysUser::getUserName, user.getUserName()) + .likeIfText("u", SysUser::getNickName, user.getNickName()) + .eqIfText("u", SysUser::getStatus, user.getStatus()) + .likeIfText("u", SysUser::getPhoneNumber, user.getPhoneNumber()) + .betweenParams("u", SysUser::getCreateTime, user.getParams(), "beginTime", "endTime") + .inIfNotEmpty("u", SysUser::getDeptId, deptIds) + .orderByAsc("u", SysUser::getUserId) + .build(); return this.selectJoinList(SysUserExportVo.class, wrapper); } @@ -172,16 +169,16 @@ public interface SysUserMapper extends BaseMapperPlus, MPJBa * @return 用户角色关联查询包装器 */ default MPJLambdaWrapper buildUserRoleJoinWrapper(SysUserBo user) { - return JoinWrappers.lambda("u", SysUser.class) + return QueryBuilder.lambdaJoin("u", SysUser.class) .distinct() .selectAll(SysUser.class) .leftJoin(SysDept.class, "d", SysDept::getDeptId, SysUser::getDeptId) .leftJoin(SysUserRole.class, "sur", SysUserRole::getUserId, SysUser::getUserId) .leftJoin(SysRole.class, "r", SysRole::getRoleId, SysUserRole::getRoleId) - .eq("u", SysUser::getDelFlag, SystemConstants.NORMAL) - .like(StringUtils.isNotBlank(user.getUserName()), "u", SysUser::getUserName, user.getUserName()) - .eq(StringUtils.isNotBlank(user.getStatus()), "u", SysUser::getStatus, user.getStatus()) - .like(StringUtils.isNotBlank(user.getPhoneNumber()), "u", SysUser::getPhoneNumber, user.getPhoneNumber()); + .likeIfText("u", SysUser::getUserName, user.getUserName()) + .eqIfText("u", SysUser::getStatus, user.getStatus()) + .likeIfText("u", SysUser::getPhoneNumber, user.getPhoneNumber()) + .build(); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwHisTaskMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwHisTaskMapper.java index 17ad73d71..9cc062383 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwHisTaskMapper.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwHisTaskMapper.java @@ -1,12 +1,9 @@ package org.dromara.workflow.mapper; -import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.base.MPJBaseMapper; -import com.github.yulichang.toolkit.JoinWrappers; -import com.github.yulichang.wrapper.MPJLambdaWrapper; -import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.warm.flow.core.enums.NodeType; import org.dromara.warm.flow.orm.entity.FlowDefinition; import org.dromara.warm.flow.orm.entity.FlowHisTask; @@ -19,8 +16,6 @@ import org.dromara.workflow.domain.vo.FlowHisTaskVo; import java.util.List; import java.util.Map; -import static org.dromara.workflow.common.constant.FlowConstant.NOT_DELETED; - /** * 任务信息Mapper接口 * @@ -31,55 +26,35 @@ public interface FlwHisTaskMapper extends BaseMapperPlus getListFinishTask(Page page, FlowTaskBo bo, List categoryIds, String userId) { Map params = bo.getParams(); - MPJLambdaWrapper wrapper = JoinWrappers.lambda("a", FlowHisTask.class) - .selectAs(FlowHisTask::getId, FlowHisTaskVo::getId) - .selectAs(FlowHisTask::getNodeCode, FlowHisTaskVo::getNodeCode) - .selectAs(FlowHisTask::getNodeName, FlowHisTaskVo::getNodeName) - .selectAs(FlowHisTask::getCooperateType, FlowHisTaskVo::getCooperateType) - .selectAs(FlowHisTask::getApprover, FlowHisTaskVo::getApprover) - .selectAs(FlowHisTask::getCollaborator, FlowHisTaskVo::getCollaborator) - .selectAs(FlowHisTask::getNodeType, FlowHisTaskVo::getNodeType) - .selectAs(FlowHisTask::getTargetNodeCode, FlowHisTaskVo::getTargetNodeCode) - .selectAs(FlowHisTask::getTargetNodeName, FlowHisTaskVo::getTargetNodeName) - .selectAs(FlowHisTask::getDefinitionId, FlowHisTaskVo::getDefinitionId) - .selectAs(FlowHisTask::getInstanceId, FlowHisTaskVo::getInstanceId) + return QueryBuilder.lambdaJoin("a", FlowHisTask.class) + .select(FlowHisTask::getId, FlowHisTask::getNodeCode, FlowHisTask::getNodeName, + FlowHisTask::getCooperateType, FlowHisTask::getApprover, FlowHisTask::getCollaborator, + FlowHisTask::getNodeType, FlowHisTask::getTargetNodeCode, FlowHisTask::getTargetNodeName, + FlowHisTask::getDefinitionId, FlowHisTask::getInstanceId) .selectAs(FlowHisTask::getFlowStatus, FlowHisTaskVo::getFlowTaskStatus) - .selectAs(FlowHisTask::getMessage, FlowHisTaskVo::getMessage) - .selectAs(FlowHisTask::getExt, FlowHisTaskVo::getExt) - .selectAs(FlowHisTask::getCreateTime, FlowHisTaskVo::getCreateTime) - .selectAs(FlowHisTask::getUpdateTime, FlowHisTaskVo::getUpdateTime) - .selectAs(FlowHisTask::getFormCustom, FlowHisTaskVo::getFormCustom) - .selectAs(FlowHisTask::getFormPath, FlowHisTaskVo::getFormPath) - .selectAs("b", FlowInstance::getFlowStatus, FlowHisTaskVo::getFlowStatus) - .selectAs("b", FlowInstance::getBusinessId, FlowHisTaskVo::getBusinessId) - .selectAs("b", FlowInstance::getCreateBy, FlowHisTaskVo::getCreateBy) - .selectAs("c", FlowDefinition::getFlowName, FlowHisTaskVo::getFlowName) - .selectAs("c", FlowDefinition::getFlowCode, FlowHisTaskVo::getFlowCode) - .selectAs("c", FlowDefinition::getCategory, FlowHisTaskVo::getCategory) - .selectAs("c", FlowDefinition::getVersion, FlowHisTaskVo::getVersion) - .selectAs("biz", FlowInstanceBizExt::getBusinessCode, FlowHisTaskVo::getBusinessCode) - .selectAs("biz", FlowInstanceBizExt::getBusinessTitle, FlowHisTaskVo::getBusinessTitle) + .select(FlowHisTask::getMessage, FlowHisTask::getExt, FlowHisTask::getCreateTime, + FlowHisTask::getUpdateTime, FlowHisTask::getFormCustom, FlowHisTask::getFormPath) + .select("b", FlowInstance::getFlowStatus, FlowInstance::getBusinessId, FlowInstance::getCreateBy) + .select("c", FlowDefinition::getFlowName, FlowDefinition::getFlowCode, FlowDefinition::getCategory, + FlowDefinition::getVersion) + .select("biz", FlowInstanceBizExt::getBusinessCode, FlowInstanceBizExt::getBusinessTitle) .leftJoin(FlowInstance.class, "b", FlowInstance::getId, FlowHisTask::getInstanceId) .leftJoin(FlowDefinition.class, "c", FlowDefinition::getId, FlowHisTask::getDefinitionId) .leftJoin(FlowInstanceBizExt.class, "biz", FlowInstanceBizExt::getInstanceId, FlowInstance::getId) - .eq("a", FlowHisTask::getDelFlag, NOT_DELETED) - .eq("b", FlowInstance::getDelFlag, NOT_DELETED) - .eq("c", FlowDefinition::getDelFlag, NOT_DELETED) .in("a", FlowHisTask::getNodeType, List.of("1", "3", "4")) .ne("a", FlowHisTask::getFlowStatus, TaskStatusEnum.COPY.getStatus()) - .like(StringUtils.isNotBlank(bo.getNodeName()), "a", FlowHisTask::getNodeName, bo.getNodeName()) - .like(StringUtils.isNotBlank(bo.getFlowName()), "c", FlowDefinition::getFlowName, bo.getFlowName()) - .like(StringUtils.isNotBlank(bo.getFlowCode()), "c", FlowDefinition::getFlowCode, bo.getFlowCode()) - .like(StringUtils.isNotBlank(bo.getFlowStatus()), "b", FlowInstance::getFlowStatus, bo.getFlowStatus()) - .in(CollUtil.isNotEmpty(bo.getCreateByIds()), "b", FlowInstance::getCreateBy, bo.getCreateByIds()) - .in(CollUtil.isNotEmpty(categoryIds), "c", FlowDefinition::getCategory, categoryIds) - .between(params.get("beginTime") != null && params.get("endTime") != null, - "a", FlowHisTask::getCreateTime, params.get("beginTime"), params.get("endTime")) - .eq(StringUtils.isNotBlank(userId), "a", FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()) - .eq(StringUtils.isNotBlank(userId), "a", FlowHisTask::getApprover, userId) + .likeIfText("a", FlowHisTask::getNodeName, bo.getNodeName()) + .likeIfText("c", FlowDefinition::getFlowName, bo.getFlowName()) + .likeIfText("c", FlowDefinition::getFlowCode, bo.getFlowCode()) + .likeIfText("b", FlowInstance::getFlowStatus, bo.getFlowStatus()) + .inIfNotEmpty("b", FlowInstance::getCreateBy, bo.getCreateByIds()) + .inIfNotEmpty("c", FlowDefinition::getCategory, categoryIds) + .betweenParams("a", FlowHisTask::getCreateTime, params, "beginTime", "endTime") + .eq(userId != null && !userId.isBlank(), "a", FlowHisTask::getNodeType, NodeType.BETWEEN.getKey()) + .eqIfText("a", FlowHisTask::getApprover, userId) .orderByDesc("a", FlowHisTask::getCreateTime) - .orderByDesc("a", FlowHisTask::getUpdateTime); - return wrapper.page(page, FlowHisTaskVo.class); + .orderByDesc("a", FlowHisTask::getUpdateTime) + .page(page, FlowHisTaskVo.class); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwTaskMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwTaskMapper.java index bb8ab4744..ceb96457e 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwTaskMapper.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/FlwTaskMapper.java @@ -1,13 +1,10 @@ package org.dromara.workflow.mapper; -import cn.hutool.core.collection.CollUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.base.MPJBaseMapper; -import com.github.yulichang.toolkit.JoinWrappers; -import com.github.yulichang.wrapper.MPJLambdaWrapper; import org.dromara.common.core.enums.BusinessStatusEnum; -import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.warm.flow.core.enums.NodeType; import org.dromara.warm.flow.orm.entity.*; import org.dromara.workflow.domain.FlowInstanceBizExt; @@ -17,8 +14,6 @@ import org.dromara.workflow.domain.vo.FlowTaskVo; import java.util.List; import java.util.Map; -import static org.dromara.workflow.common.constant.FlowConstant.NOT_DELETED; - /** * 任务信息Mapper接口 * @@ -29,92 +24,62 @@ public interface FlwTaskMapper extends BaseMapperPlus, MPJ default Page getListRunTask(Page page, FlowTaskBo bo, List categoryIds, String userId) { Map params = bo.getParams(); - MPJLambdaWrapper wrapper = JoinWrappers.lambda("t", FlowTask.class) + return QueryBuilder.lambdaJoin("t", FlowTask.class) .distinct() - .selectAs(FlowTask::getId, FlowTaskVo::getId) - .selectAs(FlowTask::getNodeCode, FlowTaskVo::getNodeCode) - .selectAs(FlowTask::getNodeName, FlowTaskVo::getNodeName) - .selectAs(FlowTask::getNodeType, FlowTaskVo::getNodeType) - .selectAs(FlowTask::getDefinitionId, FlowTaskVo::getDefinitionId) - .selectAs(FlowTask::getInstanceId, FlowTaskVo::getInstanceId) - .selectAs(FlowTask::getCreateTime, FlowTaskVo::getCreateTime) - .selectAs(FlowTask::getUpdateTime, FlowTaskVo::getUpdateTime) - .selectAs("i", FlowInstance::getBusinessId, FlowTaskVo::getBusinessId) - .selectAs("i", FlowInstance::getFlowStatus, FlowTaskVo::getFlowStatus) - .selectAs("i", FlowInstance::getCreateBy, FlowTaskVo::getCreateBy) - .selectAs("d", FlowDefinition::getFlowName, FlowTaskVo::getFlowName) - .selectAs("d", FlowDefinition::getFlowCode, FlowTaskVo::getFlowCode) - .selectAs("d", FlowDefinition::getFormCustom, FlowTaskVo::getFormCustom) - .selectAs("d", FlowDefinition::getCategory, FlowTaskVo::getCategory) - .selectAs("d", FlowDefinition::getVersion, FlowTaskVo::getVersion) - .selectAs("uu", FlowUser::getProcessedBy, FlowTaskVo::getProcessedBy) - .selectAs("uu", FlowUser::getType, FlowTaskVo::getType) - .selectAs("biz", FlowInstanceBizExt::getBusinessCode, FlowTaskVo::getBusinessCode) - .selectAs("biz", FlowInstanceBizExt::getBusinessTitle, FlowTaskVo::getBusinessTitle) + .select(FlowTask::getId, FlowTask::getNodeCode, FlowTask::getNodeName, FlowTask::getNodeType, + FlowTask::getDefinitionId, FlowTask::getInstanceId, FlowTask::getCreateTime, FlowTask::getUpdateTime) + .select("i", FlowInstance::getBusinessId, FlowInstance::getFlowStatus, FlowInstance::getCreateBy) + .select("d", FlowDefinition::getFlowName, FlowDefinition::getFlowCode, FlowDefinition::getFormCustom, + FlowDefinition::getCategory, FlowDefinition::getVersion) + .select("uu", FlowUser::getProcessedBy, FlowUser::getType) + .select("biz", FlowInstanceBizExt::getBusinessCode, FlowInstanceBizExt::getBusinessTitle) .selectAs("COALESCE(NULLIF(TRIM(t.form_path), ''), NULLIF(TRIM(d.form_path), ''))", FlowTaskVo::getFormPath) .leftJoin(FlowUser.class, "uu", FlowUser::getAssociated, FlowTask::getId) .leftJoin(FlowDefinition.class, "d", FlowDefinition::getId, FlowTask::getDefinitionId) .leftJoin(FlowInstance.class, "i", FlowInstance::getId, FlowTask::getInstanceId) .leftJoin(FlowInstanceBizExt.class, "biz", FlowInstanceBizExt::getInstanceId, FlowInstance::getId) .eq("t", FlowTask::getNodeType, NodeType.BETWEEN.getKey()) - .eq("t", FlowTask::getDelFlag, NOT_DELETED) - .eq("uu", FlowUser::getDelFlag, NOT_DELETED) .in("uu", FlowUser::getType, List.of("1", "2", "3")) - .like(StringUtils.isNotBlank(bo.getNodeName()), "t", FlowTask::getNodeName, bo.getNodeName()) - .like(StringUtils.isNotBlank(bo.getFlowName()), "d", FlowDefinition::getFlowName, bo.getFlowName()) - .like(StringUtils.isNotBlank(bo.getFlowCode()), "d", FlowDefinition::getFlowCode, bo.getFlowCode()) - .like(StringUtils.isNotBlank(bo.getFlowStatus()), "i", FlowInstance::getFlowStatus, bo.getFlowStatus()) - .in(CollUtil.isNotEmpty(bo.getCreateByIds()), "i", FlowInstance::getCreateBy, bo.getCreateByIds()) - .in(CollUtil.isNotEmpty(categoryIds), "d", FlowDefinition::getCategory, categoryIds) - .between(params.get("beginTime") != null && params.get("endTime") != null, - "t", FlowTask::getCreateTime, params.get("beginTime"), params.get("endTime")) - .eq(StringUtils.isNotBlank(userId), "uu", FlowUser::getProcessedBy, userId) - .eq(StringUtils.isNotBlank(userId), "i", FlowInstance::getFlowStatus, BusinessStatusEnum.WAITING.getStatus()) + .likeIfText("t", FlowTask::getNodeName, bo.getNodeName()) + .likeIfText("d", FlowDefinition::getFlowName, bo.getFlowName()) + .likeIfText("d", FlowDefinition::getFlowCode, bo.getFlowCode()) + .likeIfText("i", FlowInstance::getFlowStatus, bo.getFlowStatus()) + .inIfNotEmpty("i", FlowInstance::getCreateBy, bo.getCreateByIds()) + .inIfNotEmpty("d", FlowDefinition::getCategory, categoryIds) + .betweenParams("t", FlowTask::getCreateTime, params, "beginTime", "endTime") + .eqIfText("uu", FlowUser::getProcessedBy, userId) + .eq(userId != null && !userId.isBlank(), "i", FlowInstance::getFlowStatus, BusinessStatusEnum.WAITING.getStatus()) .orderByDesc("t", FlowTask::getCreateTime) - .orderByDesc("t", FlowTask::getUpdateTime); - return wrapper.page(page, FlowTaskVo.class); + .orderByDesc("t", FlowTask::getUpdateTime) + .page(page, FlowTaskVo.class); } default Page getTaskCopyByPage(Page page, FlowTaskBo bo, List categoryIds, String userId) { Map params = bo.getParams(); - MPJLambdaWrapper wrapper = JoinWrappers.lambda("a", FlowUser.class) - .selectAs("b", FlowHisTask::getId, FlowTaskVo::getId) - .selectAs("b", FlowHisTask::getUpdateTime, FlowTaskVo::getUpdateTime) - .selectAs("c", FlowInstance::getBusinessId, FlowTaskVo::getBusinessId) - .selectAs("c", FlowInstance::getFlowStatus, FlowTaskVo::getFlowStatus) - .selectAs("c", FlowInstance::getCreateBy, FlowTaskVo::getCreateBy) - .selectAs(FlowUser::getProcessedBy, FlowTaskVo::getProcessedBy) - .selectAs(FlowUser::getCreateTime, FlowTaskVo::getCreateTime) - .selectAs("b", FlowHisTask::getFormCustom, FlowTaskVo::getFormCustom) - .selectAs("b", FlowHisTask::getFormPath, FlowTaskVo::getFormPath) - .selectAs("b", FlowHisTask::getNodeName, FlowTaskVo::getNodeName) - .selectAs("b", FlowHisTask::getNodeCode, FlowTaskVo::getNodeCode) - .selectAs("d", FlowDefinition::getFlowName, FlowTaskVo::getFlowName) - .selectAs("d", FlowDefinition::getFlowCode, FlowTaskVo::getFlowCode) - .selectAs("d", FlowDefinition::getCategory, FlowTaskVo::getCategory) - .selectAs("d", FlowDefinition::getVersion, FlowTaskVo::getVersion) - .selectAs("biz", FlowInstanceBizExt::getBusinessCode, FlowTaskVo::getBusinessCode) - .selectAs("biz", FlowInstanceBizExt::getBusinessTitle, FlowTaskVo::getBusinessTitle) + return QueryBuilder.lambdaJoin("a", FlowUser.class) + .select(FlowUser::getProcessedBy, FlowUser::getCreateTime) + .select("b", FlowHisTask::getId, FlowHisTask::getUpdateTime, FlowHisTask::getFormCustom, + FlowHisTask::getFormPath, FlowHisTask::getNodeName, FlowHisTask::getNodeCode) + .select("c", FlowInstance::getBusinessId, FlowInstance::getFlowStatus, FlowInstance::getCreateBy) + .select("d", FlowDefinition::getFlowName, FlowDefinition::getFlowCode, FlowDefinition::getCategory, + FlowDefinition::getVersion) + .select("biz", FlowInstanceBizExt::getBusinessCode, FlowInstanceBizExt::getBusinessTitle) .leftJoin(FlowHisTask.class, "b", FlowHisTask::getTaskId, FlowUser::getAssociated) .leftJoin(FlowInstance.class, "c", FlowInstance::getId, FlowHisTask::getInstanceId) .leftJoin(FlowDefinition.class, "d", FlowDefinition::getId, FlowInstance::getDefinitionId) .leftJoin(FlowInstanceBizExt.class, "biz", FlowInstanceBizExt::getInstanceId, FlowInstance::getId) .eq("a", FlowUser::getType, "4") - .eq("a", FlowUser::getDelFlag, NOT_DELETED) - .eq("b", FlowHisTask::getDelFlag, NOT_DELETED) - .eq("d", FlowDefinition::getDelFlag, NOT_DELETED) - .like(StringUtils.isNotBlank(bo.getNodeName()), "b", FlowHisTask::getNodeName, bo.getNodeName()) - .like(StringUtils.isNotBlank(bo.getFlowName()), "d", FlowDefinition::getFlowName, bo.getFlowName()) - .like(StringUtils.isNotBlank(bo.getFlowCode()), "d", FlowDefinition::getFlowCode, bo.getFlowCode()) - .like(StringUtils.isNotBlank(bo.getFlowStatus()), "c", FlowInstance::getFlowStatus, bo.getFlowStatus()) - .in(CollUtil.isNotEmpty(bo.getCreateByIds()), "c", FlowInstance::getCreateBy, bo.getCreateByIds()) - .in(CollUtil.isNotEmpty(categoryIds), "d", FlowDefinition::getCategory, categoryIds) - .between(params.get("beginTime") != null && params.get("endTime") != null, - "a", FlowUser::getCreateTime, params.get("beginTime"), params.get("endTime")) - .eq(StringUtils.isNotBlank(userId), "a", FlowUser::getProcessedBy, userId) + .likeIfText("b", FlowHisTask::getNodeName, bo.getNodeName()) + .likeIfText("d", FlowDefinition::getFlowName, bo.getFlowName()) + .likeIfText("d", FlowDefinition::getFlowCode, bo.getFlowCode()) + .likeIfText("c", FlowInstance::getFlowStatus, bo.getFlowStatus()) + .inIfNotEmpty("c", FlowInstance::getCreateBy, bo.getCreateByIds()) + .inIfNotEmpty("d", FlowDefinition::getCategory, categoryIds) + .betweenParams("a", FlowUser::getCreateTime, params, "beginTime", "endTime") + .eqIfText("a", FlowUser::getProcessedBy, userId) .orderByDesc("a", FlowUser::getCreateTime) - .orderByDesc("b", FlowHisTask::getUpdateTime); - return wrapper.page(page, FlowTaskVo.class); + .orderByDesc("b", FlowHisTask::getUpdateTime) + .page(page, FlowTaskVo.class); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java index 87be5598a..e1dccaae8 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwInstanceServiceImpl.java @@ -6,7 +6,6 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; -import com.github.yulichang.toolkit.JoinWrappers; import com.github.yulichang.wrapper.MPJLambdaWrapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -16,6 +15,7 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.query.LambdaJoinQueryBuilder; import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.warm.flow.core.FlowEngine; @@ -138,43 +138,32 @@ public class FlwInstanceServiceImpl implements IFlwInstanceService { * @return 查询条件构造方法 */ private MPJLambdaWrapper buildQueryWrapper(FlowInstanceBo flowInstanceBo) { - MPJLambdaWrapper queryWrapper = JoinWrappers.lambda("fi", FlowInstance.class) - .selectAs(FlowInstance::getId, FlowInstanceVo::getId) - .selectAs(FlowInstance::getCreateTime, FlowInstanceVo::getCreateTime) - .selectAs(FlowInstance::getUpdateTime, FlowInstanceVo::getUpdateTime) - .selectAs(FlowInstance::getDelFlag, FlowInstanceVo::getDelFlag) - .selectAs(FlowInstance::getDefinitionId, FlowInstanceVo::getDefinitionId) - .selectAs(FlowInstance::getBusinessId, FlowInstanceVo::getBusinessId) - .selectAs(FlowInstance::getNodeType, FlowInstanceVo::getNodeType) - .selectAs(FlowInstance::getNodeCode, FlowInstanceVo::getNodeCode) - .selectAs(FlowInstance::getNodeName, FlowInstanceVo::getNodeName) - .selectAs(FlowInstance::getVariable, FlowInstanceVo::getVariable) - .selectAs(FlowInstance::getFlowStatus, FlowInstanceVo::getFlowStatus) - .selectAs(FlowInstance::getActivityStatus, FlowInstanceVo::getActivityStatus) - .selectAs(FlowInstance::getCreateBy, FlowInstanceVo::getCreateBy) - .selectAs(FlowInstance::getExt, FlowInstanceVo::getExt) - .selectAs(org.dromara.warm.flow.orm.entity.FlowDefinition::getFlowName, FlowInstanceVo::getFlowName) - .selectAs(org.dromara.warm.flow.orm.entity.FlowDefinition::getFlowCode, FlowInstanceVo::getFlowCode) - .selectAs(org.dromara.warm.flow.orm.entity.FlowDefinition::getVersion, FlowInstanceVo::getVersion) - .selectAs(org.dromara.warm.flow.orm.entity.FlowDefinition::getFormCustom, FlowInstanceVo::getFormCustom) - .selectAs(org.dromara.warm.flow.orm.entity.FlowDefinition::getFormPath, FlowInstanceVo::getFormPath) - .selectAs(org.dromara.warm.flow.orm.entity.FlowDefinition::getCategory, FlowInstanceVo::getCategory) - .selectAs(FlowInstanceBizExt::getBusinessCode, FlowInstanceVo::getBusinessCode) - .selectAs(FlowInstanceBizExt::getBusinessTitle, FlowInstanceVo::getBusinessTitle) + LambdaJoinQueryBuilder queryBuilder = QueryBuilder.lambdaJoin("fi", FlowInstance.class) + .select(FlowInstance::getId, FlowInstance::getCreateTime, FlowInstance::getUpdateTime, + FlowInstance::getDelFlag, FlowInstance::getDefinitionId, FlowInstance::getBusinessId, + FlowInstance::getNodeType, FlowInstance::getNodeCode, FlowInstance::getNodeName, + FlowInstance::getVariable, FlowInstance::getFlowStatus, FlowInstance::getActivityStatus, + FlowInstance::getCreateBy, FlowInstance::getExt) + .select("fd", org.dromara.warm.flow.orm.entity.FlowDefinition::getFlowName, + org.dromara.warm.flow.orm.entity.FlowDefinition::getFlowCode, + org.dromara.warm.flow.orm.entity.FlowDefinition::getVersion, + org.dromara.warm.flow.orm.entity.FlowDefinition::getFormCustom, + org.dromara.warm.flow.orm.entity.FlowDefinition::getFormPath, + org.dromara.warm.flow.orm.entity.FlowDefinition::getCategory) + .select("biz", FlowInstanceBizExt::getBusinessCode, FlowInstanceBizExt::getBusinessTitle) .leftJoin(org.dromara.warm.flow.orm.entity.FlowDefinition.class, "fd", org.dromara.warm.flow.orm.entity.FlowDefinition::getId, FlowInstance::getDefinitionId) .leftJoin(FlowInstanceBizExt.class, "biz", FlowInstanceBizExt::getInstanceId, FlowInstance::getId); - queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getNodeName()), "fi", FlowInstance::getNodeName, flowInstanceBo.getNodeName()); - queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getFlowName()), "fd", org.dromara.warm.flow.orm.entity.FlowDefinition::getFlowName, flowInstanceBo.getFlowName()); - queryWrapper.like(StringUtils.isNotBlank(flowInstanceBo.getFlowCode()), "fd", org.dromara.warm.flow.orm.entity.FlowDefinition::getFlowCode, flowInstanceBo.getFlowCode()); + queryBuilder.likeIfText("fi", FlowInstance::getNodeName, flowInstanceBo.getNodeName()); + queryBuilder.likeIfText("fd", org.dromara.warm.flow.orm.entity.FlowDefinition::getFlowName, flowInstanceBo.getFlowName()); + queryBuilder.likeIfText("fd", org.dromara.warm.flow.orm.entity.FlowDefinition::getFlowCode, flowInstanceBo.getFlowCode()); if (StringUtils.isNotBlank(flowInstanceBo.getCategory())) { List categoryIds = flwCategoryMapper.selectCategoryIdsByParentId(Convert.toLong(flowInstanceBo.getCategory())); - queryWrapper.in(CollUtil.isNotEmpty(categoryIds), "fd", org.dromara.warm.flow.orm.entity.FlowDefinition::getCategory, StreamUtils.toList(categoryIds, Convert::toStr)); + queryBuilder.inIfNotEmpty("fd", org.dromara.warm.flow.orm.entity.FlowDefinition::getCategory, StreamUtils.toList(categoryIds, Convert::toStr)); } - queryWrapper.eq(StringUtils.isNotBlank(flowInstanceBo.getBusinessId()), "fi", FlowInstance::getBusinessId, flowInstanceBo.getBusinessId()); - queryWrapper.in(CollUtil.isNotEmpty(flowInstanceBo.getCreateByIds()), "fi", FlowInstance::getCreateBy, flowInstanceBo.getCreateByIds()); - queryWrapper.eq("fi", FlowInstance::getDelFlag, "0"); - queryWrapper.orderByDesc("fi", FlowInstance::getCreateTime); - return queryWrapper; + queryBuilder.eqIfText("fi", FlowInstance::getBusinessId, flowInstanceBo.getBusinessId()); + queryBuilder.inIfNotEmpty("fi", FlowInstance::getCreateBy, flowInstanceBo.getCreateByIds()); + queryBuilder.orderByDesc("fi", FlowInstance::getCreateTime); + return queryBuilder.build(); } /** From 8b24560f67684b8928abef59116afc7be375f9a2 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 16:59:32 +0800 Subject: [PATCH 12/35] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20aws-sdk=20?= =?UTF-8?q?=E6=90=9E=E7=89=88=E6=9C=AC=E9=AA=8C=E8=AF=81=E7=AD=96=E7=95=A5?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=E9=97=AE=E9=A2=98(=E9=80=82=E9=85=8D?= =?UTF-8?q?=E4=B8=80=E4=BA=9B=E4=BD=8E=E7=89=88=E6=9C=AC=E7=9A=84=E5=8E=82?= =?UTF-8?q?=E5=95=86=E9=81=BF=E5=85=8D=E6=8A=A5=E9=94=99)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/oss/client/DefaultOssClientImpl.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/DefaultOssClientImpl.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/DefaultOssClientImpl.java index 837ec68df..3c358f49c 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/DefaultOssClientImpl.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/DefaultOssClientImpl.java @@ -5,6 +5,8 @@ import org.dromara.common.oss.config.OssClientConfig; import org.dromara.common.oss.exception.S3StorageException; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; +import software.amazon.awssdk.core.checksums.RequestChecksumCalculation; +import software.amazon.awssdk.core.checksums.ResponseChecksumValidation; import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3AsyncClient; @@ -44,6 +46,10 @@ public class DefaultOssClientImpl extends AbstractOssClientImpl { // 创建 AWS 认证信息 StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create(AwsBasicCredentials.create(accessKey, secretKey)); + S3Configuration s3Configuration = S3Configuration.builder() + .chunkedEncodingEnabled(false) + .pathStyleAccessEnabled(usePathStyleAccess) + .build(); // 创建AWS基于 Netty 的 S3 客户端 this.s3AsyncClient = S3AsyncClient.builder() @@ -51,6 +57,9 @@ public class DefaultOssClientImpl extends AbstractOssClientImpl { .endpointOverride(URI.create(endpointUrl)) .region(region) .forcePathStyle(usePathStyleAccess) + .serviceConfiguration(s3Configuration) + .requestChecksumCalculation(RequestChecksumCalculation.WHEN_REQUIRED) + .responseChecksumValidation(ResponseChecksumValidation.WHEN_REQUIRED) .httpClient( NettyNioAsyncHttpClient.builder() .connectionTimeout(Duration.ofSeconds(60)) @@ -69,13 +78,7 @@ public class DefaultOssClientImpl extends AbstractOssClientImpl { .region(region) .credentialsProvider(credentialsProvider) .endpointOverride(URI.create(domainUrl)) - .serviceConfiguration( - // 创建 S3 配置对象 - S3Configuration.builder() - .chunkedEncodingEnabled(false) - .pathStyleAccessEnabled(usePathStyleAccess) - .build() - ) + .serviceConfiguration(s3Configuration) .build(); // 创建异步调度器对象 From a6bb80dc4cdeef3323d17a5ee953e63f958e90f8 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 17:07:05 +0800 Subject: [PATCH 13/35] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20oss?= =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E9=BB=98=E8=AE=A4=E5=BC=80=E5=90=AF?= =?UTF-8?q?=E8=99=9A=E6=8B=9F=E7=BA=BF=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../org/dromara/common/oss/config/OssAsyncExecutorConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/config/OssAsyncExecutorConfig.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/config/OssAsyncExecutorConfig.java index 6fc7eeb55..df3e83c56 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/config/OssAsyncExecutorConfig.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/config/OssAsyncExecutorConfig.java @@ -32,7 +32,7 @@ public record OssAsyncExecutorConfig( * 默认异步执行器配置 */ public static final OssAsyncExecutorConfig DEFAULT = OssAsyncExecutorConfig.builder() - .enabledVirtualThread(false) + .enabledVirtualThread(true) .corePoolSize(DEFAULT_CORE_POOL_SIZE) .build(); From efedb832b2014cda778db20f2f516f3b60c7964e 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 18:07:49 +0800 Subject: [PATCH 14/35] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20springdoc?= =?UTF-8?q?=20=E8=87=AA=E5=AE=9A=E4=B9=89=E9=80=BB=E8=BE=91=E8=A1=8C?= =?UTF-8?q?=E4=B8=BA=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/doc/config/SpringDocConfig.java | 29 +- .../ClassTagOperationCustomizer.java | 148 +++++++++ .../JavadocOperationCustomizer.java | 58 ++++ .../common/doc/handler/OpenApiHandler.java | 302 ------------------ 4 files changed, 221 insertions(+), 316 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/customizer/ClassTagOperationCustomizer.java create mode 100644 ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/customizer/JavadocOperationCustomizer.java delete mode 100644 ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java index 74f9b73b0..cc55c7814 100644 --- a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java @@ -6,18 +6,14 @@ import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.security.SecurityRequirement; import lombok.RequiredArgsConstructor; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.doc.core.customizer.ClassTagOperationCustomizer; +import org.dromara.common.doc.core.customizer.JavadocOperationCustomizer; import org.dromara.common.doc.config.properties.SpringDocProperties; import org.dromara.common.doc.core.resolver.JavadocResolver; import org.dromara.common.doc.core.resolver.SaTokenAnnotationMetadataJavadocResolver; -import org.dromara.common.doc.handler.OpenApiHandler; import org.springdoc.core.configuration.SpringDocConfiguration; -import org.springdoc.core.customizers.OpenApiBuilderCustomizer; import org.springdoc.core.customizers.OpenApiCustomizer; -import org.springdoc.core.customizers.ServerBaseUrlCustomizer; -import org.springdoc.core.properties.SpringDocConfigProperties; import org.springdoc.core.providers.JavadocProvider; -import org.springdoc.core.service.OpenAPIService; -import org.springdoc.core.service.SecurityService; import org.springdoc.core.utils.PropertyResolverUtils; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -91,16 +87,21 @@ public class SpringDocConfig { } /** - * 自定义 openapi 处理器 + * Controller 类级标签增强 */ @Bean - public OpenAPIService openApiBuilder(Optional openAPI, - SecurityService securityParser, - SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, - Optional> openApiBuilderCustomisers, - Optional> serverBaseUrlCustomisers, Optional javadocProvider, - List javadocResolvers) { - return new OpenApiHandler(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomisers, serverBaseUrlCustomisers, javadocProvider, javadocResolvers); + public ClassTagOperationCustomizer classTagOperationCustomizer(Optional javadocProvider, + PropertyResolverUtils propertyResolverUtils) { + return new ClassTagOperationCustomizer(javadocProvider, propertyResolverUtils); + } + + /** + * 方法 JavaDoc 与权限描述增强 + */ + @Bean + public JavadocOperationCustomizer javadocOperationCustomizer(Optional javadocProvider, + List javadocResolvers) { + return new JavadocOperationCustomizer(javadocProvider, javadocResolvers); } /** diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/customizer/ClassTagOperationCustomizer.java b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/customizer/ClassTagOperationCustomizer.java new file mode 100644 index 000000000..b3757c374 --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/customizer/ClassTagOperationCustomizer.java @@ -0,0 +1,148 @@ +package org.dromara.common.doc.core.customizer; + +import cn.hutool.core.io.IoUtil; +import io.swagger.v3.core.util.AnnotationsUtils; +import io.swagger.v3.oas.annotations.tags.Tags; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springdoc.core.customizers.OpenApiCustomizer; +import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.providers.JavadocProvider; +import org.springdoc.core.service.OpenAPIService; +import org.springdoc.core.utils.PropertyResolverUtils; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.CollectionUtils; +import org.springframework.web.method.HandlerMethod; + +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Controller 类级标签增强。 + * + * @author Lion Li + */ +@RequiredArgsConstructor +public class ClassTagOperationCustomizer implements OperationCustomizer, OpenApiCustomizer { + + private final Optional javadocProvider; + + private final PropertyResolverUtils propertyResolverUtils; + + private final Map tags = new ConcurrentHashMap<>(); + + private final Set replacedAutoTagNames = ConcurrentHashMap.newKeySet(); + + @Override + public Operation customize(Operation operation, HandlerMethod handlerMethod) { + Class beanType = handlerMethod.getBeanType(); + List classTags = getClassTags(beanType); + if (!CollectionUtils.isEmpty(classTags)) { + // 优先使用 Controller 类上的 @Tag / @Tags,保持 Swagger 原生注解语义 + addAnnotationTags(operation, classTags); + return operation; + } + + String tagName = getClassJavadocTagName(beanType); + if (StringUtils.isBlank(tagName)) { + return operation; + } + + String autoTagName = OpenAPIService.splitCamelCase(beanType.getSimpleName()); + if (!shouldUseClassJavadocTag(operation, autoTagName)) { + return operation; + } + + // 无显式 @Tag 时,将 springdoc 自动生成的类名 tag 替换为类 JavaDoc 第一行 + removeOperationTag(operation, autoTagName); + addOperationTag(operation, tagName); + replacedAutoTagNames.add(autoTagName); + tags.putIfAbsent(tagName, new Tag().name(tagName).description(javadocProvider.get().getClassJavadoc(beanType))); + return operation; + } + + @Override + public void customise(OpenAPI openApi) { + if (!CollectionUtils.isEmpty(openApi.getTags()) && !CollectionUtils.isEmpty(replacedAutoTagNames)) { + // 移除已被 JavaDoc tag 替换的默认类名 tag,避免 Swagger UI 出现空分组 + openApi.getTags().removeIf(tag -> replacedAutoTagNames.contains(tag.getName())); + } + // 将类级 @Tag 描述或 JavaDoc 描述补充到 OpenAPI 顶层 tags + tags.values().forEach(tag -> { + if (openApi.getTags() == null || openApi.getTags().stream().noneMatch(item -> Objects.equals(item.getName(), tag.getName()))) { + openApi.addTagsItem(tag); + } + }); + } + + private List getClassTags(Class beanType) { + Set tagsSet = AnnotatedElementUtils.findAllMergedAnnotations(beanType, Tags.class); + Set mergedTags = tagsSet.stream() + .flatMap(item -> Stream.of(item.value())) + .collect(Collectors.toSet()); + mergedTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(beanType, io.swagger.v3.oas.annotations.tags.Tag.class)); + return new ArrayList<>(mergedTags); + } + + private void addAnnotationTags(Operation operation, List classTags) { + classTags.stream() + .map(io.swagger.v3.oas.annotations.tags.Tag::name) + .map(name -> propertyResolverUtils.resolve(name, Locale.getDefault())) + .filter(StringUtils::isNotBlank) + .forEach(name -> addOperationTag(operation, name)); + + AnnotationsUtils.getTags(classTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true) + .ifPresent(items -> items.forEach(tag -> { + tag.name(propertyResolverUtils.resolve(tag.getName(), Locale.getDefault())); + tag.description(propertyResolverUtils.resolve(tag.getDescription(), Locale.getDefault())); + if (StringUtils.isNotBlank(tag.getName())) { + tags.putIfAbsent(tag.getName(), tag); + } + })); + } + + private String getClassJavadocTagName(Class beanType) { + if (javadocProvider.isEmpty()) { + return null; + } + String description = javadocProvider.get().getClassJavadoc(beanType); + if (StringUtils.isBlank(description)) { + return null; + } + // 与原 OpenApiHandler 保持一致:类 JavaDoc 第一行作为 tag 名,完整 JavaDoc 作为 tag 描述 + List lines = IoUtil.readLines(new StringReader(description), new ArrayList<>()); + return lines.stream().filter(StringUtils::isNotBlank).findFirst().orElse(null); + } + + private boolean shouldUseClassJavadocTag(Operation operation, String autoTagName) { + return CollectionUtils.isEmpty(operation.getTags()) || operation.getTags().contains(autoTagName); + } + + private void addOperationTag(Operation operation, String tagName) { + if (operation.getTags() == null) { + operation.setTags(new ArrayList<>()); + } + if (!operation.getTags().contains(tagName)) { + operation.addTagsItem(tagName); + } + } + + private void removeOperationTag(Operation operation, String tagName) { + if (!CollectionUtils.isEmpty(operation.getTags())) { + operation.getTags().removeIf(item -> Objects.equals(item, tagName)); + } + } + +} diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/customizer/JavadocOperationCustomizer.java b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/customizer/JavadocOperationCustomizer.java new file mode 100644 index 000000000..a8f251bb1 --- /dev/null +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/core/customizer/JavadocOperationCustomizer.java @@ -0,0 +1,58 @@ +package org.dromara.common.doc.core.customizer; + +import io.swagger.v3.oas.models.Operation; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.dromara.common.doc.core.resolver.JavadocResolver; +import org.springdoc.core.customizers.OperationCustomizer; +import org.springdoc.core.providers.JavadocProvider; +import org.springframework.util.CollectionUtils; +import org.springframework.web.method.HandlerMethod; + +import java.util.List; +import java.util.Optional; + +/** + * 方法 JavaDoc 与扩展描述增强。 + * + * @author Lion Li + */ +@RequiredArgsConstructor +public class JavadocOperationCustomizer implements OperationCustomizer { + + private final Optional javadocProvider; + + private final List javadocResolvers; + + @Override + public Operation customize(Operation operation, HandlerMethod handlerMethod) { + javadocProvider.ifPresent(provider -> { + String description = provider.getMethodJavadocDescription(handlerMethod.getMethod()); + if (StringUtils.isNotBlank(description)) { + // 使用方法 JavaDoc 首句作为接口摘要,完整 JavaDoc 作为接口描述 + operation.setSummary(provider.getFirstSentence(description)); + operation.setDescription(description); + } + }); + + if (CollectionUtils.isEmpty(javadocResolvers)) { + return operation; + } + + StringBuilder description = new StringBuilder(Optional.ofNullable(operation.getDescription()).orElse("")); + List resolvedDescriptions = javadocResolvers.stream() + .sorted() + // 只执行支持当前 HandlerMethod 的扩展解析器,避免无注解接口被追加权限说明 + .filter(resolver -> resolver.supports(handlerMethod)) + .map(resolver -> resolver.resolve(handlerMethod, operation)) + .filter(StringUtils::isNotBlank) + .toList(); + if (!resolvedDescriptions.isEmpty()) { + // 在原方法 JavaDoc 后追加权限等扩展描述,保持现有 resolver 扩展点 + resolvedDescriptions.forEach(description::append); + operation.setDescription(description.toString()); + } + return operation; + } + +} diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java deleted file mode 100644 index f26ce5b24..000000000 --- a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/handler/OpenApiHandler.java +++ /dev/null @@ -1,302 +0,0 @@ -package org.dromara.common.doc.handler; - -import cn.hutool.core.io.IoUtil; -import io.swagger.v3.core.jackson.TypeNameResolver; -import io.swagger.v3.core.util.AnnotationsUtils; -import io.swagger.v3.oas.annotations.tags.Tags; -import io.swagger.v3.oas.models.Components; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.Operation; -import io.swagger.v3.oas.models.Paths; -import io.swagger.v3.oas.models.tags.Tag; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.dromara.common.core.utils.StreamUtils; -import org.dromara.common.doc.core.resolver.JavadocResolver; -import org.springdoc.core.customizers.OpenApiBuilderCustomizer; -import org.springdoc.core.customizers.ServerBaseUrlCustomizer; -import org.springdoc.core.properties.SpringDocConfigProperties; -import org.springdoc.core.providers.JavadocProvider; -import org.springdoc.core.service.OpenAPIService; -import org.springdoc.core.service.SecurityService; -import org.springdoc.core.utils.PropertyResolverUtils; -import org.springframework.context.ApplicationContext; -import org.springframework.core.annotation.AnnotatedElementUtils; -import org.springframework.util.CollectionUtils; -import org.springframework.web.method.HandlerMethod; - -import java.io.StringReader; -import java.lang.reflect.Method; -import java.util.*; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * 自定义 openapi 处理器 - * 对源码功能进行修改 增强使用 - */ -@Slf4j -@SuppressWarnings("all") -public class OpenApiHandler extends OpenAPIService { - - /** - * The Basic error controller. - */ - private static Class basicErrorController; - - /** - * The Security parser. - */ - private final SecurityService securityParser; - - /** - * The Mappings map. - */ - private final Map mappingsMap = new HashMap<>(); - - /** - * The Springdoc tags. - */ - private final Map springdocTags = new HashMap<>(); - - /** - * The Open api builder customisers. - */ - private final Optional> openApiBuilderCustomisers; - - /** - * The server base URL customisers. - */ - private final Optional> serverBaseUrlCustomizers; - - /** - * The Spring doc config properties. - */ - private final SpringDocConfigProperties springDocConfigProperties; - - /** - * The Cached open api map. - */ - private final Map cachedOpenAPI = new HashMap<>(); - - /** - * The Property resolver utils. - */ - private final PropertyResolverUtils propertyResolverUtils; - - /** - * Javadoc解析器接口 - */ - private final List javadocResolvers; - - /** - * The javadoc provider. - */ - private final Optional javadocProvider; - - /** - * The Context. - */ - private ApplicationContext context; - - /** - * The Open api. - */ - private OpenAPI openAPI; - - /** - * The Is servers present. - */ - private boolean isServersPresent; - - /** - * The Server base url. - */ - private String serverBaseUrl; - - /** - * Instantiates a new Open api builder. - * - * @param openAPI the open api - * @param securityParser the security parser - * @param springDocConfigProperties the spring doc config properties - * @param propertyResolverUtils the property resolver utils - * @param openApiBuilderCustomizers the open api builder customisers - * @param serverBaseUrlCustomizers the server base url customizers - * @param javadocProvider the javadoc provider - * @param javadocResolvers Javadoc 解析器列表 - */ - public OpenApiHandler(Optional openAPI, SecurityService securityParser, - SpringDocConfigProperties springDocConfigProperties, PropertyResolverUtils propertyResolverUtils, - Optional> openApiBuilderCustomizers, - Optional> serverBaseUrlCustomizers, - Optional javadocProvider, - List javadocResolvers) { - super(openAPI, securityParser, springDocConfigProperties, propertyResolverUtils, openApiBuilderCustomizers, serverBaseUrlCustomizers, javadocProvider); - if (openAPI.isPresent()) { - this.openAPI = openAPI.get(); - if (this.openAPI.getComponents() == null) - this.openAPI.setComponents(new Components()); - if (this.openAPI.getPaths() == null) - this.openAPI.setPaths(new Paths()); - if (!CollectionUtils.isEmpty(this.openAPI.getServers())) - this.isServersPresent = true; - } - this.propertyResolverUtils = propertyResolverUtils; - this.securityParser = securityParser; - this.springDocConfigProperties = springDocConfigProperties; - this.openApiBuilderCustomisers = openApiBuilderCustomizers; - this.serverBaseUrlCustomizers = serverBaseUrlCustomizers; - this.javadocProvider = javadocProvider; - this.javadocResolvers = javadocResolvers == null ? new ArrayList<>() : javadocResolvers; - if (springDocConfigProperties.isUseFqn()) - TypeNameResolver.std.setUseFqn(true); - } - - /** - * 构建接口标签、权限描述与方法摘要。 - * - * @param handlerMethod Handler 方法 - * @param operation OpenAPI 操作对象 - * @param openAPI OpenAPI 文档对象 - * @param locale 当前语言环境 - * @return 处理后的操作对象 - */ - @Override - public Operation buildTags(HandlerMethod handlerMethod, Operation operation, OpenAPI openAPI, Locale locale) { - - Set tags = new HashSet<>(); - Set tagsStr = new HashSet<>(); - - buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale); - buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale); - - if (!CollectionUtils.isEmpty(tagsStr)) - tagsStr = tagsStr.stream() - .map(str -> propertyResolverUtils.resolve(str, locale)) - .collect(Collectors.toSet()); - - if (springdocTags.containsKey(handlerMethod)) { - io.swagger.v3.oas.models.tags.Tag tag = springdocTags.get(handlerMethod); - tagsStr.add(tag.getName()); - if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { - openAPI.addTagsItem(tag); - } - } - - if (!CollectionUtils.isEmpty(tagsStr)) { - if (CollectionUtils.isEmpty(operation.getTags())) - operation.setTags(new ArrayList<>(tagsStr)); - else { - Set operationTagsSet = new HashSet<>(operation.getTags()); - operationTagsSet.addAll(tagsStr); - operation.getTags().clear(); - operation.getTags().addAll(operationTagsSet); - } - } - - if (isAutoTagClasses(operation)) { - - - if (javadocProvider.isPresent()) { - String description = javadocProvider.get().getClassJavadoc(handlerMethod.getBeanType()); - if (StringUtils.isNotBlank(description)) { - io.swagger.v3.oas.models.tags.Tag tag = new io.swagger.v3.oas.models.tags.Tag(); - - // 自定义部分 修改使用java注释当tag名 - List list = IoUtil.readLines(new StringReader(description), new ArrayList<>()); - // tag.setName(tagAutoName); - tag.setName(list.get(0)); - operation.addTagsItem(list.get(0)); - - tag.setDescription(description); - if (openAPI.getTags() == null || !openAPI.getTags().contains(tag)) { - openAPI.addTagsItem(tag); - } - } - } else { - String tagAutoName = splitCamelCase(handlerMethod.getBeanType().getSimpleName()); - operation.addTagsItem(tagAutoName); - } - } - - if (!CollectionUtils.isEmpty(tags)) { - // Existing tags - List openApiTags = openAPI.getTags(); - if (!CollectionUtils.isEmpty(openApiTags)) - tags.addAll(openApiTags); - openAPI.setTags(new ArrayList<>(tags)); - } - - // Handle SecurityRequirement at operation level - io.swagger.v3.oas.annotations.security.SecurityRequirement[] securityRequirements = securityParser - .getSecurityRequirements(handlerMethod); - if (securityRequirements != null) { - if (securityRequirements.length == 0) - operation.setSecurity(Collections.emptyList()); - else - securityParser.buildSecurityRequirement(securityRequirements, operation); - } - - if (javadocProvider.isPresent()) { - String description = javadocProvider.get().getMethodJavadocDescription(handlerMethod.getMethod()); - String summary = javadocProvider.get().getFirstSentence(description); - if (StringUtils.isNotBlank(description)){ - operation.setSummary(summary); - } - // 调用解析器提取JavaDoc中的权限信息 - if (javadocResolvers != null && !javadocResolvers.isEmpty()) { - for (JavadocResolver resolver : javadocResolvers) { - String desc = resolver.resolve(handlerMethod, operation); - description = description + desc; - } - operation.setDescription(description); - } - } - - return operation; - } - - /** - * 从方法注解中提取标签信息。 - * - * @param method 方法对象 - * @param tags 标签集合 - * @param tagsStr 标签名称集合 - * @param locale 当前语言环境 - */ - private void buildTagsFromMethod(Method method, Set tags, Set tagsStr, Locale locale) { - // method tags - Set tagsSet = AnnotatedElementUtils - .findAllMergedAnnotations(method, Tags.class); - Set methodTags = tagsSet.stream() - .flatMap(x -> Stream.of(x.value())).collect(Collectors.toSet()); - methodTags.addAll(AnnotatedElementUtils.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class)); - if (!CollectionUtils.isEmpty(methodTags)) { - tagsStr.addAll(StreamUtils.toSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale))); - List allTags = new ArrayList<>(methodTags); - addTags(allTags, tags, locale); - } - } - - /** - * 将注解标签转换并合并到 OpenAPI 标签集合。 - * - * @param sourceTags 注解标签列表 - * @param tags OpenAPI 标签集合 - * @param locale 当前语言环境 - */ - private void addTags(List sourceTags, Set tags, Locale locale) { - Optional> optionalTagSet = AnnotationsUtils - .getTags(sourceTags.toArray(new io.swagger.v3.oas.annotations.tags.Tag[0]), true); - optionalTagSet.ifPresent(tagsSet -> { - tagsSet.forEach(tag -> { - tag.name(propertyResolverUtils.resolve(tag.getName(), locale)); - tag.description(propertyResolverUtils.resolve(tag.getDescription(), locale)); - if (tags.stream().noneMatch(t -> t.getName().equals(tag.getName()))) - tags.add(tag); - }); - }); - } - -} From df2015f6d05df4b8f7049205999a0b42efb4e827 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: Sat, 16 May 2026 11:32:38 +0800 Subject: [PATCH 15/35] =?UTF-8?q?refactor(common-core):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BB=A3=E7=A0=81=E4=B8=AD=E7=9A=84=E5=B0=8F=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dromara/web/service/SysLoginService.java | 2 +- .../common/core/config/ValidatorConfig.java | 23 +++++++--------- .../common/core/domain/PageResult.java | 14 +++++++--- .../org/dromara/common/core/domain/R.java | 2 +- .../core/exception/ServiceException.java | 11 ++++++++ .../dromara/common/core/utils/DateUtils.java | 2 +- .../common/core/utils/ServletUtils.java | 6 +++-- .../common/core/utils/ThreadUtils.java | 11 +++++--- .../core/validate/dicts/DictPattern.java | 2 +- .../validate/dicts/DictPatternValidator.java | 5 +++- .../validate/enums/EnumPatternValidator.java | 27 ++++++++++++------- .../dromara/common/core/xss/XssValidator.java | 5 +++- 12 files changed, 72 insertions(+), 38 deletions(-) diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java index 5d03cfbe5..79fd7e51a 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java @@ -153,7 +153,7 @@ public class SysLoginService { loginUser.setDeptName(deptOpt.map(SysDeptVo::getDeptName).orElse(StringUtils.EMPTY)); loginUser.setDeptCategory(deptOpt.map(SysDeptVo::getDeptCategory).orElse(StringUtils.EMPTY)); } - ThreadUtils.virtualSubmit(() -> { + ThreadUtils.virtualInvokeAll(() -> { loginUser.setMenuPermission(permissionService.getMenuPermission(userId)); }, () -> { loginUser.setRolePermission(permissionService.getRolePermission(userId)); diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ValidatorConfig.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ValidatorConfig.java index a56cb5bd4..79f1df232 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ValidatorConfig.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/config/ValidatorConfig.java @@ -26,19 +26,16 @@ public class ValidatorConfig { */ @Bean public Validator validator(MessageSource messageSource) { - try (LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean()) { - // 国际化 - factoryBean.setValidationMessageSource(messageSource); - // 设置使用 HibernateValidator 校验器 - factoryBean.setProviderClass(HibernateValidator.class); - Properties properties = new Properties(); - // 设置快速失败模式(fail-fast),即校验过程中一旦遇到失败,立即停止并返回错误 - properties.setProperty("hibernate.validator.fail_fast", "true"); - factoryBean.setValidationProperties(properties); - // 加载配置 - factoryBean.afterPropertiesSet(); - return factoryBean.getValidator(); - } + LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean(); + // 国际化 + factoryBean.setValidationMessageSource(messageSource); + // 设置使用 HibernateValidator 校验器 + factoryBean.setProviderClass(HibernateValidator.class); + Properties properties = new Properties(); + // 设置快速失败模式(fail-fast),即校验过程中一旦遇到失败,立即停止并返回错误 + properties.setProperty("hibernate.validator.fail_fast", "true"); + factoryBean.setValidationProperties(properties); + return factoryBean; } } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/PageResult.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/PageResult.java index a89a755ea..c85f7355f 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/PageResult.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/PageResult.java @@ -6,6 +6,7 @@ import lombok.NoArgsConstructor; import java.io.Serial; import java.io.Serializable; import java.util.Collection; +import java.util.Collections; /** * 表格分页数据对象 @@ -36,7 +37,7 @@ public class PageResult implements Serializable { * @param total 总记录数 */ public PageResult(Collection list, long total) { - this.rows = list; + this.rows = emptyIfNull(list); this.total = total; } @@ -45,7 +46,7 @@ public class PageResult implements Serializable { */ public static PageResult build(Collection list, long total) { PageResult rspData = new PageResult<>(); - rspData.setRows(list); + rspData.setRows(emptyIfNull(list)); rspData.setTotal(total); return rspData; } @@ -55,8 +56,9 @@ public class PageResult implements Serializable { */ public static PageResult build(Collection list) { PageResult rspData = new PageResult<>(); - rspData.setRows(list); - rspData.setTotal(list.size()); + Collection rows = emptyIfNull(list); + rspData.setRows(rows); + rspData.setTotal(rows.size()); return rspData; } @@ -67,4 +69,8 @@ public class PageResult implements Serializable { return new PageResult<>(); } + private static Collection emptyIfNull(Collection list) { + return list == null ? Collections.emptyList() : list; + } + } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/R.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/R.java index 972263199..53c5f7737 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/R.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/domain/R.java @@ -197,7 +197,7 @@ public class R implements Serializable { * @return true=成功,false=失败 */ public static Boolean isSuccess(R ret) { - return SUCCESS == ret.getCode(); + return ret != null && SUCCESS == ret.getCode(); } } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java index 75e33cc54..a8e46aa80 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/exception/ServiceException.java @@ -57,6 +57,17 @@ public final class ServiceException extends RuntimeException { this.code = code; } + /** + * 使用错误消息和根因构造业务异常。 + * + * @param message 错误消息 + * @param cause 根因 + */ + public ServiceException(String message, Throwable cause) { + super(cause); + this.message = message; + } + /** * 使用占位符参数格式化错误消息。 * diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java index 5eb55238b..2dafe34c2 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/DateUtils.java @@ -156,7 +156,7 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils { try { return new SimpleDateFormat(format.getTimeFormat()).parse(ts); } catch (ParseException e) { - throw new RuntimeException(e); + throw new ServiceException("日期时间解析失败:" + ts, e); } } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ServletUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ServletUtils.java index 329892969..5654e8bed 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ServletUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ServletUtils.java @@ -10,6 +10,7 @@ import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpSession; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.util.LinkedCaseInsensitiveMap; import org.springframework.web.context.request.RequestAttributes; @@ -31,6 +32,7 @@ import java.util.Map; * @author ruoyi */ @NoArgsConstructor(access = AccessLevel.PRIVATE) +@Slf4j public class ServletUtils extends JakartaServletUtil { /** @@ -223,7 +225,7 @@ public class ServletUtils extends JakartaServletUtil { response.setCharacterEncoding(StandardCharsets.UTF_8.toString()); response.getWriter().print(string); } catch (IOException e) { - e.printStackTrace(); + log.error("渲染响应内容异常", e); } } @@ -249,7 +251,7 @@ public class ServletUtils extends JakartaServletUtil { // 判断 URI 后缀是否为 .json 或 .xml String uri = request.getRequestURI(); - if (StringUtils.equalsAnyIgnoreCase(uri, ".json", ".xml")) { + if (StringUtils.endsWithAny(StringUtils.lowerCase(uri), ".json", ".xml")) { return true; } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ThreadUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ThreadUtils.java index fd382918b..a03e96819 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ThreadUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ThreadUtils.java @@ -2,6 +2,7 @@ package org.dromara.common.core.utils; import lombok.AccessLevel; import lombok.NoArgsConstructor; +import org.dromara.common.core.exception.ServiceException; import java.util.ArrayList; import java.util.List; @@ -18,7 +19,7 @@ public class ThreadUtils { /** * 批量执行任务 */ - public static void virtualSubmit(Runnable ...runnableList) { + public static void virtualInvokeAll(Runnable... runnableList) { List> callableList = new ArrayList<>(); try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { for (Runnable runnable : runnableList) { @@ -27,8 +28,12 @@ public class ThreadUtils { for (Future future : callableList) { future.get(); } - } catch (InterruptedException | ExecutionException e) { - throw new RuntimeException(e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("线程执行被中断", e); + } catch (ExecutionException e) { + Throwable cause = e.getCause() == null ? e : e.getCause(); + throw new RuntimeException("线程执行异常:" + cause.getMessage(), cause); } } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java index 5bc41a354..0176106e4 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPattern.java @@ -26,7 +26,7 @@ public @interface DictPattern { /** * 分隔符 */ - String separator(); + String separator() default ","; /** * 默认校验失败提示信息 diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java index 558a3438a..e454ed8d4 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/validate/dicts/DictPatternValidator.java @@ -45,7 +45,10 @@ public class DictPatternValidator implements ConstraintValidator { - private EnumPattern annotation; + private final Set values = new HashSet<>(); @Override public void initialize(EnumPattern annotation) { ConstraintValidator.super.initialize(annotation); - this.annotation = annotation; + String fieldName = annotation.fieldName(); + if (StringUtils.isBlank(fieldName)) { + return; + } + for (Object e : annotation.type().getEnumConstants()) { + Object fieldValue = ReflectUtils.invokeGetter(e, fieldName); + if (fieldValue != null) { + values.add(String.valueOf(fieldValue)); + } + } } @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { - if (StringUtils.isNotBlank(value)) { - String fieldName = annotation.fieldName(); - for (Object e : annotation.type().getEnumConstants()) { - if (value.equals(ReflectUtils.invokeGetter(e, fieldName))) { - return true; - } - } + if (StringUtils.isBlank(value)) { + return true; } - return false; + return values.contains(value); } } diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/xss/XssValidator.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/xss/XssValidator.java index 9c3256300..374ea0b2d 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/xss/XssValidator.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/xss/XssValidator.java @@ -2,9 +2,9 @@ package org.dromara.common.core.xss; import cn.hutool.core.util.ReUtil; import cn.hutool.http.HtmlUtil; - import jakarta.validation.ConstraintValidator; import jakarta.validation.ConstraintValidatorContext; +import org.dromara.common.core.utils.StringUtils; /** * 自定义xss校验注解实现 @@ -15,6 +15,9 @@ public class XssValidator implements ConstraintValidator { @Override public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) { + if (StringUtils.isBlank(value)) { + return true; + } return !ReUtil.contains(HtmlUtil.RE_HTML_MARK, value); } From 475618e960d2c907e00f10bab230c131778847a4 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: Sat, 16 May 2026 12:02:18 +0800 Subject: [PATCH 16/35] =?UTF-8?q?refactor(common-encrypt):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E5=8A=A0=E8=A7=A3=E5=AF=86=E5=A4=84=E7=90=86=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 抽取 EncryptContextFactory 与 EncryptedFieldProcessor,统一加密上下文创建和字段处理逻辑 - 修复 MyBatis 入参加密后未恢复原对象的问题,避免调用方对象被密文化 - 移除结果解密拦截器对 MyBatis 内部 parameterHandler 的反射依赖 - 优化 EncryptorManager 缓存 key 与懒加载字段扫描逻辑 - CryptoFilter 改为构造注入 Spring MVC 依赖,避免请求期间动态获取 Bean - 修复请求/响应包装器字符集、流状态和响应头处理 - 响应加密不再 reset 原始响应,避免丢失状态码和已有响应头 - 为加密配置补充默认值和启动校验 --- .../config/ApiDecryptAutoConfiguration.java | 8 +- .../config/EncryptorAutoConfiguration.java | 40 ++++++- .../encrypt/core/EncryptContextFactory.java | 40 +++++++ .../encrypt/core/EncryptedFieldProcessor.java | 109 ++++++++++++++++++ .../common/encrypt/core/EncryptorManager.java | 53 ++++++--- .../common/encrypt/filter/CryptoFilter.java | 16 +-- .../filter/DecryptRequestBodyWrapper.java | 10 +- .../filter/EncryptResponseBodyWrapper.java | 48 ++++++-- .../MybatisDecryptInterceptor.java | 96 +-------------- .../MybatisEncryptInterceptor.java | 94 +++------------ .../properties/ApiDecryptProperties.java | 2 +- .../properties/EncryptorProperties.java | 4 +- 12 files changed, 301 insertions(+), 219 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContextFactory.java create mode 100644 ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptedFieldProcessor.java diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java index 38b22f388..559002335 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java @@ -9,6 +9,8 @@ import org.springframework.boot.context.properties.EnableConfigurationProperties import org.springframework.boot.web.servlet.FilterRegistration; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; +import org.springframework.web.servlet.HandlerExceptionResolver; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; /** * api 解密自动配置 @@ -27,8 +29,10 @@ public class ApiDecryptAutoConfiguration { order = FilterRegistrationBean.HIGHEST_PRECEDENCE, dispatcherTypes = DispatcherType.REQUEST ) - public CryptoFilter cryptoFilter(ApiDecryptProperties properties) { - return new CryptoFilter(properties); + public CryptoFilter cryptoFilter(ApiDecryptProperties properties, + RequestMappingHandlerMapping requestMappingHandlerMapping, + HandlerExceptionResolver handlerExceptionResolver) { + return new CryptoFilter(properties, requestMappingHandlerMapping, handlerExceptionResolver); } } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java index fbc4e52de..dfe1915d1 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java @@ -3,7 +3,11 @@ package org.dromara.common.encrypt.config; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.encrypt.core.EncryptContextFactory; +import org.dromara.common.encrypt.core.EncryptedFieldProcessor; import org.dromara.common.encrypt.core.EncryptorManager; +import org.dromara.common.encrypt.enums.AlgorithmType; import org.dromara.common.encrypt.interceptor.MybatisDecryptInterceptor; import org.dromara.common.encrypt.interceptor.MybatisEncryptInterceptor; import org.dromara.common.encrypt.properties.EncryptorProperties; @@ -16,8 +20,7 @@ import org.springframework.context.annotation.Bean; /** * 加解密配置 * - * @author 老马 - * @version 4.6.0 + * @author Lion Li */ @AutoConfiguration(after = MybatisPlusAutoConfiguration.class) @EnableConfigurationProperties(EncryptorProperties.class) @@ -30,17 +33,42 @@ public class EncryptorAutoConfiguration { @Bean public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) { + validateEncryptorProperties(properties); return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage()); } @Bean - public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptorManager encryptorManager) { - return new MybatisEncryptInterceptor(encryptorManager, properties); + public EncryptContextFactory encryptContextFactory() { + return new EncryptContextFactory(properties); } @Bean - public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptorManager encryptorManager) { - return new MybatisDecryptInterceptor(encryptorManager, properties); + public EncryptedFieldProcessor encryptedFieldProcessor(EncryptorManager encryptorManager, EncryptContextFactory encryptContextFactory) { + return new EncryptedFieldProcessor(encryptorManager, encryptContextFactory); + } + + @Bean + public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptedFieldProcessor encryptedFieldProcessor) { + return new MybatisEncryptInterceptor(encryptedFieldProcessor); + } + + @Bean + public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptedFieldProcessor encryptedFieldProcessor) { + return new MybatisDecryptInterceptor(encryptedFieldProcessor); + } + + private void validateEncryptorProperties(EncryptorProperties properties) { + AlgorithmType algorithm = properties.getAlgorithm(); + if (algorithm == AlgorithmType.AES || algorithm == AlgorithmType.SM4) { + if (StringUtils.isBlank(properties.getPassword())) { + throw new IllegalArgumentException("mybatis-encryptor.password 不能为空"); + } + } + if (algorithm == AlgorithmType.RSA || algorithm == AlgorithmType.SM2) { + if (StringUtils.isAnyBlank(properties.getPublicKey(), properties.getPrivateKey())) { + throw new IllegalArgumentException("mybatis-encryptor.publicKey 与 privateKey 不能为空"); + } + } } } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContextFactory.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContextFactory.java new file mode 100644 index 000000000..12c864052 --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContextFactory.java @@ -0,0 +1,40 @@ +package org.dromara.common.encrypt.core; + +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.encrypt.annotation.EncryptField; +import org.dromara.common.encrypt.enums.AlgorithmType; +import org.dromara.common.encrypt.enums.EncodeType; +import org.dromara.common.encrypt.properties.EncryptorProperties; + +import java.lang.reflect.Field; + +/** + * 加密上下文工厂。 + * + * @author Lion Li + */ +public class EncryptContextFactory { + + private final EncryptorProperties defaultProperties; + + public EncryptContextFactory(EncryptorProperties defaultProperties) { + this.defaultProperties = defaultProperties; + } + + /** + * 根据字段注解和默认配置创建加密上下文。 + * + * @param field 加密字段 + * @return 加密上下文 + */ + public EncryptContext create(Field field) { + EncryptField encryptField = field.getAnnotation(EncryptField.class); + EncryptContext encryptContext = new EncryptContext(); + encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); + encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); + encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); + encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); + encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); + return encryptContext; + } +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptedFieldProcessor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptedFieldProcessor.java new file mode 100644 index 000000000..f8d4f340c --- /dev/null +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptedFieldProcessor.java @@ -0,0 +1,109 @@ +package org.dromara.common.encrypt.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; +import java.util.*; + +/** + * 加密字段处理器。 + * + * @author Lion Li + */ +@Slf4j +public class EncryptedFieldProcessor { + + private final EncryptorManager encryptorManager; + private final EncryptContextFactory contextFactory; + + public EncryptedFieldProcessor(EncryptorManager encryptorManager, EncryptContextFactory contextFactory) { + this.encryptorManager = encryptorManager; + this.contextFactory = contextFactory; + } + + /** + * 加密对象字段,并返回原始字段快照。 + * + * @param sourceObject 待加密对象 + * @return 原始字段快照 + */ + public List encrypt(Object sourceObject) { + List snapshots = new ArrayList<>(); + handle(sourceObject, Collections.newSetFromMap(new IdentityHashMap<>()), (target, field, value) -> { + String encrypt = encryptorManager.encrypt(value, contextFactory.create(field)); + if (!Objects.equals(value, encrypt)) { + snapshots.add(new FieldSnapshot(target, field, value)); + field.set(target, encrypt); + } + }); + return snapshots; + } + + /** + * 解密对象字段。 + * + * @param sourceObject 待解密对象 + */ + public void decrypt(Object sourceObject) { + handle(sourceObject, Collections.newSetFromMap(new IdentityHashMap<>()), (target, field, value) -> + field.set(target, encryptorManager.decrypt(value, contextFactory.create(field)))); + } + + private void handle(Object sourceObject, Set visited, FieldHandler fieldHandler) { + if (ObjectUtil.isNull(sourceObject) || sourceObject instanceof String || visited.contains(sourceObject)) { + return; + } + visited.add(sourceObject); + if (sourceObject instanceof Map map) { + new HashSet<>(map.values()).forEach(value -> handle(value, visited, fieldHandler)); + return; + } + if (sourceObject instanceof Collection collection) { + if (CollUtil.isEmpty(collection)) { + return; + } + collection.forEach(item -> handle(item, visited, fieldHandler)); + return; + } + Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); + if (CollUtil.isEmpty(fields)) { + return; + } + try { + for (Field field : fields) { + String value = Convert.toStr(field.get(sourceObject)); + if (ObjectUtil.isNotNull(value)) { + fieldHandler.handle(sourceObject, field, value); + } + } + } catch (Exception e) { + log.error("处理加密字段时出错", e); + } + } + + @FunctionalInterface + private interface FieldHandler { + + void handle(Object target, Field field, String value) throws IllegalAccessException; + } + + /** + * 字段原始值快照。 + */ + public record FieldSnapshot(Object target, Field field, Object value) { + + /** + * 恢复原始字段值。 + */ + public void restore() { + try { + field.set(target, value); + } catch (IllegalAccessException e) { + log.error("恢复加密字段时出错", e); + } + } + } +} diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java index 5e2c7318e..2a8fcfcc4 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptorManager.java @@ -2,13 +2,13 @@ package org.dromara.common.encrypt.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ReflectUtil; -import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.io.Resources; import org.dromara.common.core.constant.Constants; -import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.encrypt.annotation.EncryptField; +import org.dromara.common.encrypt.enums.AlgorithmType; +import org.dromara.common.encrypt.enums.EncodeType; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; @@ -28,17 +28,15 @@ import java.util.stream.Collectors; /** * 加密管理类 * - * @author 老马 - * @version 4.6.0 + * @author Lion Li */ @Slf4j -@NoArgsConstructor public class EncryptorManager { /** * 缓存加密器 */ - Map encryptorMap = new ConcurrentHashMap<>(); + Map encryptorMap = new ConcurrentHashMap<>(); /** * 类加密字段缓存 @@ -59,7 +57,13 @@ public class EncryptorManager { * 获取类加密字段缓存 */ public Set getFieldCache(Class sourceClazz) { - return ObjectUtils.notNullGetter(fieldCache, f -> f.get(sourceClazz)); + if (sourceClazz == null || sourceClazz.isPrimitive() || sourceClazz.isArray() + || sourceClazz.isEnum() || sourceClazz.isAnnotation() + || ClassUtils.isPrimitiveOrWrapper(sourceClazz) + || sourceClazz.getName().startsWith("java.")) { + return Set.of(); + } + return fieldCache.computeIfAbsent(sourceClazz, this::getEncryptFieldSetFromClazz); } /** @@ -68,13 +72,9 @@ public class EncryptorManager { * @param encryptContext 加密执行者需要的相关配置参数 */ public IEncryptor registAndGetEncryptor(EncryptContext encryptContext) { - int key = encryptContext.hashCode(); - if (encryptorMap.containsKey(key)) { - return encryptorMap.get(key); - } - IEncryptor encryptor = ReflectUtil.newInstance(encryptContext.getAlgorithm().getClazz(), encryptContext); - encryptorMap.put(key, encryptor); - return encryptor; + EncryptorCacheKey key = EncryptorCacheKey.of(encryptContext); + return encryptorMap.computeIfAbsent(key, cacheKey -> + ReflectUtil.newInstance(cacheKey.algorithm().getClazz(), encryptContext)); } /** @@ -83,7 +83,7 @@ public class EncryptorManager { * @param encryptContext 加密执行者需要的相关配置参数 */ public void removeEncryptor(EncryptContext encryptContext) { - this.encryptorMap.remove(encryptContext.hashCode()); + this.encryptorMap.remove(EncryptorCacheKey.of(encryptContext)); } /** @@ -120,6 +120,9 @@ public class EncryptorManager { * 通过 typeAliasesPackage 设置的扫描包 扫描缓存实体 */ private void scanEncryptClasses(String typeAliasesPackage) { + if (StringUtils.isBlank(typeAliasesPackage)) { + return; + } PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); CachingMetadataReaderFactory factory = new CachingMetadataReaderFactory(); String[] packagePatternArray = StringUtils.splitPreserveAllTokens(typeAliasesPackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); @@ -149,7 +152,7 @@ public class EncryptorManager { Set fieldSet = new HashSet<>(); // 判断clazz如果是接口,内部类,匿名类就直接返回 if (clazz.isInterface() || clazz.isMemberClass() || clazz.isAnonymousClass()) { - return fieldSet; + return Set.of(); } while (clazz != null) { Field[] fields = clazz.getDeclaredFields(); @@ -165,4 +168,22 @@ public class EncryptorManager { return fieldSet; } + private record EncryptorCacheKey( + AlgorithmType algorithm, + EncodeType encode, + String password, + String publicKey, + String privateKey + ) { + + private static EncryptorCacheKey of(EncryptContext encryptContext) { + return new EncryptorCacheKey( + encryptContext.getAlgorithm(), + encryptContext.getEncode(), + encryptContext.getPassword(), + encryptContext.getPublicKey(), + encryptContext.getPrivateKey()); + } + } + } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java index 2445541e5..f3eb24ab5 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java @@ -6,7 +6,6 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.exception.ServiceException; -import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.encrypt.annotation.ApiEncrypt; import org.dromara.common.encrypt.properties.ApiDecryptProperties; @@ -27,9 +26,15 @@ import java.io.IOException; */ public class CryptoFilter implements Filter { private final ApiDecryptProperties properties; + private final RequestMappingHandlerMapping requestMappingHandlerMapping; + private final HandlerExceptionResolver handlerExceptionResolver; - public CryptoFilter(ApiDecryptProperties properties) { + public CryptoFilter(ApiDecryptProperties properties, + RequestMappingHandlerMapping requestMappingHandlerMapping, + HandlerExceptionResolver handlerExceptionResolver) { this.properties = properties; + this.requestMappingHandlerMapping = requestMappingHandlerMapping; + this.handlerExceptionResolver = handlerExceptionResolver; EncryptUtils.validateRsaPublicKey(properties.getPublicKey()); EncryptUtils.validateRsaPrivateKey(properties.getPrivateKey()); } @@ -55,8 +60,7 @@ public class CryptoFilter implements Filter { } else { // 是否有注解,有就报错,没有放行 if (ObjectUtil.isNotNull(apiEncrypt)) { - HandlerExceptionResolver exceptionResolver = SpringUtils.getBean("handlerExceptionResolver", HandlerExceptionResolver.class); - exceptionResolver.resolveException( + handlerExceptionResolver.resolveException( servletRequest, servletResponse, null, new ServiceException("没有访问权限,请联系管理员授权", HttpStatus.FORBIDDEN)); return; @@ -75,7 +79,6 @@ public class CryptoFilter implements Filter { ObjectUtil.defaultIfNull(responseWrapper, response)); if (responseFlag) { - servletResponse.reset(); // 对原始内容加密 String encryptContent = responseBodyWrapper.getEncryptContent( servletResponse, properties.getPublicKey(), properties.getHeaderFlag()); @@ -88,10 +91,9 @@ public class CryptoFilter implements Filter { * 获取 ApiEncrypt 注解 */ private ApiEncrypt getApiEncryptAnnotation(HttpServletRequest servletRequest) { - RequestMappingHandlerMapping handlerMapping = SpringUtils.getBean("requestMappingHandlerMapping", RequestMappingHandlerMapping.class); // 获取注解 try { - HandlerExecutionChain mappingHandler = handlerMapping.getHandler(servletRequest); + HandlerExecutionChain mappingHandler = requestMappingHandlerMapping.getHandler(servletRequest); if (ObjectUtil.isNotNull(mappingHandler)) { Object handler = mappingHandler.getHandler(); if (ObjectUtil.isNotNull(handler)) { diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java index 98f4bc7c8..b3afab84f 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java @@ -13,6 +13,7 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; /** @@ -41,7 +42,8 @@ public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper { @Override public BufferedReader getReader() { - return new BufferedReader(new InputStreamReader(getInputStream())); + Charset charset = Charset.forName(getCharacterEncoding()); + return new BufferedReader(new InputStreamReader(getInputStream(), charset)); } @@ -72,17 +74,17 @@ public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper { @Override public int available() { - return body.length; + return bais.available(); } @Override public boolean isFinished() { - return false; + return bais.available() == 0; } @Override public boolean isReady() { - return false; + return true; } @Override diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java index 78e06ff73..06cb1f096 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java @@ -1,6 +1,5 @@ package org.dromara.common.encrypt.filter; -import cn.hutool.core.util.RandomUtil; import jakarta.servlet.ServletOutputStream; import jakarta.servlet.WriteListener; import jakarta.servlet.http.HttpServletResponse; @@ -8,7 +7,10 @@ import jakarta.servlet.http.HttpServletResponseWrapper; import org.dromara.common.encrypt.utils.EncryptUtils; import java.io.*; +import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Base64; /** * 加密响应参数包装类 @@ -17,19 +19,26 @@ import java.nio.charset.StandardCharsets; */ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { + private static final SecureRandom SECURE_RANDOM = new SecureRandom(); + private final ByteArrayOutputStream byteArrayOutputStream; private final ServletOutputStream servletOutputStream; - private final PrintWriter printWriter; + private PrintWriter printWriter; + private Charset charset; public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException { super(response); this.byteArrayOutputStream = new ByteArrayOutputStream(); this.servletOutputStream = this.getOutputStream(); - this.printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream)); + this.charset = resolveCharset(response); } @Override public PrintWriter getWriter() { + if (printWriter == null) { + charset = resolveCharset((HttpServletResponse) getResponse()); + printWriter = new PrintWriter(new OutputStreamWriter(byteArrayOutputStream, charset)); + } return printWriter; } @@ -48,6 +57,11 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { byteArrayOutputStream.reset(); } + @Override + public void resetBuffer() { + byteArrayOutputStream.reset(); + } + public byte[] getResponseData() throws IOException { flushBuffer(); return byteArrayOutputStream.toByteArray(); @@ -55,7 +69,7 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { public String getContent() throws IOException { flushBuffer(); - return byteArrayOutputStream.toString(); + return byteArrayOutputStream.toString(charset); } /** @@ -69,7 +83,7 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { */ public String getEncryptContent(HttpServletResponse servletResponse, String publicKey, String headerFlag) throws IOException { // 生成秘钥 - String aesPassword = RandomUtil.randomString(32); + String aesPassword = generateAesPassword(); // 秘钥使用 Base64 编码 String encryptAes = EncryptUtils.encryptByBase64(aesPassword); // Rsa 公钥加密 Base64 编码 @@ -78,16 +92,16 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { // 设置响应头 // vue版本需要设置 servletResponse.addHeader("Access-Control-Expose-Headers", headerFlag); - servletResponse.setHeader("Access-Control-Allow-Origin", "*"); - servletResponse.setHeader("Access-Control-Allow-Methods", "*"); servletResponse.setHeader(headerFlag, encryptPassword); - servletResponse.setCharacterEncoding(StandardCharsets.UTF_8.toString()); + servletResponse.setCharacterEncoding(charset.name()); // 获取原始内容 String originalBody = this.getContent(); // 对内容进行加密 - return EncryptUtils.encryptByAes(originalBody, aesPassword); + String encryptContent = EncryptUtils.encryptByAes(originalBody, aesPassword); + servletResponse.setContentLengthLong(encryptContent.getBytes(charset).length); + return encryptContent; } @Override @@ -95,7 +109,7 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { return new ServletOutputStream() { @Override public boolean isReady() { - return false; + return true; } @Override @@ -120,4 +134,18 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { }; } + private Charset resolveCharset(HttpServletResponse response) { + String characterEncoding = response.getCharacterEncoding(); + if (characterEncoding == null) { + return StandardCharsets.UTF_8; + } + return Charset.forName(characterEncoding); + } + + private String generateAesPassword() { + byte[] bytes = new byte[24]; + SECURE_RANDOM.nextBytes(bytes); + return Base64.getEncoder().encodeToString(bytes); + } + } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java index 1122669e7..e278fcecd 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisDecryptInterceptor.java @@ -1,32 +1,18 @@ package org.dromara.common.encrypt.interceptor; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.util.ObjectUtil; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.resultset.ResultSetHandler; import org.apache.ibatis.plugin.*; -import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.encrypt.annotation.EncryptField; -import org.dromara.common.encrypt.core.EncryptContext; -import org.dromara.common.encrypt.core.EncryptorManager; -import org.dromara.common.encrypt.enums.AlgorithmType; -import org.dromara.common.encrypt.enums.EncodeType; -import org.dromara.common.encrypt.properties.EncryptorProperties; +import org.dromara.common.encrypt.core.EncryptedFieldProcessor; -import java.lang.reflect.Field; import java.sql.Statement; -import java.util.*; +import java.util.Properties; /** * 出参解密拦截器 * - * @author 老马 - * @version 4.6.0 + * @author Lion Li */ -@Slf4j @Intercepts({@Signature( type = ResultSetHandler.class, method = "handleResultSets", @@ -35,91 +21,19 @@ import java.util.*; @AllArgsConstructor public class MybatisDecryptInterceptor implements Interceptor { - private final EncryptorManager encryptorManager; - private final EncryptorProperties defaultProperties; + private final EncryptedFieldProcessor encryptedFieldProcessor; @Override public Object intercept(Invocation invocation) throws Throwable { - // 开始进行参数解密 - ResultSetHandler resultSetHandler = (ResultSetHandler) invocation.getTarget(); - Field parameterHandlerField = resultSetHandler.getClass().getDeclaredField("parameterHandler"); - parameterHandlerField.setAccessible(true); - Object target = parameterHandlerField.get(resultSetHandler); - if (target instanceof ParameterHandler parameterHandler) { - Object parameterObject = parameterHandler.getParameterObject(); - if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) { - this.decryptHandler(parameterObject); - } - } // 获取执行mysql执行结果 Object result = invocation.proceed(); if (result == null) { return null; } - this.decryptHandler(result); + encryptedFieldProcessor.decrypt(result); return result; } - /** - * 解密对象 - * - * @param sourceObject 待加密对象 - */ - private void decryptHandler(Object sourceObject) { - if (ObjectUtil.isNull(sourceObject)) { - return; - } - if (sourceObject instanceof Map map) { - new HashSet<>(map.values()).forEach(this::decryptHandler); - return; - } - if (sourceObject instanceof List list) { - if(CollUtil.isEmpty(list)) { - return; - } - // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 - Object firstItem = list.get(0); - if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { - return; - } - list.forEach(this::decryptHandler); - return; - } - // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错) - Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); - if(ObjectUtil.isNull(fields)){ - return; - } - try { - for (Field field : fields) { - field.set(sourceObject, this.decryptField(Convert.toStr(field.get(sourceObject)), field)); - } - } catch (Exception e) { - log.error("处理解密字段时出错", e); - } - } - - /** - * 字段值进行加密。通过字段的批注注册新的加密算法 - * - * @param value 待加密的值 - * @param field 待加密字段 - * @return 加密后结果 - */ - private String decryptField(String value, Field field) { - if (ObjectUtil.isNull(value)) { - return null; - } - EncryptField encryptField = field.getAnnotation(EncryptField.class); - EncryptContext encryptContext = new EncryptContext(); - encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); - encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); - encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); - encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); - encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); - return this.encryptorManager.decrypt(value, encryptContext); - } - @Override public Object plugin(Object target) { return Plugin.wrap(target, this); diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java index 3525f19a0..7659bd9b7 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/interceptor/MybatisEncryptInterceptor.java @@ -1,31 +1,20 @@ package org.dromara.common.encrypt.interceptor; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.plugin.*; -import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.encrypt.annotation.EncryptField; -import org.dromara.common.encrypt.core.EncryptContext; -import org.dromara.common.encrypt.core.EncryptorManager; -import org.dromara.common.encrypt.enums.AlgorithmType; -import org.dromara.common.encrypt.enums.EncodeType; -import org.dromara.common.encrypt.properties.EncryptorProperties; +import org.dromara.common.encrypt.core.EncryptedFieldProcessor; -import java.lang.reflect.Field; import java.sql.PreparedStatement; -import java.util.*; +import java.util.List; +import java.util.Properties; /** * 入参加密拦截器 * - * @author 老马 - * @version 4.6.0 + * @author Lion Li */ -@Slf4j @Intercepts({@Signature( type = ParameterHandler.class, method = "setParameters", @@ -34,19 +23,25 @@ import java.util.*; @AllArgsConstructor public class MybatisEncryptInterceptor implements Interceptor { - private final EncryptorManager encryptorManager; - private final EncryptorProperties defaultProperties; + private final EncryptedFieldProcessor encryptedFieldProcessor; @Override public Object intercept(Invocation invocation) throws Throwable { + List snapshots = List.of(); Object target = invocation.getTarget(); if (target instanceof ParameterHandler parameterHandler) { Object parameterObject = parameterHandler.getParameterObject(); if (ObjectUtil.isNotNull(parameterObject) && !(parameterObject instanceof String)) { - this.encryptHandler(parameterObject); + snapshots = encryptedFieldProcessor.encrypt(parameterObject); + } + } + try { + return invocation.proceed(); + } finally { + for (EncryptedFieldProcessor.FieldSnapshot snapshot : snapshots) { + snapshot.restore(); } } - return invocation.proceed(); } @Override @@ -54,67 +49,6 @@ public class MybatisEncryptInterceptor implements Interceptor { return Plugin.wrap(target, this); } - /** - * 加密对象 - * - * @param sourceObject 待加密对象 - */ - private void encryptHandler(Object sourceObject) { - if (ObjectUtil.isNull(sourceObject)) { - return; - } - if (sourceObject instanceof Map map) { - new HashSet<>(map.values()).forEach(this::encryptHandler); - return; - } - if (sourceObject instanceof List list) { - if(CollUtil.isEmpty(list)) { - return; - } - // 判断第一个元素是否含有注解。如果没有直接返回,提高效率 - Object firstItem = list.get(0); - if (ObjectUtil.isNull(firstItem) || CollUtil.isEmpty(encryptorManager.getFieldCache(firstItem.getClass()))) { - return; - } - list.forEach(this::encryptHandler); - return; - } - // 不在缓存中的类,就是没有加密注解的类(当然也有可能是typeAliasesPackage写错) - Set fields = encryptorManager.getFieldCache(sourceObject.getClass()); - if(ObjectUtil.isNull(fields)){ - return; - } - try { - for (Field field : fields) { - field.set(sourceObject, this.encryptField(Convert.toStr(field.get(sourceObject)), field)); - } - } catch (Exception e) { - log.error("处理加密字段时出错", e); - } - } - - /** - * 字段值进行加密。通过字段的批注注册新的加密算法 - * - * @param value 待加密的值 - * @param field 待加密字段 - * @return 加密后结果 - */ - private String encryptField(String value, Field field) { - if (ObjectUtil.isNull(value)) { - return null; - } - EncryptField encryptField = field.getAnnotation(EncryptField.class); - EncryptContext encryptContext = new EncryptContext(); - encryptContext.setAlgorithm(encryptField.algorithm() == AlgorithmType.DEFAULT ? defaultProperties.getAlgorithm() : encryptField.algorithm()); - encryptContext.setEncode(encryptField.encode() == EncodeType.DEFAULT ? defaultProperties.getEncode() : encryptField.encode()); - encryptContext.setPassword(StringUtils.isBlank(encryptField.password()) ? defaultProperties.getPassword() : encryptField.password()); - encryptContext.setPrivateKey(StringUtils.isBlank(encryptField.privateKey()) ? defaultProperties.getPrivateKey() : encryptField.privateKey()); - encryptContext.setPublicKey(StringUtils.isBlank(encryptField.publicKey()) ? defaultProperties.getPublicKey() : encryptField.publicKey()); - return this.encryptorManager.encrypt(value, encryptContext); - } - - @Override public void setProperties(Properties properties) { } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/ApiDecryptProperties.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/ApiDecryptProperties.java index 6aadb3e03..969efbf2b 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/ApiDecryptProperties.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/ApiDecryptProperties.java @@ -19,7 +19,7 @@ public class ApiDecryptProperties { /** * 头部标识 */ - private String headerFlag; + private String headerFlag = "encrypt-key"; /** * 响应加密公钥 diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/EncryptorProperties.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/EncryptorProperties.java index a6886b061..04b9e1aca 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/EncryptorProperties.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/properties/EncryptorProperties.java @@ -23,7 +23,7 @@ public class EncryptorProperties { /** * 默认算法 */ - private AlgorithmType algorithm; + private AlgorithmType algorithm = AlgorithmType.BASE64; /** * 安全秘钥 @@ -43,6 +43,6 @@ public class EncryptorProperties { /** * 编码方式,base64/hex */ - private EncodeType encode; + private EncodeType encode = EncodeType.BASE64; } From 7b66f0de26631be925d63e832135cc1e0699bb10 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: Sat, 16 May 2026 13:56:35 +0800 Subject: [PATCH 17/35] =?UTF-8?q?refactor(common-excel):=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=20Excel=20=E5=AF=BC=E5=85=A5=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 删除 ExcelUtil,统一由 ExcelBuilder 承载导入、导出、模板导出、ZIP 导出和自定义写出能力 - 新增 ExcelBuilder.read(...) 导入构建器,支持 validate、failFast、listener、sheet、密码、空行、表头行、trim/strip、限制读取行数等配置 - 扩展导出构建器,支持动态列、sheet 编号、导出密码、表头控制、固定列宽/行高、自定义 Converter 和 WriteHandler - 将业务代码中的 ExcelUtil 调用全部替换为 ExcelBuilder 链式调用 - 同步调整代码生成器 controller 模板,后续生成代码默认使用 ExcelBuilder - 将文件名编码、响应头处理、字典表达式转换收敛为内部实现细节 --- .../excel/convert/ExcelDictConvert.java | 86 +- .../excel/convert/ExcelEnumConvert.java | 21 +- .../common/excel/core/CellMergeHandler.java | 9 +- .../excel/core/DefaultExcelListener.java | 19 +- .../common/excel/core/ExcelDownHandler.java | 58 +- .../excel/handler/DataWriteHandler.java | 8 +- .../common/excel/utils/ExcelBuilder.java | 895 ++++++++++++++++++ .../dromara/common/excel/utils/ExcelUtil.java | 626 ------------ .../demo/controller/TestDemoController.java | 8 +- .../demo/controller/TestExcelController.java | 24 +- .../demo/controller/TestTreeController.java | 4 +- .../demo/service/IExportExcelService.java | 4 +- .../service/impl/ExportExcelServiceImpl.java | 17 +- .../main/resources/vm/java/controller.java.vm | 4 +- .../monitor/SysLoginInfoController.java | 4 +- .../monitor/SysOperlogController.java | 4 +- .../system/SysClientController.java | 4 +- .../system/SysConfigController.java | 4 +- .../system/SysDictDataController.java | 4 +- .../system/SysDictTypeController.java | 4 +- .../controller/system/SysPostController.java | 4 +- .../controller/system/SysRoleController.java | 4 +- .../controller/system/SysUserController.java | 10 +- .../controller/FlwCategoryController.java | 4 +- .../controller/TestLeaveController.java | 4 +- 25 files changed, 1109 insertions(+), 724 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelBuilder.java delete mode 100644 ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java index 5df7efb73..19d883429 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelDictConvert.java @@ -13,10 +13,12 @@ import org.dromara.common.excel.annotation.ExcelDictFormat; import org.dromara.common.core.service.DictService; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.excel.utils.ExcelUtil; import lombok.extern.slf4j.Slf4j; import java.lang.reflect.Field; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Pattern; /** * 字典格式化转换处理 @@ -26,6 +28,8 @@ import java.lang.reflect.Field; @Slf4j public class ExcelDictConvert implements Converter { + private DictService dictService; + @Override public Class supportJavaTypeKey() { return Object.class; @@ -43,9 +47,9 @@ public class ExcelDictConvert implements Converter { String label = cellData.getStringValue(); String value; if (StringUtils.isBlank(type)) { - value = ExcelUtil.reverseByExp(label, anno.readConverterExp(), anno.separator()); + value = reverseByExp(label, anno.readConverterExp(), anno.separator()); } else { - value = SpringUtils.getBean(DictService.class).getDictValue(type, label, anno.separator()); + value = getDictService().getDictValue(type, label, anno.separator()); } return Convert.convert(contentProperty.getField().getType(), value); } @@ -60,9 +64,9 @@ public class ExcelDictConvert implements Converter { String value = Convert.toStr(object); String label; if (StringUtils.isBlank(type)) { - label = ExcelUtil.convertByExp(value, anno.readConverterExp(), anno.separator()); + label = convertByExp(value, anno.readConverterExp(), anno.separator()); } else { - label = SpringUtils.getBean(DictService.class).getDictLabel(type, value, anno.separator()); + label = getDictService().getDictLabel(type, value, anno.separator()); } return new WriteCellData<>(label); } @@ -70,4 +74,76 @@ public class ExcelDictConvert implements Converter { private ExcelDictFormat getAnnotation(Field field) { return AnnotationUtil.getAnnotation(field, ExcelDictFormat.class); } + + private DictService getDictService() { + if (dictService == null) { + dictService = SpringUtils.getBean(DictService.class); + } + return dictService; + } + + /** + * 解析导出值 0=男,1=女,2=未知。 + */ + private static String convertByExp(String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + Map convertSource = parseConverterExp(converterExp); + for (Map.Entry item : convertSource.entrySet()) { + if (StringUtils.contains(propertyValue, separator)) { + for (String value : splitPropertyValue(propertyValue, separator)) { + if (item.getKey().equals(value)) { + propertyString.append(item.getValue()).append(separator); + break; + } + } + } else { + if (item.getKey().equals(propertyValue)) { + return item.getValue(); + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + /** + * 反向解析值 男=0,女=1,未知=2。 + */ + private static String reverseByExp(String propertyValue, String converterExp, String separator) { + StringBuilder propertyString = new StringBuilder(); + Map convertSource = parseConverterExp(converterExp); + for (Map.Entry item : convertSource.entrySet()) { + if (StringUtils.contains(propertyValue, separator)) { + for (String value : splitPropertyValue(propertyValue, separator)) { + if (item.getValue().equals(value)) { + propertyString.append(item.getKey()).append(separator); + break; + } + } + } else { + if (item.getValue().equals(propertyValue)) { + return item.getKey(); + } + } + } + return StringUtils.stripEnd(propertyString.toString(), separator); + } + + private static Map parseConverterExp(String converterExp) { + Map result = new LinkedHashMap<>(); + if (StringUtils.isBlank(converterExp)) { + return result; + } + for (String item : converterExp.split(StringUtils.SEPARATOR)) { + String[] itemArray = item.split("=", 2); + if (itemArray.length != 2) { + throw new IllegalArgumentException("Excel转换表达式格式错误: " + item); + } + result.put(itemArray[0], itemArray[1]); + } + return result; + } + + private static String[] splitPropertyValue(String propertyValue, String separator) { + return propertyValue.split(Pattern.quote(separator)); + } } diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java index 877218d90..7c6c787cf 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/convert/ExcelEnumConvert.java @@ -27,7 +27,7 @@ import java.util.concurrent.ConcurrentHashMap; public class ExcelEnumConvert implements Converter { private static final Map> ENUM_MAP_CACHE = new ConcurrentHashMap<>(); - private static final Map> ENUM_REVERSE_MAP_CACHE = new ConcurrentHashMap<>(); + private static final Map> ENUM_REVERSE_MAP_CACHE = new ConcurrentHashMap<>(); @Override public Class supportJavaTypeKey() { @@ -55,16 +55,24 @@ public class ExcelEnumConvert implements Converter { } Map enumCodeToTextMap = beforeConvert(contentProperty); // 从Java输出至Excel是code转text,从Excel转Java应将text与code对调 - Map enumTextToCodeMap = ENUM_REVERSE_MAP_CACHE.computeIfAbsent( + Map enumTextToCodeMap = ENUM_REVERSE_MAP_CACHE.computeIfAbsent( contentProperty.getField(), f -> { - Map reverseMap = new HashMap<>(); - enumCodeToTextMap.forEach((key, value) -> reverseMap.put(value, key)); + Map reverseMap = new HashMap<>(); + enumCodeToTextMap.forEach((key, value) -> { + Object oldValue = reverseMap.put(value, key); + if (ObjectUtil.isNotNull(oldValue)) { + throw new IllegalArgumentException("枚举导入文本值重复: " + value); + } + }); return reverseMap; } ); // 应该从text -> code中查找 - Object codeValue = enumTextToCodeMap.get(textValue); + Object codeValue = enumTextToCodeMap.get(Convert.toStr(textValue)); + if (ObjectUtil.isNull(codeValue)) { + throw new IllegalArgumentException("枚举值不匹配: " + textValue + ",允许值: " + enumTextToCodeMap.keySet()); + } return Convert.convert(contentProperty.getField().getType(), codeValue); } @@ -86,6 +94,9 @@ public class ExcelEnumConvert implements Converter { for (Enum enumConstant : enumConstants) { Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField()); String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField()); + if (ObjectUtil.isNull(codeValue) || ObjectUtil.isNull(textValue)) { + throw new IllegalArgumentException("枚举字段 code/text 不能为空: " + enumConstant.name()); + } enumValueMap.put(codeValue, textValue); } return enumValueMap; 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 cc2232fd0..a8dca7b2f 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 @@ -129,11 +129,14 @@ public class CellMergeHandler { continue; } CellMerge cm = field.getAnnotation(CellMerge.class); - int index = cm.index() == -1 ? i : cm.index(); + ExcelProperty property = field.getAnnotation(ExcelProperty.class); + int index = cm.index(); + if (index == -1) { + index = property != null && property.index() != -1 ? property.index() : i; + } mergeFields.put(field, FieldColumnIndex.of(index, cm)); - if (hasTitle) { - ExcelProperty property = field.getAnnotation(ExcelProperty.class); + if (hasTitle && property != null && property.value().length > 0) { rowIndex = Math.max(rowIndex, property.value().length); } } diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java index 513c556a5..ac5bf599d 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java @@ -39,13 +39,22 @@ public class DefaultExcelListener extends AnalysisEventListener implements /** * 导入回执 */ - private ExcelResult excelResult; + private final ExcelResult excelResult = new DefaultExcelResult<>(); + + /** + * 发生异常时是否立即终止读取,默认保持原有快速失败行为 + */ + private Boolean failFast = Boolean.TRUE; public DefaultExcelListener(boolean isValidate) { - this.excelResult = new DefaultExcelResult<>(); this.isValidate = isValidate; } + public DefaultExcelListener(boolean isValidate, boolean failFast) { + this.isValidate = isValidate; + this.failFast = failFast; + } + /** * 处理异常 * @@ -60,7 +69,7 @@ public class DefaultExcelListener extends AnalysisEventListener implements Integer rowIndex = excelDataConvertException.getRowIndex(); Integer columnIndex = excelDataConvertException.getColumnIndex(); errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常
", - rowIndex + 1, columnIndex + 1, headMap.get(columnIndex)); + rowIndex + 1, columnIndex + 1, headMap == null ? "" : headMap.get(columnIndex)); if (log.isDebugEnabled()) { log.warn(errMsg); } @@ -78,7 +87,9 @@ public class DefaultExcelListener extends AnalysisEventListener implements log.warn(errMsg, exception); } excelResult.getErrorList().add(errMsg); - throw new ExcelAnalysisException(errMsg); + if (failFast) { + throw new ExcelAnalysisException(errMsg); + } } @Override diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java index 36d4d0de6..e16bc49f7 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -15,6 +15,7 @@ import org.apache.fesod.sheet.write.metadata.holder.WriteWorkbookHolder; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddressList; +import org.apache.poi.ss.util.CellReference; import org.apache.poi.ss.util.WorkbookUtil; import org.apache.poi.xssf.usermodel.XSSFDataValidation; import org.dromara.common.core.exception.ServiceException; @@ -40,11 +41,8 @@ import java.util.*; @Slf4j public class ExcelDownHandler implements SheetWriteHandler { - /** - * Excel表格中的列名英文 - * 仅为了解析列英文,禁止修改 - */ - private static final String EXCEL_COLUMN_NAME = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + private static final int FIRST_DATA_ROW_INDEX = 1; + private static final int LAST_DATA_ROW_INDEX = 1000; /** * 单选数据Sheet名 */ @@ -57,7 +55,7 @@ public class ExcelDownHandler implements SheetWriteHandler { * 下拉可选项 */ private final List dropDownOptions; - private final DictService dictService; + private DictService dictService; /** * 当前单选进度 */ @@ -71,7 +69,6 @@ public class ExcelDownHandler implements SheetWriteHandler { this.dropDownOptions = options; this.currentOptionsColumnIndex = 0; this.currentLinkedOptionsSheetIndex = 0; - this.dictService = SpringUtils.getBean(DictService.class); } /** @@ -79,7 +76,7 @@ public class ExcelDownHandler implements SheetWriteHandler { * 1.通过解析传入的@ExcelProperty同级是否标注有@DropDown选项 * 如果有且设置了value值,则将其直接置为下拉可选项 *

- * 2.或者在调用ExcelUtil时指定了可选项,将依据传入的可选项做下拉 + * 2.或者在调用ExcelBuilder时指定了可选项,将依据传入的可选项做下拉 *

* 3.二者并存,注意调用方式 */ @@ -104,14 +101,20 @@ public class ExcelDownHandler implements SheetWriteHandler { String converterExp = format.readConverterExp(); if (StringUtils.isNotBlank(dictType)) { // 如果传递了字典名,则依据字典建立下拉 - Collection values = Optional.ofNullable(dictService.getAllDictByDictType(dictType)) + Collection values = Optional.ofNullable(getDictService().getAllDictByDictType(dictType)) .orElseThrow(() -> new ServiceException("字典 {} 不存在", dictType)) .values(); options = new ArrayList<>(values); } else if (StringUtils.isNotBlank(converterExp)) { // 如果指定了确切的值,则直接解析确切的值 - List strList = StringUtils.splitList(converterExp, format.separator()); - options = StreamUtils.toList(strList, s -> StringUtils.split(s, "=")[1]); + List strList = StringUtils.splitList(converterExp, StringUtils.SEPARATOR); + options = StreamUtils.toList(strList, s -> { + String[] itemArray = s.split("=", 2); + if (itemArray.length != 2) { + throw new ServiceException("Excel转换表达式格式错误: {}", s); + } + return itemArray[1]; + }); } } else if (field.isAnnotationPresent(ExcelEnumFormat.class)) { // 否则如果指定了@ExcelEnumFormat,则使用枚举的逻辑 @@ -145,10 +148,10 @@ public class ExcelDownHandler implements SheetWriteHandler { } dropDownOptions.forEach(everyOptions -> { // 如果传递了下拉框选择器参数 - if (!everyOptions.getNextOptions().isEmpty()) { + if (CollUtil.isNotEmpty(everyOptions.getNextOptions())) { // 当二级选项不为空时,使用额外关联表的形式 dropDownLinkedOptions(helper, workbook, sheet, everyOptions); - } else if (everyOptions.getOptions().size() > 10) { + } else if (CollUtil.isNotEmpty(everyOptions.getOptions()) && everyOptions.getOptions().size() > 10) { // 当一级选项参数个数大于10,使用额外表的形式 dropDownWithSheet(helper, workbook, sheet, everyOptions.getIndex(), everyOptions.getOptions()); } else { @@ -185,6 +188,9 @@ public class ExcelDownHandler implements SheetWriteHandler { workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true); // 选项数据(使用副本,避免修改调用方的原始数据) List firstOptions = options.getOptions(); + if (CollUtil.isEmpty(firstOptions)) { + return; + } Map> secoundOptionsMap = new HashMap<>(); options.getNextOptions().forEach((k, v) -> secoundOptionsMap.put(k, new ArrayList<>(v))); @@ -210,7 +216,7 @@ public class ExcelDownHandler implements SheetWriteHandler { String firstOptionsFunction = String.format("%s!$%s$1:$%s$1", linkedOptionsSheetName, getExcelColumnName(0), - getExcelColumnName(firstOptions.size()) + getExcelColumnName(firstOptions.size() - 1) ); // 设置名称管理器的引用位置 name.setRefersToFormula(firstOptionsFunction); @@ -242,7 +248,7 @@ public class ExcelDownHandler implements SheetWriteHandler { // 数据验证为序列模式,引用到每一个主表中的二级选项位置 // 创建子项的名称管理器,只是为了使得Excel可以识别到数据 String mainSheetFirstOptionsColumnName = getExcelColumnName(options.getIndex()); - for (int i = 0; i < 100; i++) { + for (int i = FIRST_DATA_ROW_INDEX; i <= LAST_DATA_ROW_INDEX; i++) { // 以一级选项对应的主体所在位置创建二级下拉 String secondOptionsFunction = String.format("=INDIRECT(%s%d)", mainSheetFirstOptionsColumnName, i + 1); // 二级只能主表每一行的每一列添加二级校验 @@ -348,7 +354,7 @@ public class ExcelDownHandler implements SheetWriteHandler { private void markOptionsToSheet(DataValidationHelper helper, Sheet sheet, Integer celIndex, DataValidationConstraint constraint) { // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列 - CellRangeAddressList addressList = new CellRangeAddressList(1, 1000, celIndex, celIndex); + CellRangeAddressList addressList = new CellRangeAddressList(FIRST_DATA_ROW_INDEX, LAST_DATA_ROW_INDEX, celIndex, celIndex); markDataValidationToSheet(helper, sheet, constraint, addressList); } @@ -397,17 +403,13 @@ public class ExcelDownHandler implements SheetWriteHandler { * @return 列index所在得英文名 */ private String getExcelColumnName(int columnIndex) { - // 26一循环的次数 - int columnCircleCount = columnIndex / 26; - // 26一循环内的位置 - int thisCircleColumnIndex = columnIndex % 26; - // 26一循环的次数大于0,则视为栏名至少两位 - String columnPrefix = columnCircleCount == 0 - ? StrUtil.EMPTY - : StrUtil.subWithLength(EXCEL_COLUMN_NAME, columnCircleCount - 1, 1); - // 从26一循环内取对应的栏位名 - String columnNext = StrUtil.subWithLength(EXCEL_COLUMN_NAME, thisCircleColumnIndex, 1); - // 将二者拼接即为最终的栏位名 - return columnPrefix + columnNext; + return CellReference.convertNumToColString(columnIndex); + } + + private DictService getDictService() { + if (dictService == null) { + dictService = SpringUtils.getBean(DictService.class); + } + return dictService; } } diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java index 62a03ca8b..541801764 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java @@ -16,6 +16,7 @@ import org.apache.poi.xssf.usermodel.XSSFClientAnchor; import org.apache.poi.xssf.usermodel.XSSFRichTextString; import org.dromara.common.excel.annotation.ExcelNotation; import org.dromara.common.excel.annotation.ExcelRequired; +import org.dromara.common.core.utils.reflect.ReflectUtils; import java.lang.reflect.Field; import java.util.HashMap; @@ -51,6 +52,9 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler { } // 第一行 WriteCellData cellData = context.getFirstCellData(); + if (cellData == null) { + return; + } // 第一个格子 WriteCellStyle writeCellStyle = cellData.getOrCreateStyle(); @@ -92,7 +96,7 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler { */ private static Map getRequiredMap(Class clazz) { Map requiredMap = new HashMap<>(); - Field[] fields = clazz.getDeclaredFields(); + Field[] fields = ReflectUtils.getFields(clazz); for (Field field : fields) { if (!field.isAnnotationPresent(ExcelRequired.class)) { continue; @@ -112,7 +116,7 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler { */ private static Map getNotationMap(Class clazz) { Map notationMap = new HashMap<>(); - Field[] fields = clazz.getDeclaredFields(); + Field[] fields = ReflectUtils.getFields(clazz); for (Field field : fields) { if (!field.isAnnotationPresent(ExcelNotation.class)) { continue; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelBuilder.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelBuilder.java new file mode 100644 index 000000000..6b87fee20 --- /dev/null +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelBuilder.java @@ -0,0 +1,895 @@ +package org.dromara.common.excel.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.io.resource.ClassPathResource; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ZipUtil; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.fesod.sheet.ExcelWriter; +import org.apache.fesod.sheet.FesodSheet; +import org.apache.fesod.sheet.converters.Converter; +import org.apache.fesod.sheet.read.builder.ExcelReaderBuilder; +import org.apache.fesod.sheet.read.builder.ExcelReaderSheetBuilder; +import org.apache.fesod.sheet.write.builder.ExcelWriterBuilder; +import org.apache.fesod.sheet.write.builder.ExcelWriterSheetBuilder; +import org.apache.fesod.sheet.write.handler.WriteHandler; +import org.apache.fesod.sheet.write.metadata.WriteSheet; +import org.apache.fesod.sheet.write.metadata.fill.FillConfig; +import org.apache.fesod.sheet.write.metadata.fill.FillWrapper; +import org.apache.fesod.sheet.write.style.column.LongestMatchColumnWidthStyleStrategy; +import org.apache.fesod.sheet.write.style.column.SimpleColumnWidthStyleStrategy; +import org.apache.fesod.sheet.write.style.row.SimpleRowHeightStyleStrategy; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.file.FileUtils; +import org.dromara.common.excel.convert.ExcelBigNumberConvert; +import org.dromara.common.excel.core.CellMergeStrategy; +import org.dromara.common.excel.core.DefaultExcelListener; +import org.dromara.common.excel.core.DropDownOptions; +import org.dromara.common.excel.core.ExcelDownHandler; +import org.dromara.common.excel.core.ExcelListener; +import org.dromara.common.excel.core.ExcelResult; +import org.dromara.common.excel.handler.DataWriteHandler; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * Excel 导出构造器。 + * + * @author Lion Li + */ +public final class ExcelBuilder { + + private static final String DEFAULT_SHEET_NAME = "sheet1"; + + private static final int DEFAULT_ZIP_PAGE_SIZE = 999; + + private final List data; + + private final Class headType; + + private String sheetName = DEFAULT_SHEET_NAME; + + private Integer sheetNo; + + private boolean merge; + + private List options; + + private boolean zip; + + private int pageSize = DEFAULT_ZIP_PAGE_SIZE; + + private String password; + + private Boolean needHead; + + private Boolean automaticMergeHead; + + private Collection includeFields; + + private Collection excludeFields; + + private Collection includeIndexes; + + private Collection excludeIndexes; + + private Boolean orderByIncludeColumn; + + private Integer columnWidth; + + private Short headRowHeight; + + private Short contentRowHeight; + + private List writeHandlers; + + private List> converters; + + private ExcelBuilder(List data, Class headType) { + this.data = data; + this.headType = headType; + } + + /** + * 创建导出构造器。 + * + * @param data 导出数据 + * @param headType 表头类型 + * @return 导出构造器 + */ + public static ExcelBuilder of(List data, Class headType) { + return new ExcelBuilder<>(data, headType); + } + + /** + * 创建自定义写出构造器。 + * + * @param headType 表头类型 + * @return 导出构造器 + */ + public static ExcelBuilder writer(Class headType) { + return new ExcelBuilder<>(null, headType); + } + + /** + * 创建模板导出构造器。 + * + * @param templatePath 模板路径 + * @return 模板导出构造器 + */ + public static TemplateBuilder template(String templatePath) { + return new TemplateBuilder(templatePath); + } + + /** + * 创建导入读取构造器。 + * + * @param is 文件流 + * @param clazz 接收实体类 + * @return 导入读取构造器 + */ + public static ReadBuilder read(InputStream is, Class clazz) { + return new ReadBuilder<>(is, clazz); + } + + /** + * 设置工作表名称。 + */ + public ExcelBuilder sheetName(String sheetName) { + this.sheetName = StringUtils.blankToDefault(sheetName, DEFAULT_SHEET_NAME); + return this; + } + + /** + * 设置工作表编号。 + */ + public ExcelBuilder sheetNo(Integer sheetNo) { + this.sheetNo = sheetNo; + return this; + } + + /** + * 开启单元格合并。 + */ + public ExcelBuilder merge() { + return merge(true); + } + + /** + * 设置是否合并单元格。 + */ + public ExcelBuilder merge(boolean merge) { + this.merge = merge; + return this; + } + + /** + * 设置下拉选项。 + */ + public ExcelBuilder options(List options) { + this.options = options; + return this; + } + + /** + * 设置导出文件密码。 + */ + public ExcelBuilder password(String password) { + this.password = password; + return this; + } + + /** + * 设置是否写出表头。 + */ + public ExcelBuilder needHead(boolean needHead) { + this.needHead = needHead; + return this; + } + + /** + * 设置是否自动合并多级表头。 + */ + public ExcelBuilder automaticMergeHead(boolean automaticMergeHead) { + this.automaticMergeHead = automaticMergeHead; + return this; + } + + /** + * 仅导出指定字段。 + */ + public ExcelBuilder includeFields(Collection includeFields) { + this.includeFields = includeFields; + return this; + } + + /** + * 排除指定字段。 + */ + public ExcelBuilder excludeFields(Collection excludeFields) { + this.excludeFields = excludeFields; + return this; + } + + /** + * 仅导出指定列索引。 + */ + public ExcelBuilder includeIndexes(Collection includeIndexes) { + this.includeIndexes = includeIndexes; + return this; + } + + /** + * 排除指定列索引。 + */ + public ExcelBuilder excludeIndexes(Collection excludeIndexes) { + this.excludeIndexes = excludeIndexes; + return this; + } + + /** + * 设置是否按 include 列顺序导出。 + */ + public ExcelBuilder orderByIncludeColumn(boolean orderByIncludeColumn) { + this.orderByIncludeColumn = orderByIncludeColumn; + return this; + } + + /** + * 设置固定列宽。 + */ + public ExcelBuilder columnWidth(Integer columnWidth) { + if (columnWidth != null && columnWidth <= 0) { + throw new IllegalArgumentException("columnWidth 必须大于 0"); + } + this.columnWidth = columnWidth; + return this; + } + + /** + * 设置固定行高。 + */ + public ExcelBuilder rowHeight(short headRowHeight, short contentRowHeight) { + if (headRowHeight <= 0 || contentRowHeight <= 0) { + throw new IllegalArgumentException("rowHeight 必须大于 0"); + } + this.headRowHeight = headRowHeight; + this.contentRowHeight = contentRowHeight; + return this; + } + + /** + * 注册自定义写处理器。 + */ + public ExcelBuilder registerWriteHandler(WriteHandler writeHandler) { + if (writeHandler == null) { + return this; + } + if (writeHandlers == null) { + writeHandlers = new ArrayList<>(); + } + writeHandlers.add(writeHandler); + return this; + } + + /** + * 注册自定义转换器。 + */ + public ExcelBuilder registerConverter(Converter converter) { + if (converter == null) { + return this; + } + if (converters == null) { + converters = new ArrayList<>(); + } + converters.add(converter); + return this; + } + + /** + * 开启 ZIP 分页导出。 + */ + public ExcelBuilder zip() { + return zip(DEFAULT_ZIP_PAGE_SIZE); + } + + /** + * 开启 ZIP 分页导出。 + * + * @param pageSize 每个 Excel 文件的数据量 + */ + public ExcelBuilder zip(int pageSize) { + if (pageSize <= 0) { + throw new IllegalArgumentException("pageSize 必须大于 0"); + } + this.zip = true; + this.pageSize = pageSize; + return this; + } + + /** + * 写入 HTTP 响应。 + */ + public void toResponse(HttpServletResponse response) { + if (zip) { + exportZipToResponse(response); + return; + } + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + writeSheet(os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常", e); + } + } + + /** + * 写入输出流。 + */ + public void toStream(OutputStream outputStream) { + if (zip) { + throw new UnsupportedOperationException("ZIP导出请使用 toResponse(HttpServletResponse)"); + } + writeSheet(outputStream); + } + + /** + * 使用自定义写出逻辑写入输出流。 + */ + public void toStream(OutputStream outputStream, Consumer> consumer) { + try (ExcelWriter writer = createWriter(outputStream)) { + consumer.accept(ExcelWriterWrapper.of(writer)); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + * 使用自定义写出逻辑写入 HTTP 响应。 + */ + public void toResponse(HttpServletResponse response, Consumer> consumer) { + try { + resetResponse(sheetName, response); + toStream(response.getOutputStream(), consumer); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常", e); + } + } + + private void writeSheet(OutputStream outputStream) { + ExcelWriterSheetBuilder builder = createSheetBuilder(createWriterBuilder(outputStream)); + if (merge) { + // 合并处理器 + builder.registerWriteHandler(new CellMergeStrategy(data, true)); + } + builder.doWrite(data); + } + + private ExcelWriter createWriter(OutputStream outputStream) { + return createWriterBuilder(outputStream).build(); + } + + private ExcelWriterBuilder createWriterBuilder(OutputStream outputStream) { + ExcelWriterBuilder builder = FesodSheet.write(outputStream, headType) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + // 批注必填项处理 + .registerWriteHandler(new DataWriteHandler(headType)); + if (columnWidth == null) { + // 自动适配 + builder.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()); + } else { + builder.registerWriteHandler(new SimpleColumnWidthStyleStrategy(columnWidth)); + } + if (headRowHeight != null || contentRowHeight != null) { + builder.registerWriteHandler(new SimpleRowHeightStyleStrategy(headRowHeight, contentRowHeight)); + } + // 添加下拉框操作 + builder.registerWriteHandler(new ExcelDownHandler(options)); + if (StringUtils.isNotBlank(password)) { + builder.password(password); + } + if (needHead != null) { + builder.needHead(needHead); + } + if (automaticMergeHead != null) { + builder.automaticMergeHead(automaticMergeHead); + } + if (CollUtil.isNotEmpty(includeFields)) { + builder.includeColumnFieldNames(includeFields); + } + if (CollUtil.isNotEmpty(excludeFields)) { + builder.excludeColumnFieldNames(excludeFields); + } + if (CollUtil.isNotEmpty(includeIndexes)) { + builder.includeColumnIndexes(includeIndexes); + } + if (CollUtil.isNotEmpty(excludeIndexes)) { + builder.excludeColumnIndexes(excludeIndexes); + } + if (orderByIncludeColumn != null) { + builder.orderByIncludeColumn(orderByIncludeColumn); + } + if (CollUtil.isNotEmpty(converters)) { + converters.forEach(builder::registerConverter); + } + if (CollUtil.isNotEmpty(writeHandlers)) { + writeHandlers.forEach(builder::registerWriteHandler); + } + return builder; + } + + private ExcelWriterSheetBuilder createSheetBuilder(ExcelWriterBuilder builder) { + if (sheetNo != null) { + return builder.sheet(sheetNo, sheetName); + } + return builder.sheet(sheetName); + } + + private void exportZipToResponse(HttpServletResponse response) { + if (pageSize <= 0) { + throw new IllegalArgumentException("pageSize 必须大于 0"); + } + List> pageList = ListUtil.partition(data, pageSize); + if (pageList.size() <= 1) { + zip = false; + toResponse(response); + return; + } + try { + response.setContentType("application/zip"); + response.setHeader("Content-Disposition", + "attachment;filename*=UTF-8''" + URLEncoder.encode(sheetName, StandardCharsets.UTF_8) + ".zip"); + + try (ZipOutputStream zipOut = ZipUtil.getZipOutputStream(response.getOutputStream(), CharsetUtil.CHARSET_UTF_8)) { + for (int i = 0; i < pageList.size(); i++) { + int pageNum = i + 1; + String exportSheetName = sheetName + "_第" + pageNum + "页"; + byte[] bytes = buildZipEntry(pageList.get(i), exportSheetName); + zipOut.putNextEntry(new ZipEntry(exportSheetName + ".xlsx")); + zipOut.write(bytes); + zipOut.closeEntry(); + } + } + } catch (IOException e) { + throw new RuntimeException("导出Zip异常", e); + } + } + + private byte[] buildZipEntry(List pageData, String exportSheetName) { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { + ExcelBuilder builder = ExcelBuilder.of(pageData, headType) + .sheetName(exportSheetName); + copyOptionsTo(builder); + builder.toStream(bos); + return bos.toByteArray(); + } catch (Exception e) { + throw new RuntimeException(exportSheetName + "Excel生成失败", e); + } + } + + private void copyOptionsTo(ExcelBuilder builder) { + builder.merge = merge; + builder.options = options; + builder.password = password; + builder.needHead = needHead; + builder.automaticMergeHead = automaticMergeHead; + builder.includeFields = includeFields; + builder.excludeFields = excludeFields; + builder.includeIndexes = includeIndexes; + builder.excludeIndexes = excludeIndexes; + builder.orderByIncludeColumn = orderByIncludeColumn; + builder.columnWidth = columnWidth; + builder.headRowHeight = headRowHeight; + builder.contentRowHeight = contentRowHeight; + builder.writeHandlers = writeHandlers; + builder.converters = converters; + } + + /** + * 重置响应体。 + */ + private static void resetResponse(String filename, HttpServletResponse response) throws UnsupportedEncodingException { + FileUtils.setAttachmentResponseHeader(response, encodingFilename(filename)); + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); + } + + private static String encodingFilename(String filename) { + return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx"; + } + + /** + * Excel 导入读取构造器。 + */ + public static final class ReadBuilder { + + private final InputStream inputStream; + + private final Class headType; + + private boolean validate = true; + + private boolean failFast = true; + + private ExcelListener listener; + + private Integer sheetNo; + + private String sheetName; + + private Integer headRowNumber; + + private Boolean ignoreEmptyRow; + + private String password; + + private Boolean autoTrim; + + private Boolean autoStrip; + + private Integer numRows; + + private List> converters; + + private ReadBuilder(InputStream inputStream, Class headType) { + this.inputStream = inputStream; + this.headType = headType; + } + + /** + * 设置是否校验导入数据。 + */ + public ReadBuilder validate(boolean validate) { + this.validate = validate; + return this; + } + + /** + * 设置解析异常时是否立即终止读取。 + */ + public ReadBuilder failFast(boolean failFast) { + this.failFast = failFast; + return this; + } + + /** + * 设置自定义导入监听器。 + */ + public ReadBuilder listener(ExcelListener listener) { + this.listener = listener; + return this; + } + + /** + * 设置读取的工作表编号。 + */ + public ReadBuilder sheetNo(Integer sheetNo) { + this.sheetNo = sheetNo; + return this; + } + + /** + * 设置读取的工作表名称。 + */ + public ReadBuilder sheetName(String sheetName) { + this.sheetName = sheetName; + return this; + } + + /** + * 设置表头行数。 + */ + public ReadBuilder headRowNumber(Integer headRowNumber) { + if (headRowNumber != null && headRowNumber < 0) { + throw new IllegalArgumentException("headRowNumber 不能小于 0"); + } + this.headRowNumber = headRowNumber; + return this; + } + + /** + * 设置是否忽略空行。 + */ + public ReadBuilder ignoreEmptyRow(boolean ignoreEmptyRow) { + this.ignoreEmptyRow = ignoreEmptyRow; + return this; + } + + /** + * 设置读取文件密码。 + */ + public ReadBuilder password(String password) { + this.password = password; + return this; + } + + /** + * 设置是否自动 trim 字符串。 + */ + public ReadBuilder autoTrim(boolean autoTrim) { + this.autoTrim = autoTrim; + return this; + } + + /** + * 设置是否自动 strip 字符串。 + */ + public ReadBuilder autoStrip(boolean autoStrip) { + this.autoStrip = autoStrip; + return this; + } + + /** + * 设置最多读取行数。 + */ + public ReadBuilder numRows(Integer numRows) { + if (numRows != null && numRows <= 0) { + throw new IllegalArgumentException("numRows 必须大于 0"); + } + this.numRows = numRows; + return this; + } + + /** + * 注册自定义转换器。 + */ + public ReadBuilder registerConverter(Converter converter) { + if (converter == null) { + return this; + } + if (converters == null) { + converters = new ArrayList<>(); + } + converters.add(converter); + return this; + } + + /** + * 读取Excel并返回对象集合。 + */ + public List doReadSync() { + return createSheetBuilder(createReaderBuilder(null)).doReadSync(); + } + + /** + * 读取Excel并返回解析结果。 + */ + public ExcelResult doRead() { + ExcelListener readListener = listener; + if (readListener == null) { + readListener = new DefaultExcelListener<>(validate, failFast); + } + createSheetBuilder(createReaderBuilder(readListener)).doRead(); + return readListener.getExcelResult(); + } + + /** + * 读取所有工作表并返回解析结果。 + */ + public ExcelResult doReadAll() { + ExcelListener readListener = listener; + if (readListener == null) { + readListener = new DefaultExcelListener<>(validate, failFast); + } + createReaderBuilder(readListener).doReadAll(); + return readListener.getExcelResult(); + } + + /** + * 读取所有工作表并返回对象集合。 + */ + public List doReadAllSync() { + return createReaderBuilder(null).doReadAllSync(); + } + + private ExcelReaderBuilder createReaderBuilder(ExcelListener readListener) { + ExcelReaderBuilder builder = FesodSheet.read(inputStream) + .head(headType) + .autoCloseStream(false); + if (readListener != null) { + builder.registerReadListener(readListener); + } + if (headRowNumber != null) { + builder.headRowNumber(headRowNumber); + } + if (ignoreEmptyRow != null) { + builder.ignoreEmptyRow(ignoreEmptyRow); + } + if (StringUtils.isNotBlank(password)) { + builder.password(password); + } + if (autoTrim != null) { + builder.autoTrim(autoTrim); + } + if (autoStrip != null) { + builder.autoStrip(autoStrip); + } + if (numRows != null) { + builder.numRows(numRows); + } + if (CollUtil.isNotEmpty(converters)) { + converters.forEach(builder::registerConverter); + } + return builder; + } + + private ExcelReaderSheetBuilder createSheetBuilder(ExcelReaderBuilder builder) { + ExcelReaderSheetBuilder sheetBuilder; + if (sheetNo != null && StringUtils.isNotBlank(sheetName)) { + sheetBuilder = builder.sheet(sheetNo, sheetName); + } else if (sheetNo != null) { + sheetBuilder = builder.sheet(sheetNo); + } else if (StringUtils.isNotBlank(sheetName)) { + sheetBuilder = builder.sheet(sheetName); + } else { + sheetBuilder = builder.sheet(); + } + if (numRows != null) { + sheetBuilder.numRows(numRows); + } + return sheetBuilder; + } + } + + /** + * Excel 模板导出构造器。 + */ + public static final class TemplateBuilder { + + private final String templatePath; + + private String filename = DEFAULT_SHEET_NAME; + + private TemplateMode mode; + + private Object data; + + private TemplateBuilder(String templatePath) { + this.templatePath = templatePath; + } + + /** + * 设置下载文件名。 + */ + public TemplateBuilder filename(String filename) { + this.filename = StringUtils.blankToDefault(filename, DEFAULT_SHEET_NAME); + return this; + } + + /** + * 设置单表多数据模板数据,模板格式为 {.属性}。 + */ + public TemplateBuilder data(List data) { + this.mode = TemplateMode.LIST; + this.data = data; + return this; + } + + /** + * 设置多表多数据模板数据,模板格式为 {key.属性}。 + */ + public TemplateBuilder multiList(Map data) { + this.mode = TemplateMode.MULTI_LIST; + this.data = data; + return this; + } + + /** + * 设置多 sheet 模板数据,模板格式为 {key.属性}。 + */ + public TemplateBuilder multiSheet(List> data) { + this.mode = TemplateMode.MULTI_SHEET; + this.data = data; + return this; + } + + /** + * 写入 HTTP 响应。 + */ + public void toResponse(HttpServletResponse response) { + try { + validateData(); + resetResponse(filename, response); + ServletOutputStream os = response.getOutputStream(); + toStream(os); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常", e); + } + } + + /** + * 写入输出流。 + */ + public void toStream(OutputStream outputStream) { + validateData(); + ClassPathResource templateResource = new ClassPathResource(templatePath); + try (InputStream templateStream = templateResource.getStream()) { + ExcelWriter excelWriter = FesodSheet.write(outputStream) + .withTemplate(templateStream) + .autoCloseStream(false) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .build(); + try { + fill(excelWriter); + } finally { + excelWriter.finish(); + } + } catch (IOException e) { + throw new RuntimeException("读取Excel模板异常", e); + } + } + + @SuppressWarnings("unchecked") + private void fill(ExcelWriter excelWriter) { + if (mode == TemplateMode.LIST) { + WriteSheet writeSheet = FesodSheet.writerSheet().build(); + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + for (Object item : (List) data) { + excelWriter.fill(item, fillConfig, writeSheet); + } + return; + } + if (mode == TemplateMode.MULTI_LIST) { + WriteSheet writeSheet = FesodSheet.writerSheet().build(); + for (Map.Entry map : ((Map) data).entrySet()) { + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (map.getValue() instanceof Collection) { + excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); + } else { + excelWriter.fill(map.getValue(), fillConfig, writeSheet); + } + } + return; + } + List> sheetData = (List>) data; + for (int i = 0; i < sheetData.size(); i++) { + WriteSheet writeSheet = FesodSheet.writerSheet(i).build(); + for (Map.Entry map : sheetData.get(i).entrySet()) { + FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); + if (map.getValue() instanceof Collection) { + excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); + } else { + excelWriter.fill(map.getValue(), writeSheet); + } + } + } + } + + private void validateData() { + if (mode == null || data == null) { + throw new IllegalArgumentException("数据为空"); + } + if (data instanceof Collection collection && CollUtil.isEmpty(collection)) { + throw new IllegalArgumentException("数据为空"); + } + if (data instanceof Map map && CollUtil.isEmpty(map)) { + throw new IllegalArgumentException("数据为空"); + } + } + + private enum TemplateMode { + LIST, + MULTI_LIST, + MULTI_SHEET + } + } +} diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java deleted file mode 100644 index 2f586a3d5..000000000 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/utils/ExcelUtil.java +++ /dev/null @@ -1,626 +0,0 @@ -package org.dromara.common.excel.utils; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.io.resource.ClassPathResource; -import cn.hutool.core.util.CharsetUtil; -import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.ZipUtil; -import jakarta.servlet.ServletOutputStream; -import jakarta.servlet.http.HttpServletResponse; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.apache.fesod.sheet.ExcelWriter; -import org.apache.fesod.sheet.FesodSheet; -import org.apache.fesod.sheet.write.builder.ExcelWriterSheetBuilder; -import org.apache.fesod.sheet.write.metadata.WriteSheet; -import org.apache.fesod.sheet.write.metadata.fill.FillConfig; -import org.apache.fesod.sheet.write.metadata.fill.FillWrapper; -import org.apache.fesod.sheet.write.style.column.LongestMatchColumnWidthStyleStrategy; -import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.core.utils.file.FileUtils; -import org.dromara.common.excel.convert.ExcelBigNumberConvert; -import org.dromara.common.excel.core.*; -import org.dromara.common.excel.handler.DataWriteHandler; - -import java.io.*; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.function.Consumer; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; - -/** - * Excel相关处理 - * - * @author Lion Li - */ -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class ExcelUtil { - - /** - * 读取Excel并返回对象集合 - * - * @param is 文件流 - * @param clazz 接收实体类 - * @return 数据列表 - */ - public static List importExcel(InputStream is, Class clazz) { - return FesodSheet.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync(); - } - - /** - * 读取Excel并返回解析结果(带默认校验功能) - * - * @param is 文件流 - * @param clazz 接收实体类 - * @param isValidate 是否开启校验 - * @return 解析结果(含成功数据、错误信息) - */ - public static ExcelResult importExcel(InputStream is, Class clazz, boolean isValidate) { - DefaultExcelListener listener = new DefaultExcelListener<>(isValidate); - FesodSheet.read(is, clazz, listener).sheet().doRead(); - return listener.getExcelResult(); - } - - /** - * 读取Excel并返回解析结果(使用自定义监听器) - * - * @param is 文件流 - * @param clazz 接收实体类 - * @param listener 自定义监听器 - * @return 解析结果(含成功数据、错误信息) - */ - public static ExcelResult importExcel(InputStream is, Class clazz, ExcelListener listener) { - FesodSheet.read(is, clazz, listener).sheet().doRead(); - return listener.getExcelResult(); - } - - /** - * 导出excel - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param clazz 实体类 - * @param response 响应体 - */ - public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { - try { - resetResponse(sheetName, response); - ServletOutputStream os = response.getOutputStream(); - exportExcel(list, sheetName, clazz, false, os, null); - } catch (IOException e) { - throw new RuntimeException("导出Excel异常", e); - } - } - - /** - * 大数据量Excel导出 - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param clazz 实体类 - */ - public static void exportExcelZip(List list, String sheetName, Class clazz, HttpServletResponse response) { - exportExcelZip(list, sheetName, clazz, response, 999); - } - - /** - * 大数据量Excel导出 - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param clazz 实体类 - * @param pageSize 每页条数 - */ - public static void exportExcelZip(List list, String sheetName, Class clazz, HttpServletResponse response, int pageSize) { - // 数据分页 - List> pageList = ListUtil.partition(list, pageSize); - // 只有一页,直接导出普通Excel - if (pageList.size() <= 1) { - exportSingleExcel(list, sheetName, clazz, response); - return; - } - // 多线程生成所有Excel文件(字节数组) - Map excelMap = buildExcelZipData(pageList, sheetName, clazz); - // 写入ZIP并下载 - writeExcelZipResponse(sheetName, response, excelMap); - } - - /** - * 导出【单文件】Excel - */ - private static void exportSingleExcel(List list, String sheetName, Class clazz, HttpServletResponse response) { - try { - resetResponse(sheetName, response); - ServletOutputStream os = response.getOutputStream(); - exportExcel(list, sheetName, clazz, false, os, null); - } catch (IOException e) { - throw new RuntimeException("导出Excel异常", e); - } - } - - /** - * 多线程并行生成多个Excel文件 - * 使用虚拟线程,高并发、低资源占用 - * - * @return Map<文件名, 文件字节数组> - */ - private static Map buildExcelZipData(List> pageList, String sheetName, Class clazz) { - // 有序Map,保证文件按页码顺序打包 - Map excelMap = new LinkedHashMap<>(pageList.size()); - List>> futures = new ArrayList<>(pageList.size()); - - // 使用虚拟线程池执行导出任务 - try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { - // 1. 提交所有分页导出任务 - for (int i = 0; i < pageList.size(); i++) { - int pageNum = i + 1; - List pageData = pageList.get(i); - futures.add(executor.submit(() -> buildExcelZipEntry(pageData, sheetName, clazz, pageNum))); - } - // 2. 获取所有线程执行结果 - for (Future> future : futures) { - Map.Entry excel = getExcelZipEntry(future); - excelMap.put(excel.getKey(), excel.getValue()); - } - } - return excelMap; - } - - /** - * 单页Excel生成任务(线程执行单元) - * - * @param pageData 当前页数据 - * @param pageNum 当前页码 - * @return 文件名 + 文件字节 - */ - private static Map.Entry buildExcelZipEntry(List pageData, String sheetName, Class clazz, int pageNum) { - try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) { - String exportSheetName = sheetName + "_第" + pageNum + "页"; - exportExcel(pageData, exportSheetName, clazz, false, bos, null); - return Map.entry(exportSheetName + ".xlsx", bos.toByteArray()); - } catch (Exception e) { - throw new RuntimeException("第" + pageNum + "页Excel生成失败", e); - } - } - - /** - * 安全获取异步任务结果 - * 处理中断异常、执行异常,保证任务稳定 - */ - private static Map.Entry getExcelZipEntry(Future> future) { - try { - return future.get(); - } catch (InterruptedException e) { - // 恢复中断标志 - Thread.currentThread().interrupt(); - throw new RuntimeException("Excel导出线程被中断", e); - } catch (ExecutionException e) { - throw new RuntimeException("Excel导出失败", e.getCause()); - } - } - - /** - * 将多个Excel文件打包成ZIP并输出到浏览器下载 - * - * @param excelMap 文件名 -> 文件字节 - */ - private static void writeExcelZipResponse(String sheetName, HttpServletResponse response, Map excelMap) { - try { - // 设置ZIP下载响应头 - response.setContentType("application/zip"); - response.setHeader("Content-Disposition", - "attachment;filename*=UTF-8''" + URLEncoder.encode(sheetName, StandardCharsets.UTF_8) + ".zip"); - - // 压缩写入多个Excel文件 - try (ZipOutputStream zipOut = ZipUtil.getZipOutputStream(response.getOutputStream(), CharsetUtil.CHARSET_UTF_8)) { - for (Map.Entry entry : excelMap.entrySet()) { - zipOut.putNextEntry(new ZipEntry(entry.getKey())); - zipOut.write(entry.getValue()); - zipOut.closeEntry(); - } - } - } catch (IOException e) { - throw new RuntimeException("导出Zip异常", e); - } - } - - /** - * 导出excel - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param clazz 实体类 - * @param response 响应体 - * @param options 级联下拉选 - */ - public static void exportExcel(List list, String sheetName, Class clazz, HttpServletResponse response, List options) { - try { - resetResponse(sheetName, response); - ServletOutputStream os = response.getOutputStream(); - exportExcel(list, sheetName, clazz, false, os, options); - } catch (IOException e) { - throw new RuntimeException("导出Excel异常", e); - } - } - - /** - * 导出excel - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param clazz 实体类 - * @param merge 是否合并单元格 - * @param response 响应体 - */ - public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { - try { - resetResponse(sheetName, response); - ServletOutputStream os = response.getOutputStream(); - exportExcel(list, sheetName, clazz, merge, os, null); - } catch (IOException e) { - throw new RuntimeException("导出Excel异常", e); - } - } - - /** - * 导出excel - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param clazz 实体类 - * @param merge 是否合并单元格 - * @param response 响应体 - * @param options 级联下拉选 - */ - public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, HttpServletResponse response, List options) { - try { - resetResponse(sheetName, response); - ServletOutputStream os = response.getOutputStream(); - exportExcel(list, sheetName, clazz, merge, os, options); - } catch (IOException e) { - throw new RuntimeException("导出Excel异常", e); - } - } - - /** - * 导出excel - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param clazz 实体类 - * @param os 输出流 - */ - public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os) { - exportExcel(list, sheetName, clazz, false, os, null); - } - - /** - * 导出excel - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param clazz 实体类 - * @param os 输出流 - * @param options 级联下拉选内容 - */ - public static void exportExcel(List list, String sheetName, Class clazz, OutputStream os, List options) { - exportExcel(list, sheetName, clazz, false, os, options); - } - - /** - * 导出excel - * - * @param list 导出数据集合 - * @param sheetName 工作表的名称 - * @param clazz 实体类 - * @param merge 是否合并单元格 - * @param os 输出流 - */ - public static void exportExcel(List list, String sheetName, Class clazz, boolean merge, - OutputStream os, List options) { - ExcelWriterSheetBuilder builder = FesodSheet.write(os, clazz) - .autoCloseStream(false) - // 自动适配 - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) - // 大数值自动转换 防止失真 - .registerConverter(new ExcelBigNumberConvert()) - .registerWriteHandler(new DataWriteHandler(clazz)) - .sheet(sheetName); - if (merge) { - // 合并处理器 - builder.registerWriteHandler(new CellMergeStrategy(list, true)); - } - // 添加下拉框操作 - builder.registerWriteHandler(new ExcelDownHandler(options)); - builder.doWrite(list); - } - - /** - * 导出excel - * - * @param headType 带Excel注解的类型 - * @param os 输出流 - * @param options Excel下拉可选项 - * @param consumer 导出助手消费函数 - */ - public static void exportExcel(Class headType, OutputStream os, List options, Consumer> consumer) { - try (ExcelWriter writer = FesodSheet.write(os, headType) - .autoCloseStream(false) - // 自动适配 - .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) - // 大数值自动转换 防止失真 - .registerConverter(new ExcelBigNumberConvert()) - // 批注必填项处理 - .registerWriteHandler(new DataWriteHandler(headType)) - // 添加下拉框操作 - .registerWriteHandler(new ExcelDownHandler(options)) - .build()) { - // 执行消费函数 - consumer.accept(ExcelWriterWrapper.of(writer)); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - /** - * 导出excel - * - * @param headType 带Excel注解的类型 - * @param os 输出流 - * @param consumer 导出助手消费函数 - */ - public static void exportExcel(Class headType, OutputStream os, Consumer> consumer) { - exportExcel(headType, os, null, consumer); - } - - /** - * 单表多数据模板导出 模板格式为 {.属性} - * - * @param filename 文件名 - * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 - * 例如: excel/temp.xlsx - * 重点: 模板文件必须放置到启动类对应的 resource 目录下 - * @param data 模板需要的数据 - * @param response 响应体 - */ - public static void exportTemplate(List data, String filename, String templatePath, HttpServletResponse response) { - try { - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } - resetResponse(filename, response); - ServletOutputStream os = response.getOutputStream(); - exportTemplate(data, templatePath, os); - } catch (IOException e) { - throw new RuntimeException("导出Excel异常", e); - } - } - - /** - * 单表多数据模板导出 模板格式为 {.属性} - * - * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 - * 例如: excel/temp.xlsx - * 重点: 模板文件必须放置到启动类对应的 resource 目录下 - * @param data 模板需要的数据 - * @param os 输出流 - */ - public static void exportTemplate(List data, String templatePath, OutputStream os) { - ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = FesodSheet.write(os) - .withTemplate(templateResource.getStream()) - .autoCloseStream(false) - // 大数值自动转换 防止失真 - .registerConverter(new ExcelBigNumberConvert()) - .registerWriteHandler(new DataWriteHandler(data.getFirst().getClass())) - .build(); - try { - WriteSheet writeSheet = FesodSheet.writerSheet().build(); - FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); - // 单表多数据导出 模板格式为 {.属性} - for (T d : data) { - excelWriter.fill(d, fillConfig, writeSheet); - } - } finally { - excelWriter.finish(); - } - } - - /** - * 多表多数据模板导出 模板格式为 {key.属性} - * - * @param filename 文件名 - * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 - * 例如: excel/temp.xlsx - * 重点: 模板文件必须放置到启动类对应的 resource 目录下 - * @param data 模板需要的数据 - * @param response 响应体 - */ - public static void exportTemplateMultiList(Map data, String filename, String templatePath, HttpServletResponse response) { - try { - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } - resetResponse(filename, response); - ServletOutputStream os = response.getOutputStream(); - exportTemplateMultiList(data, templatePath, os); - } catch (IOException e) { - throw new RuntimeException("导出Excel异常", e); - } - } - - /** - * 多sheet模板导出 模板格式为 {key.属性} - * - * @param filename 文件名 - * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 - * 例如: excel/temp.xlsx - * 重点: 模板文件必须放置到启动类对应的 resource 目录下 - * @param data 模板需要的数据 - * @param response 响应体 - */ - public static void exportTemplateMultiSheet(List> data, String filename, String templatePath, HttpServletResponse response) { - try { - if (CollUtil.isEmpty(data)) { - throw new IllegalArgumentException("数据为空"); - } - resetResponse(filename, response); - ServletOutputStream os = response.getOutputStream(); - exportTemplateMultiSheet(data, templatePath, os); - } catch (IOException e) { - throw new RuntimeException("导出Excel异常", e); - } - } - - /** - * 多表多数据模板导出 模板格式为 {key.属性} - * - * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 - * 例如: excel/temp.xlsx - * 重点: 模板文件必须放置到启动类对应的 resource 目录下 - * @param data 模板需要的数据 - * @param os 输出流 - */ - public static void exportTemplateMultiList(Map data, String templatePath, OutputStream os) { - ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = FesodSheet.write(os) - .withTemplate(templateResource.getStream()) - .autoCloseStream(false) - // 大数值自动转换 防止失真 - .registerConverter(new ExcelBigNumberConvert()) - .build(); - try { - WriteSheet writeSheet = FesodSheet.writerSheet().build(); - for (Map.Entry map : data.entrySet()) { - // 设置列表后续还有数据 - FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); - if (map.getValue() instanceof Collection) { - // 多表导出必须使用 FillWrapper - excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); - } else { - excelWriter.fill(map.getValue(), fillConfig, writeSheet); - } - } - } finally { - excelWriter.finish(); - } - } - - /** - * 多sheet模板导出 模板格式为 {key.属性} - * - * @param templatePath 模板路径 resource 目录下的路径包括模板文件名 - * 例如: excel/temp.xlsx - * 重点: 模板文件必须放置到启动类对应的 resource 目录下 - * @param data 模板需要的数据 - * @param os 输出流 - */ - public static void exportTemplateMultiSheet(List> data, String templatePath, OutputStream os) { - ClassPathResource templateResource = new ClassPathResource(templatePath); - ExcelWriter excelWriter = FesodSheet.write(os) - .withTemplate(templateResource.getStream()) - .autoCloseStream(false) - // 大数值自动转换 防止失真 - .registerConverter(new ExcelBigNumberConvert()) - .build(); - try { - for (int i = 0; i < data.size(); i++) { - WriteSheet writeSheet = FesodSheet.writerSheet(i).build(); - for (Map.Entry map : data.get(i).entrySet()) { - // 设置列表后续还有数据 - FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build(); - if (map.getValue() instanceof Collection) { - // 多表导出必须使用 FillWrapper - excelWriter.fill(new FillWrapper(map.getKey(), (Collection) map.getValue()), fillConfig, writeSheet); - } else { - excelWriter.fill(map.getValue(), writeSheet); - } - } - } - } finally { - excelWriter.finish(); - } - } - - /** - * 重置响应体 - */ - private static void resetResponse(String sheetName, HttpServletResponse response) throws UnsupportedEncodingException { - String filename = encodingFilename(sheetName); - FileUtils.setAttachmentResponseHeader(response, filename); - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); - } - - /** - * 解析导出值 0=男,1=女,2=未知 - * - * @param propertyValue 参数值 - * @param converterExp 翻译注解 - * @param separator 分隔符 - * @return 解析后值 - */ - public static String convertByExp(String propertyValue, String converterExp, String separator) { - StringBuilder propertyString = new StringBuilder(); - String[] convertSource = converterExp.split(StringUtils.SEPARATOR); - for (String item : convertSource) { - String[] itemArray = item.split("="); - if (StringUtils.containsAny(propertyValue, separator)) { - for (String value : propertyValue.split(separator)) { - if (itemArray[0].equals(value)) { - propertyString.append(itemArray[1]).append(separator); - break; - } - } - } else { - if (itemArray[0].equals(propertyValue)) { - return itemArray[1]; - } - } - } - return StringUtils.stripEnd(propertyString.toString(), separator); - } - - /** - * 反向解析值 男=0,女=1,未知=2 - * - * @param propertyValue 参数值 - * @param converterExp 翻译注解 - * @param separator 分隔符 - * @return 解析后值 - */ - public static String reverseByExp(String propertyValue, String converterExp, String separator) { - StringBuilder propertyString = new StringBuilder(); - String[] convertSource = converterExp.split(StringUtils.SEPARATOR); - for (String item : convertSource) { - String[] itemArray = item.split("="); - if (StringUtils.containsAny(propertyValue, separator)) { - for (String value : propertyValue.split(separator)) { - if (itemArray[1].equals(value)) { - propertyString.append(itemArray[0]).append(separator); - break; - } - } - } else { - if (itemArray[1].equals(propertyValue)) { - return itemArray[0]; - } - } - } - return StringUtils.stripEnd(propertyString.toString(), separator); - } - - /** - * 编码文件名 - */ - public static String encodingFilename(String filename) { - return IdUtil.fastSimpleUUID() + "_" + filename + ".xlsx"; - } - -} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestDemoController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestDemoController.java index b91c4f80c..cb1ba9680 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestDemoController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestDemoController.java @@ -12,7 +12,7 @@ import org.dromara.common.redis.annotation.RepeatSubmit; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.core.domain.PageResult; import org.dromara.common.excel.core.ExcelResult; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.demo.domain.TestDemo; @@ -74,7 +74,9 @@ public class TestDemoController extends BaseController { @SaCheckPermission("demo:demo:import") @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R importData(@RequestPart("file") MultipartFile file) throws Exception { - ExcelResult excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true); + ExcelResult excelResult = ExcelBuilder.read(file.getInputStream(), TestDemoImportVo.class) + .validate(true) + .doRead(); List list = MapstructUtils.convert(excelResult.getList(), TestDemo.class); testDemoService.saveBatch(list); return R.ok(excelResult.getAnalysis()); @@ -92,7 +94,7 @@ public class TestDemoController extends BaseController { // for (TestDemoVo vo : list) { // vo.setId(1234567891234567893L); // } - ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response); + ExcelBuilder.of(list, TestDemoVo.class).sheetName("测试单表").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestExcelController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestExcelController.java index 8458dc8ba..f727938fc 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestExcelController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestExcelController.java @@ -6,7 +6,7 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.RequiredArgsConstructor; import org.dromara.common.excel.core.ExcelResult; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.demo.domain.vo.ExportDemoVo; import org.dromara.demo.listener.ExportDemoListener; import org.dromara.demo.service.IExportExcelService; @@ -14,7 +14,6 @@ import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; -import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -48,7 +47,10 @@ public class TestExcelController { list.add(new TestObj("单列表测试1", "列表测试1", "列表测试2", "列表测试3", "列表测试4")); list.add(new TestObj("单列表测试2", "列表测试5", "列表测试6", "列表测试7", "列表测试8")); list.add(new TestObj("单列表测试3", "列表测试9", "列表测试10", "列表测试11", "列表测试12")); - ExcelUtil.exportTemplate(CollUtil.newArrayList(map, list), "单列表.xlsx", "excel/单列表.xlsx", response); + ExcelBuilder.template("excel/单列表.xlsx") + .filename("单列表.xlsx") + .data(CollUtil.newArrayList(map, list)) + .toResponse(response); } /** @@ -82,7 +84,10 @@ public class TestExcelController { multiListMap.put("data2", list2); multiListMap.put("data3", list3); multiListMap.put("data4", list4); - ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response); + ExcelBuilder.template("excel/多列表.xlsx") + .filename("多列表.xlsx") + .multiList(multiListMap) + .toResponse(response); } /** @@ -101,7 +106,7 @@ public class TestExcelController { * @param response / */ @GetMapping("/customExport") - public void customExport(HttpServletResponse response) throws IOException { + public void customExport(HttpServletResponse response) { exportExcelService.customExport(response); } @@ -137,7 +142,10 @@ public class TestExcelController { list.add(sheetMap2); list.add(sheetMap3); list.add(sheetMap4); - ExcelUtil.exportTemplateMultiSheet(list, "多sheet列表", "excel/多sheet列表.xlsx", response); + ExcelBuilder.template("excel/多sheet列表.xlsx") + .filename("多sheet列表") + .multiSheet(list) + .toResponse(response); } /** @@ -146,7 +154,9 @@ public class TestExcelController { @PostMapping(value = "/importWithOptions", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public List importWithOptions(@RequestPart("file") MultipartFile file) throws Exception { // 处理解析结果 - ExcelResult excelResult = ExcelUtil.importExcel(file.getInputStream(), ExportDemoVo.class, new ExportDemoListener()); + ExcelResult excelResult = ExcelBuilder.read(file.getInputStream(), ExportDemoVo.class) + .listener(new ExportDemoListener()) + .doRead(); return excelResult.getList(); } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestTreeController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestTreeController.java index 1677abce0..4e61d482c 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestTreeController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/TestTreeController.java @@ -6,7 +6,7 @@ import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; import org.dromara.common.core.validate.QueryGroup; import org.dromara.common.web.core.BaseController; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.redis.annotation.RepeatSubmit; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; @@ -55,7 +55,7 @@ public class TestTreeController extends BaseController { @GetMapping("/export") public void export(@Validated TestTreeBo bo, HttpServletResponse response) { List list = testTreeService.queryList(bo); - ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response); + ExcelBuilder.of(list, TestTreeVo.class).sheetName("测试树表").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/IExportExcelService.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/IExportExcelService.java index ad2392b97..896ad0a1f 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/IExportExcelService.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/IExportExcelService.java @@ -2,8 +2,6 @@ package org.dromara.demo.service; import jakarta.servlet.http.HttpServletResponse; -import java.io.IOException; - /** * 导出下拉框Excel示例 * @@ -23,5 +21,5 @@ public interface IExportExcelService { * * @param response / */ - void customExport(HttpServletResponse response) throws IOException; + void customExport(HttpServletResponse response); } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java index 7163530ed..d8adfd889 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java @@ -8,15 +8,13 @@ import lombok.Data; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.utils.StreamUtils; -import org.dromara.common.core.utils.file.FileUtils; import org.dromara.common.excel.core.DropDownOptions; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.excel.utils.ExcelWriterWrapper; import org.dromara.demo.domain.vo.ExportDemoVo; import org.dromara.demo.service.IExportExcelService; import org.springframework.stereotype.Service; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -103,7 +101,10 @@ public class ExportExcelServiceImpl implements IExportExcelService { return everyRowData; }); - ExcelUtil.exportExcel(outList, "下拉框示例", ExportDemoVo.class, response, options); + ExcelBuilder.of(outList, ExportDemoVo.class) + .sheetName("下拉框示例") + .options(options) + .toResponse(response); } private String buildOptions(List cityDataList, Integer id) { @@ -240,12 +241,8 @@ public class ExportExcelServiceImpl implements IExportExcelService { @Override - public void customExport(HttpServletResponse response) throws IOException { - String filename = ExcelUtil.encodingFilename("自定义导出"); - FileUtils.setAttachmentResponseHeader(response, filename); - response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8"); - - ExcelUtil.exportExcel(ExportDemoVo.class, response.getOutputStream(), wrapper -> { + public void customExport(HttpServletResponse response) { + ExcelBuilder.writer(ExportDemoVo.class).sheetName("自定义导出").toResponse(response, wrapper -> { // 创建表格数据,业务中一般通过数据库查询 List excelDataList = new ArrayList<>(); for (int i = 0; i < 30; i++) { diff --git a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm index 36c54ac03..3ac280c67 100644 --- a/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm +++ b/ruoyi-modules/ruoyi-gen/src/main/resources/vm/java/controller.java.vm @@ -19,7 +19,7 @@ import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; import org.dromara.common.log.enums.BusinessType; #if($enableExport) -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; #end import ${packageName}.domain.vo.${ClassName}Vo; import ${packageName}.domain.bo.${ClassName}Bo; @@ -68,7 +68,7 @@ public class ${ClassName}Controller extends BaseController { @PostMapping("/export") public void export(${ClassName}Bo bo, HttpServletResponse response) { List<${ClassName}Vo> list = ${className}Service.queryList(bo); - ExcelUtil.exportExcel(list, "${functionName}", ${ClassName}Vo.class, response); + ExcelBuilder.of(list, ${ClassName}Vo.class).sheetName("${functionName}").toResponse(response); } #end diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLoginInfoController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLoginInfoController.java index a61e5e607..336b0bec5 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLoginInfoController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysLoginInfoController.java @@ -7,7 +7,7 @@ import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.CacheNames; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; @@ -59,7 +59,7 @@ public class SysLoginInfoController extends BaseController { @PostMapping("/export") public void export(SysLoginInfoBo loginInfo, HttpServletResponse response) { List list = loginInfoService.selectLoginInfoList(loginInfo); - ExcelUtil.exportExcel(list, "登录日志", SysLoginInfoVo.class, response); + ExcelBuilder.of(list, SysLoginInfoVo.class).sheetName("登录日志").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysOperlogController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysOperlogController.java index dc8fd7fdb..8b5974bfd 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysOperlogController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysOperlogController.java @@ -6,7 +6,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; @@ -56,7 +56,7 @@ public class SysOperlogController extends BaseController { @PostMapping("/export") public void export(SysOperLogBo operLog, HttpServletResponse response) { List list = operLogService.selectOperLogList(operLog); - ExcelUtil.exportExcel(list, "操作日志", SysOperLogVo.class, response); + ExcelBuilder.of(list, SysOperLogVo.class).sheetName("操作日志").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java index 7bef26648..4be7e7abd 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysClientController.java @@ -9,7 +9,7 @@ import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; @@ -61,7 +61,7 @@ public class SysClientController extends BaseController { @PostMapping("/export") public void export(SysClientBo bo, HttpServletResponse response) { List list = sysClientService.queryList(bo); - ExcelUtil.exportExcel(list, "客户端管理", SysClientVo.class, response); + ExcelBuilder.of(list, SysClientVo.class).sheetName("客户端管理").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysConfigController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysConfigController.java index 8c6210fa7..12d2a7e09 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysConfigController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysConfigController.java @@ -5,7 +5,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; @@ -57,7 +57,7 @@ public class SysConfigController extends BaseController { @PostMapping("/export") public void export(SysConfigBo config, HttpServletResponse response) { List list = configService.selectConfigList(config); - ExcelUtil.exportExcel(list, "参数数据", SysConfigVo.class, response); + ExcelBuilder.of(list, SysConfigVo.class).sheetName("参数数据").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java index e316477a3..131c1e2e9 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictDataController.java @@ -6,7 +6,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; @@ -61,7 +61,7 @@ public class SysDictDataController extends BaseController { @PostMapping("/export") public void export(SysDictDataBo dictData, HttpServletResponse response) { List list = dictDataService.selectDictDataList(dictData); - ExcelUtil.exportExcel(list, "字典数据", SysDictDataVo.class, response); + ExcelBuilder.of(list, SysDictDataVo.class).sheetName("字典数据").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictTypeController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictTypeController.java index dea026e9f..b18d0ad4d 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictTypeController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysDictTypeController.java @@ -6,7 +6,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; @@ -58,7 +58,7 @@ public class SysDictTypeController extends BaseController { @PostMapping("/export") public void export(SysDictTypeBo dictType, HttpServletResponse response) { List list = dictTypeService.selectDictTypeList(dictType); - ExcelUtil.exportExcel(list, "字典类型", SysDictTypeVo.class, response); + ExcelBuilder.of(list, SysDictTypeVo.class).sheetName("字典类型").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java index 6ab322ae6..2df12437b 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysPostController.java @@ -8,7 +8,7 @@ import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; @@ -64,7 +64,7 @@ public class SysPostController extends BaseController { @PostMapping("/export") public void export(SysPostBo post, HttpServletResponse response) { List list = postService.selectPostList(post); - ExcelUtil.exportExcel(list, "岗位数据", SysPostVo.class, response); + ExcelBuilder.of(list, SysPostVo.class).sheetName("岗位数据").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java index 3762006d9..7885fa91e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java @@ -6,7 +6,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; @@ -66,7 +66,7 @@ public class SysRoleController extends BaseController { @PostMapping("/export") public void export(SysRoleBo role, HttpServletResponse response) { List list = roleService.selectRoleList(role); - ExcelUtil.exportExcel(list, "角色数据", SysRoleVo.class, response); + ExcelBuilder.of(list, SysRoleVo.class).sheetName("角色数据").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java index e0c741c32..e55390194 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysUserController.java @@ -16,7 +16,7 @@ import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.encrypt.annotation.ApiEncrypt; import org.dromara.common.excel.core.ExcelResult; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; @@ -84,7 +84,7 @@ public class SysUserController extends BaseController { @PostMapping("/export") public void export(SysUserBo user, HttpServletResponse response) { List list = userService.selectUserExportList(user); - ExcelUtil.exportExcel(list, "用户数据", SysUserExportVo.class, response); + ExcelBuilder.of(list, SysUserExportVo.class).sheetName("用户数据").toResponse(response); } /** @@ -97,7 +97,9 @@ public class SysUserController extends BaseController { @SaCheckPermission("system:user:import") @PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) public R importData(@RequestPart("file") MultipartFile file, boolean updateSupport) throws Exception { - ExcelResult result = ExcelUtil.importExcel(file.getInputStream(), SysUserImportVo.class, new SysUserImportListener(updateSupport)); + ExcelResult result = ExcelBuilder.read(file.getInputStream(), SysUserImportVo.class) + .listener(new SysUserImportListener(updateSupport)) + .doRead(); return R.ok(result.getAnalysis()); } @@ -108,7 +110,7 @@ public class SysUserController extends BaseController { */ @PostMapping("/importTemplate") public void importTemplate(HttpServletResponse response) { - ExcelUtil.exportExcel(new ArrayList<>(), "用户数据", SysUserImportVo.class, response); + ExcelBuilder.of(new ArrayList<>(), SysUserImportVo.class).sheetName("用户数据").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java index ad1387a32..4c04e0c3e 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/FlwCategoryController.java @@ -8,7 +8,7 @@ import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.redis.annotation.RepeatSubmit; @@ -61,7 +61,7 @@ public class FlwCategoryController extends BaseController { @PostMapping("/export") public void export(FlowCategoryBo bo, HttpServletResponse response) { List list = flwCategoryService.queryList(bo); - ExcelUtil.exportExcel(list, "流程分类", FlowCategoryVo.class, response); + ExcelBuilder.of(list, FlowCategoryVo.class).sheetName("流程分类").toResponse(response); } /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java index d285d232c..3b6cb6a23 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/TestLeaveController.java @@ -9,7 +9,7 @@ import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; import org.dromara.common.core.validate.AddGroup; import org.dromara.common.core.validate.EditGroup; -import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.mybatis.core.page.PageQuery; @@ -64,7 +64,7 @@ public class TestLeaveController extends BaseController { @PostMapping("/export") public void export(TestLeaveBo bo, HttpServletResponse response) { List list = testLeaveService.queryList(bo); - ExcelUtil.exportExcel(list, "请假", TestLeaveVo.class, response); + ExcelBuilder.of(list, TestLeaveVo.class).sheetName("请假").toResponse(response); } /** From 2af4b2c7a32e2cec645249792af76e5d692d338a 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: Sat, 16 May 2026 14:08:52 +0800 Subject: [PATCH 18/35] =?UTF-8?q?refactor(common-json):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20JSON=20=E5=A2=9E=E5=BC=BA=E5=A4=84=E7=90=86?= =?UTF-8?q?=E9=93=BE=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 抽取 JSON 字符串校验的重复解析逻辑 - 为 JsonFieldProcessor 增加 supports 判断,减少无关字段处理 - 优化 JsonValueEnhancer,未命中增强字段时直接返回原对象 - 拆分响应增强渲染逻辑,提升代码可读性 - 增强上下文属性管理能力 - 优化翻译、脱敏处理器的字段匹配逻辑 - 修正 Date 反序列化空字符串处理 - 支持 LocalDateTime 秒级、毫秒级及负时间戳反序列化 --- .../json/enhance/JsonEnhancementContext.java | 38 +++++++ .../json/enhance/JsonFieldProcessor.java | 8 ++ .../json/enhance/JsonValueEnhancer.java | 103 ++++++++++++------ .../json/handler/CustomDateDeserializer.java | 6 +- .../CustomLocalDateTimeDeserializer.java | 36 +++++- .../dromara/common/json/utils/JsonUtils.java | 34 +++--- .../handler/SensitiveJsonFieldProcessor.java | 5 + .../TranslationJsonFieldProcessor.java | 18 ++- 8 files changed, 180 insertions(+), 68 deletions(-) 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 64005fc8d..adc6fbee1 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 @@ -5,6 +5,7 @@ import tools.jackson.databind.json.JsonMapper; import java.util.LinkedHashMap; import java.util.Map; +import java.util.function.Supplier; /** * 单次响应增强上下文。 @@ -16,6 +17,8 @@ public class JsonEnhancementContext { private final Map attributes = new LinkedHashMap<>(); + private boolean processingRequired; + /** * 构造响应增强上下文。 * @@ -37,6 +40,20 @@ public class JsonEnhancementContext { return (T) attributes.get(key); } + /** + * 获取上下文属性,不存在时创建并写入。 + * + * @param key 属性键 + * @param supplier 属性值创建器 + * @param 属性值类型 + * @return 属性值 + */ + @SuppressWarnings("unchecked") + public T getOrCreateAttribute(String key, Supplier supplier) { + Object value = attributes.computeIfAbsent(key, ignored -> supplier.get()); + return (T) value; + } + /** * 设置上下文属性。 * @@ -47,4 +64,25 @@ public class JsonEnhancementContext { attributes.put(key, value); } + /** + * 判断上下文是否包含指定属性。 + */ + public boolean containsAttribute(String key) { + return attributes.containsKey(key); + } + + /** + * 移除上下文属性。 + */ + public void removeAttribute(String key) { + attributes.remove(key); + } + + /** + * 标记本次响应存在需要处理的字段。 + */ + public void markProcessingRequired() { + this.processingRequired = true; + } + } diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldProcessor.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldProcessor.java index 9613023ab..ad165908d 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldProcessor.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/enhance/JsonFieldProcessor.java @@ -20,6 +20,14 @@ package org.dromara.common.json.enhance; */ public interface JsonFieldProcessor { + /** + * 判断当前处理器是否需要处理该字段。 + * 默认返回 true 以兼容无注解驱动的自定义处理器。 + */ + default boolean supports(JsonFieldContext fieldContext) { + return true; + } + /** * 收集阶段:扫描字段,将需要处理的 key 存入 context。 * 每个字段调用一次,整个对象树扫描完成后才会进入 prepare 阶段。 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 68dd8da22..62c93975f 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 @@ -54,7 +54,13 @@ public class JsonValueEnhancer { if (body == null || body instanceof JsonNode || processors.isEmpty()) { return body; } - return enhanceTree(body); + JsonEnhancementContext context = new JsonEnhancementContext(jsonMapper); + collectValue(body, context, new IdentityHashMap<>()); + if (!context.isProcessingRequired()) { + return body; + } + processors.forEach(processor -> processor.prepare(context)); + return renderValue(body, context, new IdentityHashMap<>()); } /** @@ -73,6 +79,9 @@ public class JsonValueEnhancer { private JsonNode enhanceTree(Object value) { JsonEnhancementContext context = new JsonEnhancementContext(jsonMapper); collectValue(value, context, new IdentityHashMap<>()); + if (!context.isProcessingRequired()) { + return jsonMapper.valueToTree(value); + } processors.forEach(processor -> processor.prepare(context)); return renderValue(value, context, new IdentityHashMap<>()); } @@ -103,7 +112,7 @@ public class JsonValueEnhancer { for (PropertyMetadata metadata : getProperties(value.getClass())) { Object propertyValue = metadata.getValue(value); JsonFieldContext fieldContext = new JsonFieldContext(value, metadata.propertyName(), metadata.member(), propertyValue); - processors.forEach(processor -> processor.collect(fieldContext, context)); + collectField(fieldContext, context); collectValue(propertyValue, context, visited); } } finally { @@ -111,6 +120,15 @@ public class JsonValueEnhancer { } } + private void collectField(JsonFieldContext fieldContext, JsonEnhancementContext context) { + for (JsonFieldProcessor processor : processors) { + if (processor.supports(fieldContext)) { + context.markProcessingRequired(); + processor.collect(fieldContext, context); + } + } + } + private JsonNode renderValue(Object value, JsonEnhancementContext context, IdentityHashMap visited) { switch (value) { case null -> { @@ -120,27 +138,16 @@ public class JsonValueEnhancer { return jsonNode; } case Map map -> { - ObjectNode objectNode = jsonMapper.createObjectNode(); - map.forEach((key, childValue) -> objectNode.set(String.valueOf(key), renderValue(childValue, context, visited))); - return objectNode; + return renderMap(map, context, visited); } case Iterable iterable -> { - ArrayNode arrayNode = jsonMapper.createArrayNode(); - for (Object child : iterable) { - arrayNode.add(renderValue(child, context, visited)); - } - return arrayNode; + return renderIterable(iterable, context, visited); } default -> { } } if (value.getClass().isArray()) { - ArrayNode arrayNode = jsonMapper.createArrayNode(); - int length = Array.getLength(value); - for (int i = 0; i < length; i++) { - arrayNode.add(renderValue(Array.get(value, i), context, visited)); - } - return arrayNode; + return renderArray(value, context, visited); } if (isSimpleValue(value.getClass())) { return jsonMapper.valueToTree(value); @@ -149,28 +156,58 @@ public class JsonValueEnhancer { return jsonMapper.valueToTree(value); } try { - ObjectNode objectNode = jsonMapper.createObjectNode(); - for (PropertyMetadata metadata : getProperties(value.getClass())) { - Object originalValue = metadata.getValue(value); - JsonFieldContext fieldContext = new JsonFieldContext(value, metadata.propertyName(), metadata.member(), originalValue); - Object processedValue = originalValue; - boolean changed = false; - for (JsonFieldProcessor processor : processors) { - Object nextValue = processor.process(fieldContext, processedValue, context); - changed = changed || !Objects.equals(processedValue, nextValue); - processedValue = nextValue; - } - JsonNode childNode = changed - ? enhanceTranslatedValue(processedValue, context, visited) - : renderValue(processedValue, context, visited); - objectNode.set(metadata.propertyName(), childNode); - } - return objectNode; + return renderPojo(value, context, visited); } finally { visited.remove(value); } } + private ObjectNode renderMap(Map map, JsonEnhancementContext context, IdentityHashMap visited) { + ObjectNode objectNode = jsonMapper.createObjectNode(); + map.forEach((key, childValue) -> objectNode.set(String.valueOf(key), renderValue(childValue, context, visited))); + return objectNode; + } + + private ArrayNode renderIterable(Iterable iterable, JsonEnhancementContext context, IdentityHashMap visited) { + ArrayNode arrayNode = jsonMapper.createArrayNode(); + for (Object child : iterable) { + arrayNode.add(renderValue(child, context, visited)); + } + return arrayNode; + } + + private ArrayNode renderArray(Object value, JsonEnhancementContext context, IdentityHashMap visited) { + ArrayNode arrayNode = jsonMapper.createArrayNode(); + int length = Array.getLength(value); + for (int i = 0; i < length; i++) { + arrayNode.add(renderValue(Array.get(value, i), context, visited)); + } + return arrayNode; + } + + private ObjectNode renderPojo(Object value, JsonEnhancementContext context, IdentityHashMap visited) { + ObjectNode objectNode = jsonMapper.createObjectNode(); + for (PropertyMetadata metadata : getProperties(value.getClass())) { + Object originalValue = metadata.getValue(value); + JsonFieldContext fieldContext = new JsonFieldContext(value, metadata.propertyName(), metadata.member(), originalValue); + Object processedValue = originalValue; + boolean changed = false; + for (JsonFieldProcessor processor : processors) { + if (!processor.supports(fieldContext)) { + continue; + } + Object nextValue = processor.process(fieldContext, processedValue, context); + changed = changed || !Objects.equals(processedValue, nextValue); + processedValue = nextValue; + } + JsonNode childNode = changed + ? enhanceTranslatedValue(processedValue, context, visited) + : renderValue(processedValue, context, visited); + objectNode.set(metadata.propertyName(), childNode); + } + return objectNode; + } + private JsonNode enhanceTranslatedValue(Object value, JsonEnhancementContext context, IdentityHashMap visited) { if (value == null || value instanceof JsonNode || isSimpleValue(value.getClass())) { return renderValue(value, context, visited); diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java index 7e8a61522..65a35063f 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomDateDeserializer.java @@ -2,7 +2,6 @@ package org.dromara.common.json.handler; import cn.hutool.core.date.DateTime; import cn.hutool.core.date.DateUtil; -import org.dromara.common.core.utils.ObjectUtils; import tools.jackson.core.JsonParser; import tools.jackson.databind.DeserializationContext; import tools.jackson.databind.ValueDeserializer; @@ -25,10 +24,11 @@ public class CustomDateDeserializer extends ValueDeserializer { */ @Override public Date deserialize(JsonParser p, DeserializationContext ctxt) { - DateTime parse = DateUtil.parse(p.getString()); - if (ObjectUtils.isNull(parse)) { + String text = p.getString(); + if (text == null || text.isBlank()) { return null; } + DateTime parse = DateUtil.parse(text.trim()); return parse.toJdkDate(); } diff --git a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomLocalDateTimeDeserializer.java b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomLocalDateTimeDeserializer.java index aa327cf92..b3863d5e2 100644 --- a/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomLocalDateTimeDeserializer.java +++ b/ruoyi-common/ruoyi-common-json/src/main/java/org/dromara/common/json/handler/CustomLocalDateTimeDeserializer.java @@ -19,6 +19,10 @@ import java.util.List; */ public class CustomLocalDateTimeDeserializer extends ValueDeserializer { + private static final int SECOND_TIMESTAMP_LENGTH = 10; + + private static final int MILLIS_TIMESTAMP_LENGTH = 13; + /** 支持时间的格式列表(直接解析为 LocalDateTime) */ private static final List DATETIME_FORMATTERS = List.of( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"), @@ -52,11 +56,10 @@ public class CustomLocalDateTimeDeserializer extends ValueDeserializer> getOrCreateBatches(JsonEnhancementContext context) { - Map> batches = context.getAttribute(ATTR_BATCHES); - if (batches == null) { - batches = new LinkedHashMap<>(); - context.setAttribute(ATTR_BATCHES, batches); - } - return batches; + return context.getOrCreateAttribute(ATTR_BATCHES, LinkedHashMap::new); } /** From f4b4c710c5af28c70d2113304b36083fe9c89e01 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: Sat, 16 May 2026 14:15:44 +0800 Subject: [PATCH 19/35] =?UTF-8?q?refactor(common-log):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=93=8D=E4=BD=9C=E6=97=A5=E5=BF=97=E5=88=87=E9=9D=A2?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 改为 @Around 统一处理耗时、返回值和异常 - 修复集合和 Map 参数过滤不完整的问题 - 抽取日志长度常量,减少魔法数字 - 拆分参数序列化逻辑,提升可读性 - 异常日志改为输出完整堆栈 - 参数日志支持 Collection 统一处理 --- .../dromara/common/log/aspect/LogAspect.java | 195 +++++++++++------- 1 file changed, 122 insertions(+), 73 deletions(-) diff --git a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java index 580a5a6ca..186cea20b 100644 --- a/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java +++ b/ruoyi-common/ruoyi-common-log/src/main/java/org/dromara/common/log/aspect/LogAspect.java @@ -9,10 +9,9 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.time.StopWatch; import org.aspectj.lang.JoinPoint; -import org.aspectj.lang.annotation.AfterReturning; -import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; -import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.SpringUtils; @@ -28,6 +27,7 @@ import org.springframework.http.HttpMethod; import org.springframework.validation.BindingResult; import org.springframework.web.multipart.MultipartFile; +import java.lang.reflect.Array; import java.util.*; /** @@ -41,45 +41,40 @@ import java.util.*; public class LogAspect { /** - * 计时 key + * URL 最大记录长度 */ - private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); + private static final int MAX_URL_LENGTH = 255; /** - * 在目标方法执行前启动耗时统计。 + * 客户端标识最大记录长度 + */ + private static final int MAX_CLIENT_KEY_LENGTH = 32; + + /** + * 日志内容最大记录长度 + */ + private static final int MAX_CONTENT_LENGTH = 3800; + + /** + * 执行目标方法并记录操作日志。 * * @param joinPoint 切点 * @param controllerLog 日志注解 + * @return 目标方法返回值 + * @throws Throwable 目标方法异常 */ - @Before(value = "@annotation(controllerLog)") - public void doBefore(JoinPoint joinPoint, Log controllerLog) { + @Around(value = "@annotation(controllerLog)") + public Object doAround(ProceedingJoinPoint joinPoint, Log controllerLog) throws Throwable { StopWatch stopWatch = new StopWatch(); - KEY_CACHE.set(stopWatch); stopWatch.start(); - } - - /** - * 在目标方法正常返回后记录操作日志。 - * - * @param joinPoint 切点 - * @param controllerLog 日志注解 - * @param jsonResult 返回结果 - */ - @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult") - public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) { - handleLog(joinPoint, controllerLog, null, jsonResult); - } - - /** - * 在目标方法抛出异常后记录操作日志。 - * - * @param joinPoint 切点 - * @param controllerLog 日志注解 - * @param e 异常 - */ - @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e") - public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) { - handleLog(joinPoint, controllerLog, e, null); + try { + Object jsonResult = joinPoint.proceed(); + handleLog(joinPoint, controllerLog, null, jsonResult, stopWatch); + return jsonResult; + } catch (Exception e) { + handleLog(joinPoint, controllerLog, e, null, stopWatch); + throw e; + } } /** @@ -89,8 +84,9 @@ public class LogAspect { * @param controllerLog 日志注解 * @param e 异常信息 * @param jsonResult 返回结果 + * @param stopWatch 耗时统计 */ - protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult) { + protected void handleLog(final JoinPoint joinPoint, Log controllerLog, final Exception e, Object jsonResult, StopWatch stopWatch) { try { // *========数据库日志=========*// @@ -100,8 +96,8 @@ public class LogAspect { // 请求的地址 String ip = ServletUtils.getClientIP(); operLog.setOperIp(ip); - operLog.setOperUrl(StringUtils.substring(request.getRequestURI(), 0, 255)); - operLog.setClientKey(StringUtils.substring(request.getHeader(LoginHelper.CLIENT_KEY), 0, 32)); + operLog.setOperUrl(limit(request.getRequestURI(), MAX_URL_LENGTH)); + operLog.setClientKey(limit(request.getHeader(LoginHelper.CLIENT_KEY), MAX_CLIENT_KEY_LENGTH)); LoginUser loginUser = LoginHelper.getLoginUser(); if (ObjectUtil.isNotNull(loginUser)) { operLog.setOperName(loginUser.getUsername()); @@ -118,7 +114,7 @@ public class LogAspect { if (e != null) { operLog.setStatus(BusinessStatus.FAIL.ordinal()); - operLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 3800)); + operLog.setErrorMsg(limit(e.getMessage(), MAX_CONTENT_LENGTH)); } // 设置方法名称 String className = joinPoint.getTarget().getClass().getName(); @@ -129,16 +125,13 @@ public class LogAspect { // 处理设置注解上的参数 getControllerMethodDescription(joinPoint, controllerLog, operLog, jsonResult); // 设置消耗时间 - StopWatch stopWatch = KEY_CACHE.get(); stopWatch.stop(); operLog.setCostTime(stopWatch.getDuration().toMillis()); // 发布事件保存数据库 SpringUtils.context().publishEvent(operLog); } catch (Exception exp) { // 记录本地异常日志 - log.error("异常信息:{}", exp.getMessage()); - } finally { - KEY_CACHE.remove(); + log.error("记录操作日志异常", exp); } } @@ -165,7 +158,7 @@ public class LogAspect { } // 是否需要保存response,参数和值 if (log.isSaveResponseData() && ObjectUtil.isNotNull(jsonResult)) { - operLog.setJsonResult(StringUtils.substring(JsonUtils.toJsonString(jsonResult), 0, 3800)); + operLog.setJsonResult(limit(JsonUtils.toJsonString(jsonResult), MAX_CONTENT_LENGTH)); } } @@ -182,11 +175,11 @@ public class LogAspect { String requestMethod = operLog.getRequestMethod(); if (MapUtil.isEmpty(paramsMap) && StringUtils.equalsAny(requestMethod, HttpMethod.PUT.name(), HttpMethod.POST.name(), HttpMethod.DELETE.name())) { String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames); - operLog.setOperParam(StringUtils.substring(params, 0, 3800)); + operLog.setOperParam(limit(params, MAX_CONTENT_LENGTH)); } else { MapUtil.removeAny(paramsMap, SystemConstants.EXCLUDE_PROPERTIES); MapUtil.removeAny(paramsMap, excludeParamNames); - operLog.setOperParam(StringUtils.substring(JsonUtils.toJsonString(paramsMap), 0, 3800)); + operLog.setOperParam(limit(JsonUtils.toJsonString(paramsMap), MAX_CONTENT_LENGTH)); } } @@ -205,55 +198,111 @@ public class LogAspect { String[] exclude = ArrayUtil.addAll(excludeParamNames, SystemConstants.EXCLUDE_PROPERTIES); for (Object o : paramsArray) { if (ObjectUtil.isNotNull(o) && !isFilterObject(o)) { - String str = ""; - if (o instanceof List list) { - List list1 = new ArrayList<>(); - for (Object obj : list) { - String str1 = JsonUtils.toJsonString(obj); - Dict dict = JsonUtils.parseMap(str1); - if (MapUtil.isNotEmpty(dict)) { - MapUtil.removeAny(dict, exclude); - list1.add(dict); - } - } - str = JsonUtils.toJsonString(list1); - } else { - str = JsonUtils.toJsonString(o); - Dict dict = JsonUtils.parseMap(str); - if (MapUtil.isNotEmpty(dict)) { - MapUtil.removeAny(dict, exclude); - str = JsonUtils.toJsonString(dict); - } - } - params.add(str); + params.add(serializeArg(o, exclude)); } } return params.toString(); } + /** + * 序列化单个方法参数,并移除排除字段。 + * + * @param arg 参数对象 + * @param exclude 排除字段名 + * @return 参数日志字符串 + */ + private String serializeArg(Object arg, String[] exclude) { + if (arg instanceof Collection collection) { + List list = new ArrayList<>(collection.size()); + for (Object item : collection) { + Dict dict = toFilteredDict(item, exclude); + if (MapUtil.isNotEmpty(dict)) { + list.add(dict); + } + } + return JsonUtils.toJsonString(list); + } + String str = JsonUtils.toJsonString(arg); + Dict dict = JsonUtils.parseMap(str); + if (MapUtil.isNotEmpty(dict)) { + MapUtil.removeAny(dict, exclude); + return JsonUtils.toJsonString(dict); + } + return str; + } + + /** + * 将参数转为已排除指定字段的字典。 + * + * @param value 参数值 + * @param exclude 排除字段名 + * @return 已过滤字段的字典 + */ + private Dict toFilteredDict(Object value, String[] exclude) { + String str = JsonUtils.toJsonString(value); + Dict dict = JsonUtils.parseMap(str); + if (MapUtil.isNotEmpty(dict)) { + MapUtil.removeAny(dict, exclude); + } + return dict; + } + + /** + * 限制日志字段长度。 + * + * @param value 原始字符串 + * @param maxLength 最大长度 + * @return 截断后的字符串 + */ + private String limit(String value, int maxLength) { + return StringUtils.substring(value, 0, maxLength); + } + /** * 判断是否需要过滤的对象。 * * @param o 对象信息。 * @return 如果是需要过滤的对象,则返回true;否则返回false。 */ - @SuppressWarnings("rawtypes") public boolean isFilterObject(final Object o) { Class clazz = o.getClass(); if (clazz.isArray()) { - return MultipartFile.class.isAssignableFrom(clazz.getComponentType()); + if (MultipartFile.class.isAssignableFrom(clazz.getComponentType())) { + return true; + } + int length = Array.getLength(o); + for (int i = 0; i < length; i++) { + if (isFilterValue(Array.get(o, i))) { + return true; + } + } + return false; } else if (Collection.class.isAssignableFrom(clazz)) { - Collection collection = (Collection) o; + Collection collection = (Collection) o; for (Object value : collection) { - return value instanceof MultipartFile; + if (isFilterValue(value)) { + return true; + } } } else if (Map.class.isAssignableFrom(clazz)) { - Map map = (Map) o; + Map map = (Map) o; for (Object value : map.values()) { - return value instanceof MultipartFile; + if (isFilterValue(value)) { + return true; + } } } - return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse - || o instanceof BindingResult; + return isFilterValue(o); + } + + /** + * 判断是否为日志参数过滤类型。 + * + * @param value 参数值 + * @return true 需要过滤 false 不需要过滤 + */ + private boolean isFilterValue(Object value) { + return value instanceof MultipartFile || value instanceof HttpServletRequest || value instanceof HttpServletResponse + || value instanceof BindingResult; } } From 99db6dceea69a0959d447569d95e4cda1b1c29e6 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: Sat, 16 May 2026 14:25:34 +0800 Subject: [PATCH 20/35] =?UTF-8?q?refactor(common-mail):=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20MailBuilder=20=E6=9B=BF=E4=BB=A3=20MailUtils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增链式 MailBuilder,统一邮件发送入口 - 删除 MailUtils,业务代码全部切换为 builder 调用 - 默认邮件账户改为发送时获取,避免类加载阶段依赖 Bean - 自定义发件账号时复制默认配置,避免污染全局 MailAccount - 增加收件人、标题和正文参数校验 - 调整内联图片流关闭时机,发送结束后统一关闭 - 将 MailAccount 构建逻辑下沉到 MailProperties --- .../web/controller/CaptchaController.java | 8 +- .../common/mail/config/MailConfig.java | 14 +- .../config/properties/MailProperties.java | 22 + .../dromara/common/mail/core/MailBuilder.java | 347 +++++++++++++ .../dromara/common/mail/utils/MailUtils.java | 473 ------------------ .../demo/controller/MailSendController.java | 8 +- .../service/impl/FlwCommonServiceImpl.java | 4 +- 7 files changed, 382 insertions(+), 494 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/core/MailBuilder.java delete mode 100644 ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java index 472217a6f..f1e5c32bb 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java @@ -17,7 +17,7 @@ import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.regex.RegexValidator; import org.dromara.common.mail.config.properties.MailProperties; -import org.dromara.common.mail.utils.MailUtils; +import org.dromara.common.mail.core.MailBuilder; import org.dromara.common.redis.annotation.RateLimiter; import org.dromara.common.redis.enums.LimitType; import org.dromara.common.redis.utils.RedisUtils; @@ -109,7 +109,11 @@ public class CaptchaController { String code = RandomUtil.randomNumbers(4); RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION)); try { - MailUtils.sendText(email, "登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。"); + MailBuilder.of() + .to(email) + .subject("登录验证码") + .text("您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。") + .send(); } catch (Exception e) { log.error("验证码短信发送异常 => {}", e.getMessage()); throw new ServiceException(e.getMessage()); diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java index 0ea3007b9..a0042dddd 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java @@ -19,19 +19,7 @@ public class MailConfig { @Bean @ConditionalOnProperty(value = "mail.enabled", havingValue = "true") public MailAccount mailAccount(MailProperties mailProperties) { - MailAccount account = new MailAccount(); - account.setHost(mailProperties.getHost()); - account.setPort(mailProperties.getPort()); - account.setAuth(mailProperties.getAuth()); - account.setFrom(mailProperties.getFrom()); - account.setUser(mailProperties.getUser()); - account.setPass(mailProperties.getPass()); - account.setSocketFactoryPort(mailProperties.getPort()); - account.setStarttlsEnable(mailProperties.getStarttlsEnable()); - account.setSslEnable(mailProperties.getSslEnable()); - account.setTimeout(mailProperties.getTimeout()); - account.setConnectionTimeout(mailProperties.getConnectionTimeout()); - return account; + return mailProperties.toMailAccount(); } } diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java index e44aa3da3..8347effb0 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/properties/MailProperties.java @@ -1,5 +1,6 @@ package org.dromara.common.mail.config.properties; +import cn.hutool.extra.mail.MailAccount; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -72,4 +73,25 @@ public class MailProperties { * Socket连接超时值,单位毫秒,缺省值不超时 */ private Long connectionTimeout; + + /** + * 转换为 Hutool 邮件账户配置。 + * + * @return 邮件账户配置 + */ + public MailAccount toMailAccount() { + MailAccount account = new MailAccount(); + account.setHost(host); + account.setPort(port); + account.setAuth(auth); + account.setFrom(from); + account.setUser(user); + account.setPass(pass); + account.setSocketFactoryPort(port); + account.setStarttlsEnable(starttlsEnable); + account.setSslEnable(sslEnable); + account.setTimeout(timeout); + account.setConnectionTimeout(connectionTimeout); + return account; + } } diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/core/MailBuilder.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/core/MailBuilder.java new file mode 100644 index 000000000..2cd98de7b --- /dev/null +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/core/MailBuilder.java @@ -0,0 +1,347 @@ +package org.dromara.common.mail.core; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.mail.JakartaMail; +import cn.hutool.extra.mail.MailAccount; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; + +import java.io.File; +import java.io.InputStream; +import java.util.*; + +/** + * 邮件发送构建器。 + */ +public final class MailBuilder { + + private MailAccount mailAccount; + + private boolean useGlobalSession = true; + + private final List tos = new ArrayList<>(); + + private final List ccs = new ArrayList<>(); + + private final List bccs = new ArrayList<>(); + + private final Map images = new LinkedHashMap<>(); + + private File[] files = new File[0]; + + private String from; + + private String user; + + private String pass; + + private String subject; + + private String content; + + private boolean html; + + private MailBuilder() { + } + + /** + * 创建邮件构建器,默认使用配置文件中的邮件账户。 + * + * @return 邮件构建器 + */ + public static MailBuilder of() { + return new MailBuilder(); + } + + /** + * 创建邮件构建器,使用指定邮件账户。 + * + * @param mailAccount 邮件账户 + * @return 邮件构建器 + */ + public static MailBuilder of(MailAccount mailAccount) { + return new MailBuilder().account(mailAccount); + } + + /** + * 设置自定义邮件账户。 + * + * @param mailAccount 邮件账户 + * @return 当前构建器 + */ + public MailBuilder account(MailAccount mailAccount) { + this.mailAccount = Objects.requireNonNull(mailAccount, "mailAccount must not be null"); + this.useGlobalSession = false; + return this; + } + + /** + * 覆盖发件人。 + * + * @param from 发件人 + * @return 当前构建器 + */ + public MailBuilder from(String from) { + this.from = from; + this.useGlobalSession = false; + return this; + } + + /** + * 覆盖登录用户名。 + * + * @param user 用户名 + * @return 当前构建器 + */ + public MailBuilder user(String user) { + this.user = user; + this.useGlobalSession = false; + return this; + } + + /** + * 覆盖登录密码或授权码。 + * + * @param pass 密码或授权码 + * @return 当前构建器 + */ + public MailBuilder pass(String pass) { + this.pass = pass; + this.useGlobalSession = false; + return this; + } + + /** + * 添加收件人,多个邮箱可使用逗号或分号分隔。 + * + * @param addresses 收件人 + * @return 当前构建器 + */ + public MailBuilder to(String addresses) { + this.tos.addAll(splitAddress(addresses)); + return this; + } + + /** + * 添加收件人。 + * + * @param addresses 收件人集合 + * @return 当前构建器 + */ + public MailBuilder to(Collection addresses) { + this.tos.addAll(normalizeAddresses(addresses)); + return this; + } + + /** + * 添加抄送人,多个邮箱可使用逗号或分号分隔。 + * + * @param addresses 抄送人 + * @return 当前构建器 + */ + public MailBuilder cc(String addresses) { + this.ccs.addAll(splitAddress(addresses)); + return this; + } + + /** + * 添加抄送人。 + * + * @param addresses 抄送人集合 + * @return 当前构建器 + */ + public MailBuilder cc(Collection addresses) { + this.ccs.addAll(normalizeAddresses(addresses)); + return this; + } + + /** + * 添加密送人,多个邮箱可使用逗号或分号分隔。 + * + * @param addresses 密送人 + * @return 当前构建器 + */ + public MailBuilder bcc(String addresses) { + this.bccs.addAll(splitAddress(addresses)); + return this; + } + + /** + * 添加密送人。 + * + * @param addresses 密送人集合 + * @return 当前构建器 + */ + public MailBuilder bcc(Collection addresses) { + this.bccs.addAll(normalizeAddresses(addresses)); + return this; + } + + /** + * 设置邮件标题。 + * + * @param subject 标题 + * @return 当前构建器 + */ + public MailBuilder subject(String subject) { + this.subject = subject; + return this; + } + + /** + * 设置文本正文。 + * + * @param content 正文 + * @return 当前构建器 + */ + public MailBuilder text(String content) { + this.content = content; + this.html = false; + return this; + } + + /** + * 设置 HTML 正文。 + * + * @param content 正文 + * @return 当前构建器 + */ + public MailBuilder html(String content) { + this.content = content; + this.html = true; + return this; + } + + /** + * 设置正文。 + * + * @param content 正文 + * @param html 是否 HTML + * @return 当前构建器 + */ + public MailBuilder content(String content, boolean html) { + this.content = content; + this.html = html; + return this; + } + + /** + * 添加内联图片。 + * + * @param cid 图片 cid + * @param inputStream 图片输入流 + * @return 当前构建器 + */ + public MailBuilder image(String cid, InputStream inputStream) { + if (StrUtil.isNotBlank(cid) && inputStream != null) { + this.images.put(cid, inputStream); + } + return this; + } + + /** + * 添加内联图片。 + * + * @param imageMap 图片 cid 与输入流映射 + * @return 当前构建器 + */ + public MailBuilder images(Map imageMap) { + if (MapUtil.isNotEmpty(imageMap)) { + imageMap.forEach(this::image); + } + return this; + } + + /** + * 设置附件。 + * + * @param files 附件列表 + * @return 当前构建器 + */ + public MailBuilder files(File... files) { + this.files = files == null ? new File[0] : files; + return this; + } + + /** + * 发送邮件。 + * + * @return message-id + */ + public String send() { + validate(); + MailAccount account = resolveMailAccount(); + JakartaMail mail = JakartaMail.create(account).setUseGlobalSession(useGlobalSession); + mail.setTos(tos.toArray(new String[0])); + if (CollUtil.isNotEmpty(ccs)) { + mail.setCcs(ccs.toArray(new String[0])); + } + if (CollUtil.isNotEmpty(bccs)) { + mail.setBccs(bccs.toArray(new String[0])); + } + mail.setTitle(subject); + mail.setContent(content); + mail.setHtml(html); + mail.setFiles(files); + try { + if (MapUtil.isNotEmpty(images)) { + images.forEach(mail::addImage); + } + return mail.send(); + } finally { + images.values().forEach(IoUtil::close); + } + } + + private void validate() { + if (CollUtil.isEmpty(tos)) { + throw new IllegalArgumentException("邮件收件人不能为空"); + } + if (StrUtil.isBlank(subject)) { + throw new IllegalArgumentException("邮件标题不能为空"); + } + if (content == null) { + throw new IllegalArgumentException("邮件正文不能为空"); + } + } + + private MailAccount resolveMailAccount() { + MailAccount account = mailAccount; + if (account == null) { + account = SpringUtils.getBean(MailAccount.class); + } + if (StringUtils.isAllBlank(from, user, pass)) { + return account; + } + MailAccount copy = ObjectUtil.clone(account); + copy.setFrom(StringUtils.blankToDefault(from, copy.getFrom())); + copy.setUser(StringUtils.blankToDefault(user, copy.getUser())); + copy.setPass(StringUtils.blankToDefault(pass, copy.getPass())); + return copy; + } + + private List splitAddress(String addresses) { + if (StrUtil.isBlank(addresses)) { + return Collections.emptyList(); + } + return normalizeAddresses(StrUtil.splitTrim(addresses.replace(';', ','), ',')); + } + + private List normalizeAddresses(Collection addresses) { + if (CollUtil.isEmpty(addresses)) { + return Collections.emptyList(); + } + List result = new ArrayList<>(addresses.size()); + for (String address : addresses) { + if (StrUtil.isNotBlank(address)) { + result.add(address.trim()); + } + } + return result; + } + +} diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java deleted file mode 100644 index 06343bda0..000000000 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/utils/MailUtils.java +++ /dev/null @@ -1,473 +0,0 @@ -package org.dromara.common.mail.utils; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.io.IoUtil; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.CharUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.mail.JakartaMail; -import cn.hutool.extra.mail.JakartaUserPassAuthenticator; -import cn.hutool.extra.mail.MailAccount; -import jakarta.mail.Authenticator; -import jakarta.mail.Session; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import org.dromara.common.core.utils.SpringUtils; -import org.dromara.common.core.utils.StringUtils; - -import java.io.File; -import java.io.InputStream; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -/** - * 邮件工具类 - */ -@NoArgsConstructor(access = AccessLevel.PRIVATE) -public class MailUtils { - - private static final MailAccount ACCOUNT = SpringUtils.getBean(MailAccount.class); - - /** - * 获取默认邮件账户配置。 - * - * @return 邮件账户配置 - */ - public static MailAccount getMailAccount() { - return ACCOUNT; - } - - /** - * 获取邮件发送实例 (自定义发送人以及授权码) - * - * @param from 发送人 - * @param user 发送人 - * @param pass 授权码 - * @return 邮件账户配置 - */ - public static MailAccount getMailAccount(String from, String user, String pass) { - ACCOUNT.setFrom(StringUtils.blankToDefault(from, ACCOUNT.getFrom())); - ACCOUNT.setUser(StringUtils.blankToDefault(user, ACCOUNT.getUser())); - ACCOUNT.setPass(StringUtils.blankToDefault(pass, ACCOUNT.getPass())); - return ACCOUNT; - } - - /** - * 使用配置文件中设置的账户发送文本邮件,发送给单个或多个收件人
- * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * - * @param to 收件人 - * @param subject 标题 - * @param content 正文 - * @param files 附件列表 - * @return message-id - * @since 3.2.0 - */ - public static String sendText(String to, String subject, String content, File... files) { - return send(to, subject, content, false, files); - } - - /** - * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
- * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * - * @param to 收件人 - * @param subject 标题 - * @param content 正文 - * @param files 附件列表 - * @return message-id - * @since 3.2.0 - */ - public static String sendHtml(String to, String subject, String content, File... files) { - return send(to, subject, content, true, files); - } - - /** - * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
- * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * - * @param to 收件人 - * @param subject 标题 - * @param content 正文 - * @param isHtml 是否为HTML - * @param files 附件列表 - * @return message-id - */ - public static String send(String to, String subject, String content, boolean isHtml, File... files) { - return send(splitAddress(to), subject, content, isHtml, files); - } - - /** - * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
- * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * - * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * @param subject 标题 - * @param content 正文 - * @param isHtml 是否为HTML - * @param files 附件列表 - * @return message-id - * @since 4.0.3 - */ - public static String send(String to, String cc, String bcc, String subject, String content, boolean isHtml, File... files) { - return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, isHtml, files); - } - - /** - * 使用配置文件中设置的账户发送文本邮件,发送给多人 - * - * @param tos 收件人列表 - * @param subject 标题 - * @param content 正文 - * @param files 附件列表 - * @return message-id - */ - public static String sendText(Collection tos, String subject, String content, File... files) { - return send(tos, subject, content, false, files); - } - - /** - * 使用配置文件中设置的账户发送HTML邮件,发送给多人 - * - * @param tos 收件人列表 - * @param subject 标题 - * @param content 正文 - * @param files 附件列表 - * @return message-id - * @since 3.2.0 - */ - public static String sendHtml(Collection tos, String subject, String content, File... files) { - return send(tos, subject, content, true, files); - } - - /** - * 使用配置文件中设置的账户发送邮件,发送给多人 - * - * @param tos 收件人列表 - * @param subject 标题 - * @param content 正文 - * @param isHtml 是否为HTML - * @param files 附件列表 - * @return message-id - */ - public static String send(Collection tos, String subject, String content, boolean isHtml, File... files) { - return send(tos, null, null, subject, content, isHtml, files); - } - - /** - * 使用配置文件中设置的账户发送邮件,发送给多人 - * - * @param tos 收件人列表 - * @param ccs 抄送人列表,可以为null或空 - * @param bccs 密送人列表,可以为null或空 - * @param subject 标题 - * @param content 正文 - * @param isHtml 是否为HTML - * @param files 附件列表 - * @return message-id - * @since 4.0.3 - */ - public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { - return send(getMailAccount(), true, tos, ccs, bccs, subject, content, null, isHtml, files); - } - - // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount - - /** - * 发送邮件给多人 - * - * @param mailAccount 邮件认证对象 - * @param to 收件人,多个收件人逗号或者分号隔开 - * @param subject 标题 - * @param content 正文 - * @param isHtml 是否为HTML格式 - * @param files 附件列表 - * @return message-id - * @since 3.2.0 - */ - public static String send(MailAccount mailAccount, String to, String subject, String content, boolean isHtml, File... files) { - return send(mailAccount, splitAddress(to), subject, content, isHtml, files); - } - - /** - * 发送邮件给多人 - * - * @param mailAccount 邮件账户信息 - * @param tos 收件人列表 - * @param subject 标题 - * @param content 正文 - * @param isHtml 是否为HTML格式 - * @param files 附件列表 - * @return message-id - */ - public static String send(MailAccount mailAccount, Collection tos, String subject, String content, boolean isHtml, File... files) { - return send(mailAccount, tos, null, null, subject, content, isHtml, files); - } - - /** - * 发送邮件给多人 - * - * @param mailAccount 邮件账户信息 - * @param tos 收件人列表 - * @param ccs 抄送人列表,可以为null或空 - * @param bccs 密送人列表,可以为null或空 - * @param subject 标题 - * @param content 正文 - * @param isHtml 是否为HTML格式 - * @param files 附件列表 - * @return message-id - * @since 4.0.3 - */ - public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, boolean isHtml, File... files) { - return send(mailAccount, false, tos, ccs, bccs, subject, content, null, isHtml, files); - } - - /** - * 使用配置文件中设置的账户发送HTML邮件,发送给单个或多个收件人
- * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * - * @param to 收件人 - * @param subject 标题 - * @param content 正文 - * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER - * @param files 附件列表 - * @return message-id - * @since 3.2.0 - */ - public static String sendHtml(String to, String subject, String content, Map imageMap, File... files) { - return send(to, subject, content, imageMap, true, files); - } - - /** - * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
- * 多个收件人可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * - * @param to 收件人 - * @param subject 标题 - * @param content 正文 - * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER - * @param isHtml 是否为HTML - * @param files 附件列表 - * @return message-id - */ - public static String send(String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { - return send(splitAddress(to), subject, content, imageMap, isHtml, files); - } - - /** - * 使用配置文件中设置的账户发送邮件,发送单个或多个收件人
- * 多个收件人、抄送人、密送人可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * - * @param to 收件人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * @param cc 抄送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * @param bcc 密送人,可以使用逗号“,”分隔,也可以通过分号“;”分隔 - * @param subject 标题 - * @param content 正文 - * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER - * @param isHtml 是否为HTML - * @param files 附件列表 - * @return message-id - * @since 4.0.3 - */ - public static String send(String to, String cc, String bcc, String subject, String content, Map imageMap, boolean isHtml, File... files) { - return send(splitAddress(to), splitAddress(cc), splitAddress(bcc), subject, content, imageMap, isHtml, files); - } - - /** - * 使用配置文件中设置的账户发送HTML邮件,发送给多人 - * - * @param tos 收件人列表 - * @param subject 标题 - * @param content 正文 - * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER - * @param files 附件列表 - * @return message-id - * @since 3.2.0 - */ - public static String sendHtml(Collection tos, String subject, String content, Map imageMap, File... files) { - return send(tos, subject, content, imageMap, true, files); - } - - /** - * 使用配置文件中设置的账户发送邮件,发送给多人 - * - * @param tos 收件人列表 - * @param subject 标题 - * @param content 正文 - * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER - * @param isHtml 是否为HTML - * @param files 附件列表 - * @return message-id - */ - public static String send(Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { - return send(tos, null, null, subject, content, imageMap, isHtml, files); - } - - /** - * 使用配置文件中设置的账户发送邮件,发送给多人 - * - * @param tos 收件人列表 - * @param ccs 抄送人列表,可以为null或空 - * @param bccs 密送人列表,可以为null或空 - * @param subject 标题 - * @param content 正文 - * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER - * @param isHtml 是否为HTML - * @param files 附件列表 - * @return message-id - * @since 4.0.3 - */ - public static String send(Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, boolean isHtml, File... files) { - return send(getMailAccount(), true, tos, ccs, bccs, subject, content, imageMap, isHtml, files); - } - - // ------------------------------------------------------------------------------------------------------------------------------- Custom MailAccount - - /** - * 发送邮件给多人 - * - * @param mailAccount 邮件认证对象 - * @param to 收件人,多个收件人逗号或者分号隔开 - * @param subject 标题 - * @param content 正文 - * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER - * @param isHtml 是否为HTML格式 - * @param files 附件列表 - * @return message-id - * @since 3.2.0 - */ - public static String send(MailAccount mailAccount, String to, String subject, String content, Map imageMap, boolean isHtml, File... files) { - return send(mailAccount, splitAddress(to), subject, content, imageMap, isHtml, files); - } - - /** - * 发送邮件给多人 - * - * @param mailAccount 邮件账户信息 - * @param tos 收件人列表 - * @param subject 标题 - * @param content 正文 - * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER - * @param isHtml 是否为HTML格式 - * @param files 附件列表 - * @return message-id - * @since 4.6.3 - */ - public static String send(MailAccount mailAccount, Collection tos, String subject, String content, Map imageMap, boolean isHtml, File... files) { - return send(mailAccount, tos, null, null, subject, content, imageMap, isHtml, files); - } - - /** - * 发送邮件给多人 - * - * @param mailAccount 邮件账户信息 - * @param tos 收件人列表 - * @param ccs 抄送人列表,可以为null或空 - * @param bccs 密送人列表,可以为null或空 - * @param subject 标题 - * @param content 正文 - * @param imageMap 图片与占位符,占位符格式为cid:$IMAGE_PLACEHOLDER - * @param isHtml 是否为HTML格式 - * @param files 附件列表 - * @return message-id - * @since 4.6.3 - */ - public static String send(MailAccount mailAccount, Collection tos, Collection ccs, Collection bccs, String subject, String content, Map imageMap, - boolean isHtml, File... files) { - return send(mailAccount, false, tos, ccs, bccs, subject, content, imageMap, isHtml, files); - } - - /** - * 根据配置文件,获取邮件客户端会话 - * - * @param mailAccount 邮件账户配置 - * @param isSingleton 是否单例(全局共享会话) - * @return {@link Session} - * @since 5.5.7 - */ - public static Session getSession(MailAccount mailAccount, boolean isSingleton) { - Authenticator authenticator = null; - if (mailAccount.isAuth()) { - authenticator = new JakartaUserPassAuthenticator(mailAccount.getUser(), mailAccount.getPass()); - } - - return isSingleton ? Session.getDefaultInstance(mailAccount.getSmtpProps(), authenticator) // - : Session.getInstance(mailAccount.getSmtpProps(), authenticator); - } - - // ------------------------------------------------------------------------------------------------------------------------ Private method start - - /** - * 发送邮件给多人 - * - * @param mailAccount 邮件账户信息 - * @param useGlobalSession 是否全局共享Session - * @param tos 收件人列表 - * @param ccs 抄送人列表,可以为null或空 - * @param bccs 密送人列表,可以为null或空 - * @param subject 标题 - * @param content 正文 - * @param imageMap 图片与占位符,占位符格式为cid:${cid} - * @param isHtml 是否为HTML格式 - * @param files 附件列表 - * @return message-id - * @since 4.6.3 - */ - private static String send(MailAccount mailAccount, boolean useGlobalSession, Collection tos, Collection ccs, Collection bccs, String subject, String content, - Map imageMap, boolean isHtml, File... files) { - final JakartaMail mail = JakartaMail.create(mailAccount).setUseGlobalSession(useGlobalSession); - - // 可选抄送人 - if (CollUtil.isNotEmpty(ccs)) { - mail.setCcs(ccs.toArray(new String[0])); - } - // 可选密送人 - if (CollUtil.isNotEmpty(bccs)) { - mail.setBccs(bccs.toArray(new String[0])); - } - - mail.setTos(tos.toArray(new String[0])); - mail.setTitle(subject); - mail.setContent(content); - mail.setHtml(isHtml); - mail.setFiles(files); - - // 图片 - if (MapUtil.isNotEmpty(imageMap)) { - for (Entry entry : imageMap.entrySet()) { - mail.addImage(entry.getKey(), entry.getValue()); - // 关闭流 - IoUtil.close(entry.getValue()); - } - } - - return mail.send(); - } - - /** - * 将多个联系人转为列表,分隔符为逗号或者分号 - * - * @param addresses 多个联系人,如果为空返回null - * @return 联系人列表 - */ - private static List splitAddress(String addresses) { - if (StrUtil.isBlank(addresses)) { - return null; - } - - List result; - if (StrUtil.contains(addresses, CharUtil.COMMA)) { - result = StrUtil.splitTrim(addresses, CharUtil.COMMA); - } else if (StrUtil.contains(addresses, ';')) { - result = StrUtil.splitTrim(addresses, ';'); - } else { - result = CollUtil.newArrayList(addresses); - } - return result; - } - // ------------------------------------------------------------------------------------------------------------------------ Private method end -} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/MailSendController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/MailSendController.java index 48365a350..9bf51afa2 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/MailSendController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/MailSendController.java @@ -2,7 +2,7 @@ package org.dromara.demo.controller; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.R; -import org.dromara.common.mail.utils.MailUtils; +import org.dromara.common.mail.core.MailBuilder; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; @@ -32,7 +32,7 @@ public class MailSendController { */ @GetMapping("/sendSimpleMessage") public R sendSimpleMessage(String to, String subject, String text) { - MailUtils.sendText(to, subject, text); + MailBuilder.of().to(to).subject(subject).text(text).send(); return R.ok(); } @@ -46,7 +46,7 @@ public class MailSendController { @GetMapping("/sendMessageWithAttachment") public R sendMessageWithAttachment(String to, String subject, String text) { // 附件路径 禁止前端传递 有任意读取系统文件风险 - MailUtils.sendText(to, subject, text, new File("/xxx/xxx")); + MailBuilder.of().to(to).subject(subject).text(text).files(new File("/xxx/xxx")).send(); return R.ok(); } @@ -62,7 +62,7 @@ public class MailSendController { // 附件路径 禁止前端传递 有任意读取系统文件风险 String[] paths = new String[]{"/xxx/xxx", "/xxx/xxx"}; File[] array = Arrays.stream(paths).map(File::new).toArray(File[]::new); - MailUtils.sendText(to, subject, text, array); + MailBuilder.of().to(to).subject(subject).text(text).files(array).send(); return R.ok(); } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index a1abc472d..9f790c187 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -11,7 +11,7 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.mail.utils.MailUtils; +import org.dromara.common.mail.core.MailBuilder; import org.dromara.system.api.MessageService; import org.dromara.system.api.domain.PushPayloadDTO; import org.dromara.system.api.domain.UserDTO; @@ -122,7 +122,7 @@ public class FlwCommonServiceImpl implements IFlwCommonService { message, null, path )); } - case EMAIL_MESSAGE -> MailUtils.sendText(emails, subject, message); + case EMAIL_MESSAGE -> MailBuilder.of().to(emails).subject(subject).text(message).send(); case SMS_MESSAGE -> { // LinkedHashMap map = new LinkedHashMap<>(1); // // 根据具体短信服务商参数用法传参 From ce764f08a7150d42bff821d73a843bc09d5ee119 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: Sat, 16 May 2026 14:41:13 +0800 Subject: [PATCH 21/35] =?UTF-8?q?refactor(common-mybatis):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=95=B0=E6=8D=AE=E6=9D=83=E9=99=90=E4=B8=8E=E5=88=86?= =?UTF-8?q?=E9=A1=B5=E5=9F=BA=E7=A1=80=E8=83=BD=E5=8A=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 DataPermissionIgnoreContext,收口数据权限忽略状态处理 - 优化 DataPermissionHelper,恢复进入 ignore 前的权限忽略状态 - 优化数据权限切点和通知的代理接口匹配逻辑 - 缓存 BaseMapperPlus 泛型解析结果,减少重复反射 - 优化 PageQuery 排序解析,避免修改请求对象状态 - 增强 PageQuery 默认构造和起始行号计算的空值兼容 --- .../mybatis/aspect/DataPermissionAdvice.java | 22 ++++-- .../aspect/DataPermissionPointcut.java | 27 ++++--- .../mybatis/core/mapper/BaseMapperPlus.java | 24 +++++- .../common/mybatis/core/page/PageQuery.java | 15 +++- .../mybatis/helper/DataPermissionHelper.java | 69 ++---------------- .../helper/DataPermissionIgnoreContext.java | 73 +++++++++++++++++++ 6 files changed, 141 insertions(+), 89 deletions(-) create mode 100644 ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionIgnoreContext.java diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAdvice.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAdvice.java index 54d5ad41a..50ce4a91b 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAdvice.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionAdvice.java @@ -1,6 +1,5 @@ package org.dromara.common.mybatis.aspect; -import lombok.extern.slf4j.Slf4j; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.dromara.common.mybatis.annotation.DataPermission; @@ -14,16 +13,14 @@ import java.lang.reflect.Proxy; * * @author 秋辞未寒 */ -@Slf4j public class DataPermissionAdvice implements MethodInterceptor { @Override public Object invoke(MethodInvocation invocation) throws Throwable { Object target = invocation.getThis(); Method method = invocation.getMethod(); - Object[] args = invocation.getArguments(); // 设置权限注解 - DataPermissionHelper.setPermission(getDataPermissionAnnotation(target, method, args)); + DataPermissionHelper.setPermission(getDataPermissionAnnotation(target, method)); try { // 执行代理方法 return invocation.proceed(); @@ -36,7 +33,7 @@ public class DataPermissionAdvice implements MethodInterceptor { /** * 获取数据权限注解 */ - private DataPermission getDataPermissionAnnotation(Object target, Method method,Object[] args){ + private DataPermission getDataPermissionAnnotation(Object target, Method method) { DataPermission dataPermission = method.getAnnotation(DataPermission.class); // 优先获取方法上的注解 if (dataPermission != null) { @@ -46,9 +43,18 @@ public class DataPermissionAdvice implements MethodInterceptor { Class targetClass = target.getClass(); // 如果是 JDK 动态代理,则获取真实的Class实例 if (Proxy.isProxyClass(targetClass)) { - targetClass = targetClass.getInterfaces()[0]; + return getProxyClassDataPermission(targetClass); } - dataPermission = targetClass.getAnnotation(DataPermission.class); - return dataPermission; + return targetClass.getAnnotation(DataPermission.class); + } + + private DataPermission getProxyClassDataPermission(Class targetClass) { + for (Class interfaceClass : targetClass.getInterfaces()) { + DataPermission dataPermission = interfaceClass.getAnnotation(DataPermission.class); + if (dataPermission != null) { + return dataPermission; + } + } + return null; } } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcut.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcut.java index 4b7d945a3..43ca05371 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcut.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/aspect/DataPermissionPointcut.java @@ -1,6 +1,5 @@ package org.dromara.common.mybatis.aspect; -import lombok.extern.slf4j.Slf4j; import org.dromara.common.mybatis.annotation.DataPermission; import org.springframework.aop.support.StaticMethodMatcherPointcut; @@ -12,8 +11,6 @@ import java.lang.reflect.Proxy; * * @author 秋辞未寒 */ -@Slf4j -@SuppressWarnings("all") public class DataPermissionPointcut extends StaticMethodMatcherPointcut { @Override @@ -25,15 +22,23 @@ public class DataPermissionPointcut extends StaticMethodMatcherPointcut { } // MyBatis 的 Mapper 就是通过 JDK 动态代理实现的,所以这里需要检查是否匹配 JDK 的动态代理 - Class targetClassRef = targetClass; - if (Proxy.isProxyClass(targetClassRef)) { - // 数据权限注解不对继承生效,但由于 SpringIOC 容器拿到的实际上是 MyBatis 代理过后的 Mapper,而 targetClass.isAnnotationPresent 实际匹配的是 Proxy 类的注解,不会查找代理类。 - // 所以这里不能用 targetClass.isAnnotationPresent,只能用 AnnotatedElementUtils.hasAnnotation 或 targetClass.getInterfaces()[0].isAnnotationPresent 去做匹配,以检查被代理的 MapperClass 是否具有注解 - // 原理:JDK 动态代理本质上就是对接口进行实现然后对具体的接口实现做代理,所以直接通过接口可以拿到实际的 MapperClass - targetClassRef = targetClass.getInterfaces()[0]; - - } + Class targetClassRef = resolveTargetClass(targetClass); return targetClassRef.isAnnotationPresent(DataPermission.class); } + private Class resolveTargetClass(Class targetClass) { + if (!Proxy.isProxyClass(targetClass)) { + return targetClass; + } + for (Class interfaceClass : targetClass.getInterfaces()) { + // 数据权限注解不对继承生效,但由于 SpringIOC 容器拿到的实际上是 MyBatis 代理过后的 Mapper,而 targetClass.isAnnotationPresent 实际匹配的是 Proxy 类的注解,不会查找代理类。 + // 所以这里不能用 targetClass.isAnnotationPresent,只能用 AnnotatedElementUtils.hasAnnotation 或 targetClass.getInterfaces()[0].isAnnotationPresent 去做匹配,以检查被代理的 MapperClass 是否具有注解 + // 原理:JDK 动态代理本质上就是对接口进行实现然后对具体的接口实现做代理,所以直接通过接口可以拿到实际的 MapperClass + if (interfaceClass.isAnnotationPresent(DataPermission.class)) { + return interfaceClass; + } + } + return targetClass; + } + } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java index 566c63eeb..02b716e49 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/mapper/BaseMapperPlus.java @@ -35,13 +35,20 @@ public interface BaseMapperPlus extends BaseMapper { Log log = LogFactory.getLog(BaseMapperPlus.class); + ClassValue[]> TYPE_ARGUMENT_CACHE = new ClassValue<>() { + @Override + protected Class[] computeValue(Class type) { + return GenericTypeUtils.resolveTypeArguments(type, BaseMapperPlus.class); + } + }; + /** * 获取当前实例对象关联的泛型类型 V 的 Class 对象 * * @return 返回当前实例对象关联的泛型类型 V 的 Class 对象 */ default Class currentVoClass() { - return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[1]; + return (Class) currentMapperTypes()[1]; } /** @@ -50,7 +57,20 @@ public interface BaseMapperPlus extends BaseMapper { * @return 返回当前实例对象关联的泛型类型 T 的 Class 对象 */ default Class currentModelClass() { - return (Class) GenericTypeUtils.resolveTypeArguments(this.getClass(), BaseMapperPlus.class)[0]; + return (Class) currentMapperTypes()[0]; + } + + /** + * 获取当前 Mapper 的实体与 VO 泛型类型。 + * + * @return 泛型类型数组 + */ + private Class[] currentMapperTypes() { + Class[] types = TYPE_ARGUMENT_CACHE.get(this.getClass()); + if (types == null || types.length < 2) { + throw new IllegalStateException("无法解析 Mapper 泛型类型: " + this.getClass().getName()); + } + return types; } /** diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java index d66021582..62e140668 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java @@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; +import lombok.NoArgsConstructor; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.sql.SqlUtil; @@ -21,6 +22,7 @@ import java.util.List; * @author Lion Li */ @Data +@NoArgsConstructor public class PageQuery implements Serializable { @Serial @@ -84,16 +86,16 @@ public class PageQuery implements Serializable { */ private List buildOrderItem() { if (StringUtils.isBlank(orderByColumn) || StringUtils.isBlank(isAsc)) { - return null; + return List.of(); } String orderBy = SqlUtil.escapeOrderBySql(orderByColumn); orderBy = StringUtils.toUnderScoreCase(orderBy); // 兼容前端排序类型 - isAsc = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"}); + String orderDirection = StringUtils.replaceEach(isAsc, new String[]{"ascending", "descending"}, new String[]{"asc", "desc"}); String[] orderByArr = orderBy.split(StringUtils.SEPARATOR); - String[] isAscArr = isAsc.split(StringUtils.SEPARATOR); + String[] isAscArr = orderDirection.split(StringUtils.SEPARATOR); if (isAscArr.length != 1 && isAscArr.length != orderByArr.length) { throw new ServiceException("排序参数有误"); } @@ -121,7 +123,12 @@ public class PageQuery implements Serializable { */ @JsonIgnore public Integer getFirstNum() { - return (pageNum - 1) * pageSize; + Integer currentPageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM); + Integer currentPageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE); + if (currentPageNum <= 0) { + currentPageNum = DEFAULT_PAGE_NUM; + } + return (currentPageNum - 1) * currentPageSize; } /** diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java index d31b45b1a..cae799427 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionHelper.java @@ -2,18 +2,12 @@ package org.dromara.common.mybatis.helper; import cn.dev33.satoken.context.SaHolder; import cn.dev33.satoken.context.model.SaStorage; -import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.ObjectUtil; -import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; -import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import org.dromara.common.mybatis.core.domain.DataPermissionAccess; -import org.dromara.common.core.utils.reflect.ReflectUtils; import org.dromara.common.mybatis.annotation.DataPermission; +import org.dromara.common.mybatis.core.domain.DataPermissionAccess; -import java.util.ArrayDeque; -import java.util.Deque; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; @@ -31,8 +25,6 @@ public class DataPermissionHelper { private static final String DATA_PERMISSION_KEY = "data:permission"; private static final String ACCESS_KEY = "data:permission:access"; - private static final ThreadLocal> REENTRANT_IGNORE = ThreadLocal.withInitial(ArrayDeque::new); - private static final ThreadLocal PERMISSION_CACHE = new ThreadLocal<>(); /** @@ -120,68 +112,17 @@ public class DataPermissionHelper { throw new IllegalStateException("data permission context type exception"); } - /** - * 获取当前忽略策略。 - * - * @return 忽略策略 - */ - private static IgnoreStrategy getIgnoreStrategy() { - Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL")); - if (ignoreStrategyLocal instanceof ThreadLocal IGNORE_STRATEGY_LOCAL) { - if (IGNORE_STRATEGY_LOCAL.get() instanceof IgnoreStrategy ignoreStrategy) { - return ignoreStrategy; - } - } - return null; - } - - /** - * 开启忽略数据权限(开启后需手动调用 {@link #disableIgnore()} 关闭) - */ - private static void enableIgnore() { - IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); - if (ObjectUtil.isNull(ignoreStrategy)) { - InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); - } else { - ignoreStrategy.setDataPermission(true); - } - Deque reentrantStack = REENTRANT_IGNORE.get(); - reentrantStack.push(reentrantStack.size() + 1); - } - - /** - * 关闭忽略数据权限 - */ - private static void disableIgnore() { - IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); - if (ObjectUtil.isNotNull(ignoreStrategy)) { - boolean noOtherIgnoreStrategy = !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName()) - && !Boolean.TRUE.equals(ignoreStrategy.getBlockAttack()) - && !Boolean.TRUE.equals(ignoreStrategy.getIllegalSql()) - && !Boolean.TRUE.equals(ignoreStrategy.getTenantLine()) - && CollectionUtil.isEmpty(ignoreStrategy.getOthers()); - Deque reentrantStack = REENTRANT_IGNORE.get(); - boolean empty = reentrantStack.isEmpty() || reentrantStack.pop() == 1; - if (noOtherIgnoreStrategy && empty) { - InterceptorIgnoreHelper.clearIgnoreStrategy(); - } else if (empty) { - ignoreStrategy.setDataPermission(false); - } - - } - } - /** * 在忽略数据权限中执行 * * @param handle 处理执行方法 */ public static void ignore(Runnable handle) { - enableIgnore(); + DataPermissionIgnoreContext.enable(); try { handle.run(); } finally { - disableIgnore(); + DataPermissionIgnoreContext.disable(); } } @@ -192,11 +133,11 @@ public class DataPermissionHelper { * @return 执行结果 */ public static T ignore(Supplier handle) { - enableIgnore(); + DataPermissionIgnoreContext.enable(); try { return handle.get(); } finally { - disableIgnore(); + DataPermissionIgnoreContext.disable(); } } diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionIgnoreContext.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionIgnoreContext.java new file mode 100644 index 000000000..848d3387a --- /dev/null +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/helper/DataPermissionIgnoreContext.java @@ -0,0 +1,73 @@ +package org.dromara.common.mybatis.helper; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.plugins.IgnoreStrategy; +import com.baomidou.mybatisplus.core.plugins.InterceptorIgnoreHelper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.reflect.ReflectUtils; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * 数据权限忽略状态适配器。 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +final class DataPermissionIgnoreContext { + + private static final ThreadLocal> DATA_PERMISSION_STACK = ThreadLocal.withInitial(ArrayDeque::new); + + /** + * 开启忽略数据权限。 + */ + static void enable() { + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + DATA_PERMISSION_STACK.get().push(ignoreStrategy != null && Boolean.TRUE.equals(ignoreStrategy.getDataPermission())); + if (ObjectUtil.isNull(ignoreStrategy)) { + InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().dataPermission(true).build()); + } else { + ignoreStrategy.setDataPermission(true); + } + } + + /** + * 关闭忽略数据权限,并恢复进入前的数据权限忽略状态。 + */ + static void disable() { + Deque stack = DATA_PERMISSION_STACK.get(); + boolean previousDataPermission = !stack.isEmpty() && stack.pop(); + IgnoreStrategy ignoreStrategy = getIgnoreStrategy(); + if (ObjectUtil.isNotNull(ignoreStrategy)) { + if (previousDataPermission) { + ignoreStrategy.setDataPermission(true); + } else if (isOnlyDataPermissionIgnored(ignoreStrategy) && stack.isEmpty()) { + InterceptorIgnoreHelper.clearIgnoreStrategy(); + } else { + ignoreStrategy.setDataPermission(false); + } + } + if (stack.isEmpty()) { + DATA_PERMISSION_STACK.remove(); + } + } + + private static IgnoreStrategy getIgnoreStrategy() { + Object ignoreStrategyLocal = ReflectUtils.getStaticFieldValue(ReflectUtils.getField(InterceptorIgnoreHelper.class, "IGNORE_STRATEGY_LOCAL")); + if (ignoreStrategyLocal instanceof ThreadLocal ignoreStrategyThreadLocal + && ignoreStrategyThreadLocal.get() instanceof IgnoreStrategy ignoreStrategy) { + return ignoreStrategy; + } + return null; + } + + private static boolean isOnlyDataPermissionIgnored(IgnoreStrategy ignoreStrategy) { + return !Boolean.TRUE.equals(ignoreStrategy.getDynamicTableName()) + && !Boolean.TRUE.equals(ignoreStrategy.getBlockAttack()) + && !Boolean.TRUE.equals(ignoreStrategy.getIllegalSql()) + && !Boolean.TRUE.equals(ignoreStrategy.getTenantLine()) + && CollectionUtil.isEmpty(ignoreStrategy.getOthers()); + } + +} From 287cce6238b8ad9eda382984512c52da21c3ce0c 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: Sat, 16 May 2026 14:56:07 +0800 Subject: [PATCH 22/35] =?UTF-8?q?refactor(common-oss):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20OSS=20=E5=AE=A2=E6=88=B7=E7=AB=AF=E7=94=9F=E5=91=BD?= =?UTF-8?q?=E5=91=A8=E6=9C=9F=E4=B8=8E=E9=85=8D=E7=BD=AE=E6=9E=84=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 下载 InputStream 回调后自动关闭响应流,避免底层连接释放不明确 - 统一解包异步调用异常,保留真实 S3 错误原因 - OssFactory 改为按 configKey 粒度加锁,降低多配置初始化阻塞 - OSS 配置更新和删除后主动移除本地旧客户端,释放相关资源 - 收敛 OSS region、ACL、路径风格和访问 URL 构建逻辑 - Options 新增 create 方法,保留 builder 兼容旧调用 --- .../oss/client/AbstractOssClientImpl.java | 77 +++++++---------- .../common/oss/config/OssClientConfig.java | 84 ++++++++++--------- .../common/oss/factory/OssFactory.java | 31 +++++-- .../org/dromara/common/oss/model/Options.java | 7 ++ .../service/impl/SysOssConfigServiceImpl.java | 18 +++- 5 files changed, 123 insertions(+), 94 deletions(-) diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/AbstractOssClientImpl.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/AbstractOssClientImpl.java index af4eddd61..82cdb1494 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/AbstractOssClientImpl.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/AbstractOssClientImpl.java @@ -37,7 +37,9 @@ import java.util.Collection; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; +import java.util.concurrent.ExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -119,10 +121,7 @@ public abstract class AbstractOssClientImpl implements OssClient { // 将状态转为已初始化 initialized.compareAndSet(false, true); } catch (Exception e) { - if (e instanceof S3StorageException) { - throw e; - } - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -168,10 +167,7 @@ public abstract class AbstractOssClientImpl implements OssClient { .handleAsync(handleAsyncAction) .join(); } catch (Exception e) { - if (e instanceof S3StorageException ex) { - throw ex; - } - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -229,10 +225,7 @@ public abstract class AbstractOssClientImpl implements OssClient { options.setLength(file.length()); return bucketUpload(bucket, key, file.getChannel(), -1L, options); } catch (Exception e) { - if (e instanceof S3StorageException ex) { - throw ex; - } - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -253,10 +246,7 @@ public abstract class AbstractOssClientImpl implements OssClient { } return bucketUpload(bucket, key, in, size, options); } catch (Exception e) { - if (e instanceof S3StorageException ex) { - throw ex; - } - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -282,10 +272,7 @@ public abstract class AbstractOssClientImpl implements OssClient { try (ByteArrayInputStream in = new ByteArrayInputStream(data)) { return bucketUpload(bucket, key, in, data.length, options); } catch (Exception e) { - if (e instanceof S3StorageException ex) { - throw ex; - } - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -341,10 +328,7 @@ public abstract class AbstractOssClientImpl implements OssClient { .join() .result(); } catch (Exception e) { - if (e instanceof S3StorageException ex) { - throw ex; - } - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -356,25 +340,18 @@ public abstract class AbstractOssClientImpl implements OssClient { publisher.subscribe(downloadSubscriber).join(); return getObjectResult; } catch (Exception e) { - if (e instanceof S3StorageException ex) { - throw ex; - } - throw S3StorageException.form(e); + throw toStorageException(e); } } @Override public T bucketDownload(String bucket, String key, BiFunction downloadTransformer) { - try { - ResponseInputStream responseInputStream = doCustomDownload(builder -> builder.bucket(bucket).key(key), AsyncResponseTransformer.toBlockingInputStream(), null); + try (ResponseInputStream responseInputStream = doCustomDownload(builder -> builder.bucket(bucket).key(key), AsyncResponseTransformer.toBlockingInputStream(), null)) { GetObjectResponse response = responseInputStream.response(); GetObjectResult getObjectResult = buildGetObjectResult(key, response); return downloadTransformer.apply(getObjectResult, responseInputStream); } catch (Exception e) { - if (e instanceof S3StorageException ex) { - throw ex; - } - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -383,10 +360,7 @@ public abstract class AbstractOssClientImpl implements OssClient { try (OutputStream out = Files.newOutputStream(path)) { return bucketDownload(bucket, key, out); } catch (Exception e) { - if (e instanceof S3StorageException ex) { - throw ex; - } - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -395,10 +369,7 @@ public abstract class AbstractOssClientImpl implements OssClient { try (FileOutputStream out = new FileOutputStream(file)) { return bucketDownload(bucket, key, out); } catch (Exception e) { - if (e instanceof S3StorageException ex) { - throw ex; - } - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -438,7 +409,7 @@ public abstract class AbstractOssClientImpl implements OssClient { s3AsyncClient.deleteObject(builder -> builder.bucket(bucket).key(key)).join(); return true; } catch (Exception e) { - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -452,7 +423,7 @@ public abstract class AbstractOssClientImpl implements OssClient { .url() .toExternalForm(); } catch (Exception e) { - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -466,7 +437,7 @@ public abstract class AbstractOssClientImpl implements OssClient { .url() .toExternalForm(); } catch (Exception e) { - throw S3StorageException.form(e); + throw toStorageException(e); } } @@ -623,6 +594,22 @@ public abstract class AbstractOssClientImpl implements OssClient { return fileName.substring(index); } + private S3StorageException toStorageException(Throwable e) { + Throwable cause = unwrapAsyncException(e); + if (cause instanceof S3StorageException ex) { + return ex; + } + return S3StorageException.form(cause); + } + + private Throwable unwrapAsyncException(Throwable e) { + Throwable cause = e; + while ((cause instanceof CompletionException || cause instanceof ExecutionException) && cause.getCause() != null) { + cause = cause.getCause(); + } + return cause; + } + @Override public void close() throws Exception { if (s3TransferManager != null) { 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 def93240f..f6986e99c 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 @@ -166,38 +166,17 @@ public class OssClientConfig implements Config !s.isBlank()) - .orElseThrow(() -> S3StorageException.form("endpoint is not configured.")); - return BucketUrlUtil.rebuildUrlHeader(useHttps, endpoint); + return BucketUrlUtil.rebuildUrlHeader(useHttps, getEndpoint()); } /** @@ -221,7 +197,7 @@ public class OssClientConfig implements Config HttpUtil.isHttp(s) || HttpUtil.isHttps(s)) + .filter(OssClientConfig::hasHttpHeader) // 否则使用站点 .orElseGet(this::getEndpointUrl); } @@ -245,20 +221,50 @@ public class OssClientConfig implements Config HttpUtil.isHttp(s) || HttpUtil.isHttps(s)) - // 否则使用站点 - .orElseGet(() -> - endpoint() - .filter(s -> !s.isBlank()) - .orElseThrow(() -> S3StorageException.form("endpoint is not configured.")) - ); + String url = getAccessBaseUrl(); // 根据是否使用路径风格配置项决定存储桶的URL风格 return usePathStyleAccess ? BucketUrlUtil.getPathStyleBucketUrl(useHttps, url, bucket) : BucketUrlUtil.getSiteStyleBucketUrl(useHttps, url, bucket); } + private static Region parseRegion(String regionString) { + if (StringUtils.isBlank(regionString)) { + return Region.US_EAST_1; + } + return Region.of(regionString); + } + + private static boolean resolvePathStyleAccess(OssProperties properties) { + // 旧配置没有显式路径风格字段,只能继续按内置云厂商 endpoint 做兼容推断。 + return !StringUtils.containsAny(properties.getEndpoint(), OssConstant.CLOUD_SERVICE); + } + + private static AccessControlPolicyConfig resolveAccessControlPolicy(String accessPolicyString) { + // 绝大多数云厂商不允许操作 ACL,默认禁用;当前业务只用访问策略判断是否生成预签名 URL。 + if (StringUtils.isBlank(accessPolicyString)) { + return AccessControlPolicyConfig.DEFAULT; + } + return AccessControlPolicyConfig.builder() + .enabled(true) + .accessPolicy(AccessPolicy.formType(accessPolicyString)) + .build(); + } + + private String getAccessBaseUrl() { + return domain() + .filter(OssClientConfig::hasHttpHeader) + .orElseGet(this::getEndpoint); + } + + private String getEndpoint() { + return endpoint() + .filter(s -> !s.isBlank()) + .orElseThrow(() -> S3StorageException.form("endpoint is not configured.")); + } + + private static boolean hasHttpHeader(String url) { + return HttpUtil.isHttp(url) || HttpUtil.isHttps(url); + } + /** * ACL访问策略配置 */ diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java index f51784656..4169303e4 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/factory/OssFactory.java @@ -26,7 +26,7 @@ import java.util.concurrent.locks.ReentrantLock; public class OssFactory { private static final Map CLIENT_CACHE = new ConcurrentHashMap<>(); - private static final ReentrantLock LOCK = new ReentrantLock(); + private static final Map CLIENT_LOCKS = new ConcurrentHashMap<>(); /** * 获取默认实例 @@ -44,13 +44,17 @@ public class OssFactory { * 根据类型获取实例 */ public static OssClient instance(String configKey) { + if (StringUtils.isBlank(configKey)) { + throw S3StorageException.form("文件存储服务类型无法找到!"); + } String json = CacheUtils.get(CacheNames.SYS_OSS_CONFIG, configKey); if (json == null) { throw S3StorageException.form("系统异常, '" + configKey + "'配置信息不存在!"); } OssProperties properties = JsonUtils.parseObject(json, OssProperties.class); OssClientConfig config = OssClientConfig.formProperties(properties); - LOCK.lock(); + ReentrantLock lock = getClientLock(configKey); + lock.lock(); try { OssClient client = CLIENT_CACHE.get(configKey); if (client != null) { @@ -63,7 +67,7 @@ public class OssFactory { CLIENT_CACHE.put(configKey, newClient); return newClient; } finally { - LOCK.unlock(); + lock.unlock(); } } @@ -71,12 +75,25 @@ public class OssFactory { * 移除实例 */ public static boolean remove(String configKey) { - OssClient client = CLIENT_CACHE.remove(configKey); - if (client == null) { + if (StringUtils.isBlank(configKey)) { return false; } - closeClient(configKey, client); - return true; + ReentrantLock lock = getClientLock(configKey); + lock.lock(); + try { + OssClient client = CLIENT_CACHE.remove(configKey); + if (client == null) { + return false; + } + closeClient(configKey, client); + return true; + } finally { + lock.unlock(); + } + } + + private static ReentrantLock getClientLock(String configKey) { + return CLIENT_LOCKS.computeIfAbsent(configKey, key -> new ReentrantLock()); } private static void closeClient(String configKey, OssClient client) { diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/Options.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/Options.java index cb30a5a21..3f298cb8a 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/Options.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/model/Options.java @@ -74,6 +74,13 @@ public class Options { * 创建可选项对象 */ public static Options builder() { + return create(); + } + + /** + * 创建可选项对象 + */ + public static Options create() { return new Options(); } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java index 26773808b..a59013f4c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java @@ -17,6 +17,7 @@ import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.oss.constant.OssConstant; +import org.dromara.common.oss.factory.OssFactory; import org.dromara.common.redis.utils.CacheUtils; import org.dromara.common.redis.utils.RedisUtils; import org.dromara.system.domain.SysOssConfig; @@ -29,6 +30,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Collection; import java.util.List; +import java.util.Objects; /** * 对象存储配置Service业务层处理 @@ -130,6 +132,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { public Boolean updateByBo(SysOssConfigBo bo) { SysOssConfig config = MapstructUtils.convert(bo, SysOssConfig.class); validEntityBeforeSave(config); + SysOssConfig oldConfig = ossConfigMapper.selectById(config.getOssConfigId()); boolean flag = ossConfigMapper.lambda() .set(ObjectUtil.isNull(config.getPrefix()), SysOssConfig::getPrefix, "") .set(ObjectUtil.isNull(config.getRegion()), SysOssConfig::getRegion, "") @@ -140,7 +143,12 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { if (flag) { // 从数据库查询完整的数据做缓存 config = ossConfigMapper.selectById(config.getOssConfigId()); + if (ObjectUtil.isNotNull(oldConfig) && !Objects.equals(oldConfig.getConfigKey(), config.getConfigKey())) { + CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, oldConfig.getConfigKey()); + OssFactory.remove(oldConfig.getConfigKey()); + } CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); + OssFactory.remove(config.getConfigKey()); } return flag; } @@ -174,12 +182,16 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { List list = CollUtil.newArrayList(); for (Long configId : ids) { SysOssConfig config = ossConfigMapper.selectById(configId); - list.add(config); + if (ObjectUtil.isNotNull(config)) { + list.add(config); + } } boolean flag = ossConfigMapper.deleteByIds(ids) > 0; if (flag) { - list.forEach(sysOssConfig -> - CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey())); + list.forEach(sysOssConfig -> { + CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey()); + OssFactory.remove(sysOssConfig.getConfigKey()); + }); } return flag; } From 3cea6f294f3e0c1ca431af4eb03fe78bec66d9bf 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: Sat, 16 May 2026 15:11:34 +0800 Subject: [PATCH 23/35] =?UTF-8?q?refactor(common-push):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E6=B6=88=E6=81=AF=E6=8E=A8=E9=80=81=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E4=B8=8E=E8=BF=9E=E6=8E=A5=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修正 SSE/WebSocket 自动装配条件,message.enabled=false 时不再注册推送组件 - 将 SSE 超时、心跳间隔、WebSocket 发送超时和缓冲区大小下沉到配置项 - 将推送会话管理器改为构造器注入定时线程池,减少 SpringUtils 服务定位 - WebSocket 传输异常时主动移除并关闭会话,避免异常连接残留 - WebSocket 握手改为从 ServerHttpRequest 读取客户端标识,降低线程上下文依赖 - 补充 Redis topic 空消息防御,避免异常消息触发 NPE - PushDTO 增加静态工厂方法,简化推送参数构建 --- .../src/main/resources/application.yml | 8 ++++ .../push/config/MessageSseConfiguration.java | 12 ++++-- .../config/MessageWebSocketConfiguration.java | 16 +++++--- .../push/core/SseEmitterSessionManager.java | 35 ++++++++++++------ .../push/core/WebSocketSessionManager.java | 37 ++++++++++++++----- .../org/dromara/common/push/dto/PushDTO.java | 24 ++++++++++++ .../push/handler/PlusWebSocketHandler.java | 25 ++++++++++--- .../common/push/helper/PushHelper.java | 5 +-- .../interceptor/PlusWebSocketInterceptor.java | 13 ++++--- .../push/listener/MessageTopicListener.java | 8 ++-- .../push/properties/MessageProperties.java | 22 ++++++++++- 11 files changed, 155 insertions(+), 50 deletions(-) diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index b8848f036..015f6a284 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -220,6 +220,14 @@ message: path: /resource/message # websocket 允许的跨域来源 allowedOrigins: '*' + # SSE 连接超时时间,单位毫秒 + sse-timeout: 86400000 + # 本地连接心跳检测间隔,单位秒 + heartbeat-interval: 60 + # WebSocket 单次发送超时时间,单位毫秒 + web-socket-send-time-limit: 10000 + # WebSocket 发送缓冲区大小 + web-socket-buffer-size-limit: 64000 --- # warm-flow工作流配置 warm-flow: diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/config/MessageSseConfiguration.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/config/MessageSseConfiguration.java index 2f14eb893..c67ec5d96 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/config/MessageSseConfiguration.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/config/MessageSseConfiguration.java @@ -3,17 +3,20 @@ package org.dromara.common.push.config; import org.dromara.common.push.controller.SseController; import org.dromara.common.push.core.SseEmitterSessionManager; import org.dromara.common.push.listener.MessageTopicListener; +import org.dromara.common.push.properties.MessageProperties; import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; +import java.util.concurrent.ScheduledExecutorService; + /** * SSE 消息推送自动装配。 * * @author Lion Li */ @AutoConfiguration(after = MessageAutoConfiguration.class) -@ConditionalOnProperty(prefix = "message", name = "transport", havingValue = "sse", matchIfMissing = true) +@ConditionalOnExpression("'${message.enabled:true}'.equalsIgnoreCase('true') && '${message.transport:sse}'.equalsIgnoreCase('sse')") public class MessageSseConfiguration { /** @@ -23,8 +26,9 @@ public class MessageSseConfiguration { * @return SseEmitterSessionManager 实例 */ @Bean - public SseEmitterSessionManager sseEmitterManager() { - return new SseEmitterSessionManager(); + public SseEmitterSessionManager sseEmitterManager(ScheduledExecutorService scheduledExecutorService, + MessageProperties messageProperties) { + return new SseEmitterSessionManager(scheduledExecutorService, messageProperties); } /** diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/config/MessageWebSocketConfiguration.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/config/MessageWebSocketConfiguration.java index b4bbd30eb..8b17eebfc 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/config/MessageWebSocketConfiguration.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/config/MessageWebSocketConfiguration.java @@ -6,13 +6,15 @@ import org.dromara.common.push.handler.PlusWebSocketHandler; import org.dromara.common.push.interceptor.PlusWebSocketInterceptor; import org.dromara.common.push.properties.MessageProperties; import org.springframework.boot.autoconfigure.AutoConfiguration; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.context.annotation.Bean; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.server.HandshakeInterceptor; +import java.util.concurrent.ScheduledExecutorService; + /** * WebSocket 消息推送自动装配。 * @@ -20,7 +22,7 @@ import org.springframework.web.socket.server.HandshakeInterceptor; */ @EnableWebSocket @AutoConfiguration(after = MessageAutoConfiguration.class) -@ConditionalOnProperty(prefix = "message", name = "transport", havingValue = "websocket") +@ConditionalOnExpression("'${message.enabled:true}'.equalsIgnoreCase('true') && '${message.transport:sse}'.equalsIgnoreCase('websocket')") public class MessageWebSocketConfiguration { /** @@ -42,8 +44,9 @@ public class MessageWebSocketConfiguration { * 负责连接管理、消息发送、定时清理失效会话 */ @Bean - public WebSocketSessionManager webSocketSessionManager() { - return new WebSocketSessionManager(); + public WebSocketSessionManager webSocketSessionManager(ScheduledExecutorService scheduledExecutorService, + MessageProperties messageProperties) { + return new WebSocketSessionManager(scheduledExecutorService, messageProperties); } /** @@ -60,8 +63,9 @@ public class MessageWebSocketConfiguration { * 处理连接、消息、心跳、断开、异常等事件 */ @Bean - public WebSocketHandler webSocketHandler(WebSocketSessionManager webSocketSessionManager) { - return new PlusWebSocketHandler(webSocketSessionManager); + public WebSocketHandler webSocketHandler(WebSocketSessionManager webSocketSessionManager, + MessageProperties messageProperties) { + return new PlusWebSocketHandler(webSocketSessionManager, messageProperties); } /** diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java index 57d5e8eb8..c7f36fb85 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java @@ -3,10 +3,10 @@ package org.dromara.common.push.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.push.constant.MessageConstants; import org.dromara.common.push.dto.PushDTO; +import org.dromara.common.push.properties.MessageProperties; import org.dromara.common.redis.utils.RedisUtils; import org.dromara.system.api.domain.PushPayloadDTO; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; @@ -30,10 +30,17 @@ public class SseEmitterSessionManager implements PushSessionManager { private final static Map> USER_TOKEN_EMITTERS = new ConcurrentHashMap<>(); - public SseEmitterSessionManager() { + private final MessageProperties messageProperties; + + public SseEmitterSessionManager(ScheduledExecutorService scheduledExecutorService, MessageProperties messageProperties) { + this.messageProperties = messageProperties; // 定时执行 SSE 心跳检测 - SpringUtils.getBean(ScheduledExecutorService.class) - .scheduleWithFixedDelay(this::sseMonitor, 60L, 60L, TimeUnit.SECONDS); + scheduledExecutorService.scheduleWithFixedDelay( + this::sseMonitor, + messageProperties.getHeartbeatInterval(), + messageProperties.getHeartbeatInterval(), + TimeUnit.SECONDS + ); } /** @@ -54,8 +61,8 @@ public class SseEmitterSessionManager implements PushSessionManager { oldEmitter.complete(); } - // 创建一个新的 SseEmitter 实例,超时时间设置为一天 避免连接之后直接关闭浏览器导致连接停滞 - SseEmitter emitter = new SseEmitter(86400000L); + // 创建一个新的 SseEmitter 实例,避免连接之后直接关闭浏览器导致连接停滞 + SseEmitter emitter = new SseEmitter(messageProperties.getSseTimeout()); emitters.put(token, emitter); @@ -195,6 +202,9 @@ public class SseEmitterSessionManager implements PushSessionManager { */ @Override public void sendMessage(Long userId, PushPayloadDTO payload) { + if (payload == null) { + return; + } sendMessage(userId, JsonUtils.toJsonString(payload)); } @@ -229,6 +239,9 @@ public class SseEmitterSessionManager implements PushSessionManager { */ @Override public void sendMessage(PushPayloadDTO payload) { + if (payload == null) { + return; + } sendMessage(JsonUtils.toJsonString(payload)); } @@ -239,6 +252,9 @@ public class SseEmitterSessionManager implements PushSessionManager { */ @Override public void publishMessage(PushDTO pushDTO) { + if (pushDTO == null || pushDTO.getPayload() == null) { + return; + } RedisUtils.publish(MessageConstants.MESSAGE_TOPIC, pushDTO, consumer -> log.info( "发送主题订阅消息topic:{} userIds:{} message:{}", MessageConstants.MESSAGE_TOPIC, @@ -263,11 +279,6 @@ public class SseEmitterSessionManager implements PushSessionManager { */ @Override public void publishAll(PushPayloadDTO payload) { - PushDTO dto = new PushDTO(); - dto.setPayload(payload); - RedisUtils.publish(MessageConstants.MESSAGE_TOPIC, dto, consumer -> { - log.info("发送主题订阅消息topic:{} type:{} source:{} message:{}", - MessageConstants.MESSAGE_TOPIC, payload.getType(), payload.getSource(), payload.getMessage()); - }); + publishMessage(PushDTO.broadcast(payload)); } } diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java index 435fbf8d7..4061aad6e 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java @@ -3,9 +3,9 @@ package org.dromara.common.push.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.push.dto.PushDTO; +import org.dromara.common.push.properties.MessageProperties; import org.dromara.common.redis.utils.RedisUtils; import org.dromara.system.api.domain.PushPayloadDTO; import org.springframework.web.socket.*; @@ -40,9 +40,13 @@ public class WebSocketSessionManager implements PushSessionManager { * 构造函数 * 初始化定时任务:每60秒执行一次会话监控,自动清理无效连接 */ - public WebSocketSessionManager() { - SpringUtils.getBean(ScheduledExecutorService.class) - .scheduleWithFixedDelay(this::sessionMonitor, 60L, 60L, TimeUnit.SECONDS); + public WebSocketSessionManager(ScheduledExecutorService scheduledExecutorService, MessageProperties messageProperties) { + scheduledExecutorService.scheduleWithFixedDelay( + this::sessionMonitor, + messageProperties.getHeartbeatInterval(), + messageProperties.getHeartbeatInterval(), + TimeUnit.SECONDS + ); } /** @@ -68,6 +72,17 @@ public class WebSocketSessionManager implements PushSessionManager { * @param token 客户端唯一标识 */ public void disconnect(Long userId, String token) { + disconnect(userId, token, CloseStatus.NORMAL); + } + + /** + * 用户断开WebSocket连接 + * + * @param userId 用户ID + * @param token 客户端唯一标识 + * @param status 关闭状态码 + */ + public void disconnect(Long userId, String token, CloseStatus status) { if (userId == null || token == null) { return; } @@ -77,7 +92,7 @@ public class WebSocketSessionManager implements PushSessionManager { return; } // 移除指定token会话并关闭 - closeSession(sessions.remove(token), CloseStatus.NORMAL); + closeSession(sessions.remove(token), status); // 该用户无任何会话时,从缓存中移除 if (sessions.isEmpty()) { USER_TOKEN_SESSIONS.remove(userId); @@ -163,6 +178,9 @@ public class WebSocketSessionManager implements PushSessionManager { */ @Override public void sendMessage(PushPayloadDTO payload) { + if (payload == null) { + return; + } USER_TOKEN_SESSIONS.keySet().forEach(userId -> sendMessage(userId, payload)); } @@ -174,6 +192,9 @@ public class WebSocketSessionManager implements PushSessionManager { */ @Override public void publishMessage(PushDTO pushDTO) { + if (pushDTO == null || pushDTO.getPayload() == null) { + return; + } RedisUtils.publish(MESSAGE_TOPIC, pushDTO, consumer -> log.info( "WebSocket发送主题订阅消息topic:{} userIds:{} message:{}", MESSAGE_TOPIC, @@ -189,9 +210,7 @@ public class WebSocketSessionManager implements PushSessionManager { */ @Override public void publishAll(PushPayloadDTO payload) { - PushDTO dto = new PushDTO(); - dto.setPayload(payload); - publishMessage(dto); + publishMessage(PushDTO.broadcast(payload)); } /** @@ -241,7 +260,7 @@ public class WebSocketSessionManager implements PushSessionManager { * @param session 待关闭的会话 * @param status 关闭状态码 */ - private void closeSession(WebSocketSession session, CloseStatus status) { + public void closeSession(WebSocketSession session, CloseStatus status) { if (session == null) { return; } diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/dto/PushDTO.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/dto/PushDTO.java index e44df6f27..ff81f184c 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/dto/PushDTO.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/dto/PushDTO.java @@ -27,4 +27,28 @@ public class PushDTO implements Serializable { * 推送消息体。 */ private PushPayloadDTO payload; + + /** + * 构建指定用户推送消息。 + * + * @param userIds 目标用户 ID 列表 + * @param payload 推送消息体 + * @return 推送 DTO + */ + public static PushDTO of(List userIds, PushPayloadDTO payload) { + PushDTO dto = new PushDTO(); + dto.setUserIds(userIds); + dto.setPayload(payload); + return dto; + } + + /** + * 构建广播推送消息。 + * + * @param payload 推送消息体 + * @return 推送 DTO + */ + public static PushDTO broadcast(PushPayloadDTO payload) { + return of(null, payload); + } } diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/handler/PlusWebSocketHandler.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/handler/PlusWebSocketHandler.java index 7c340903c..3fb38be99 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/handler/PlusWebSocketHandler.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/handler/PlusWebSocketHandler.java @@ -9,6 +9,7 @@ import org.dromara.common.core.utils.StringUtils; import org.dromara.common.push.constant.MessageConstants; import org.dromara.common.push.core.WebSocketSessionManager; import org.dromara.common.push.dto.PushDTO; +import org.dromara.common.push.properties.MessageProperties; import org.dromara.system.api.domain.PushPayloadDTO; import org.dromara.system.api.model.LoginUser; import org.springframework.web.socket.*; @@ -33,6 +34,11 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler { */ private final WebSocketSessionManager webSocketSessionManager; + /** + * 消息推送配置 + */ + private final MessageProperties messageProperties; + /** * 建立WebSocket连接后触发 * 校验用户登录信息,注册会话 @@ -56,7 +62,11 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler { webSocketSessionManager.connect( loginUser.getUserId(), token, - new ConcurrentWebSocketSessionDecorator(session, 10 * 1000, 64_000) + new ConcurrentWebSocketSessionDecorator( + session, + messageProperties.getWebSocketSendTimeLimit(), + messageProperties.getWebSocketBufferSizeLimit() + ) ); log.info("[connect] sessionId: {}, userId:{}, token:***{}", session.getId(), loginUser.getUserId(), StringUtils.right(token, 8)); } @@ -82,9 +92,7 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler { } // 构建客户端自定义消息并发布 - PushDTO dto = new PushDTO(); - dto.setUserIds(List.of(loginUser.getUserId())); - dto.setPayload(PushPayloadDTO.of( + PushDTO dto = PushDTO.of(List.of(loginUser.getUserId()), PushPayloadDTO.of( PushTypeEnum.CUSTOM, PushSourceEnum.CLIENT, message.getPayload(), @@ -116,7 +124,14 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler { */ @Override public void handleTransportError(WebSocketSession session, Throwable exception) { - log.error("[transport error] sessionId: {}, exception:{}", session.getId(), exception.getMessage()); + log.error("[transport error] sessionId: {}, exception:{}", session.getId(), exception.getMessage(), exception); + LoginUser loginUser = (LoginUser) session.getAttributes().get(MessageConstants.LOGIN_USER_KEY); + String token = (String) session.getAttributes().get(MessageConstants.LOGIN_TOKEN_KEY); + if (ObjectUtil.hasNull(loginUser, token)) { + webSocketSessionManager.closeSession(session, CloseStatus.SERVER_ERROR); + return; + } + webSocketSessionManager.disconnect(loginUser.getUserId(), token, CloseStatus.SERVER_ERROR); } /** diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/helper/PushHelper.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/helper/PushHelper.java index 51f78f673..9ca621832 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/helper/PushHelper.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/helper/PushHelper.java @@ -70,10 +70,7 @@ public class PushHelper { * @param payload 消息推送体 */ public static void publishMessage(List userIds, PushPayloadDTO payload) { - PushDTO dto = new PushDTO(); - dto.setUserIds(userIds); - dto.setPayload(payload); - publishMessage(dto); + publishMessage(PushDTO.of(userIds, payload)); } /** diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/interceptor/PlusWebSocketInterceptor.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/interceptor/PlusWebSocketInterceptor.java index b53a537c0..4bc1461e5 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/interceptor/PlusWebSocketInterceptor.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/interceptor/PlusWebSocketInterceptor.java @@ -3,7 +3,6 @@ package org.dromara.common.push.interceptor; import cn.dev33.satoken.exception.NotLoginException; import cn.dev33.satoken.stp.StpUtil; import lombok.extern.slf4j.Slf4j; -import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.push.constant.MessageConstants; import org.dromara.common.satoken.utils.LoginHelper; @@ -12,6 +11,7 @@ import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.HandshakeInterceptor; +import org.springframework.web.util.UriComponentsBuilder; import java.util.Map; @@ -44,12 +44,15 @@ public class PlusWebSocketInterceptor implements HandshakeInterceptor { } // 3. 校验客户端ID(防止多端冒用) - String headerCid = ServletUtils.getRequest().getHeader(LoginHelper.CLIENT_KEY); - String paramCid = ServletUtils.getParameter(LoginHelper.CLIENT_KEY); - String clientId = StpUtil.getExtra(LoginHelper.CLIENT_KEY).toString(); + String headerCid = request.getHeaders().getFirst(LoginHelper.CLIENT_KEY); + String paramCid = UriComponentsBuilder.fromUri(request.getURI()) + .build() + .getQueryParams() + .getFirst(LoginHelper.CLIENT_KEY); + Object clientExtra = StpUtil.getExtra(LoginHelper.CLIENT_KEY); // 客户端ID必须与请求头/参数中的一致,否则拒绝连接 - if (!StringUtils.equalsAny(clientId, headerCid, paramCid)) { + if (clientExtra == null || !StringUtils.equalsAny(clientExtra.toString(), headerCid, paramCid)) { throw NotLoginException.newInstance(StpUtil.getLoginType(), "-100", "客户端ID与Token不匹配", StpUtil.getTokenValue()); diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/listener/MessageTopicListener.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/listener/MessageTopicListener.java index a0cb451a4..9eb25c83d 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/listener/MessageTopicListener.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/listener/MessageTopicListener.java @@ -32,12 +32,12 @@ public class MessageTopicListener implements ApplicationRunner, Ordered { public void run(ApplicationArguments args) { // 订阅消息主题,处理消息分发 pushSessionManager.subscribeMessage(message -> { - log.info("消息主题订阅收到消息userIds={} message={}", - message.getUserIds(), - message.getPayload() == null ? null : message.getPayload().getMessage()); - if (message.getPayload() == null) { + if (message == null || message.getPayload() == null) { return; } + log.info("消息主题订阅收到消息userIds={} message={}", + message.getUserIds(), + message.getPayload().getMessage()); // 有指定用户 -> 单发 if (CollUtil.isNotEmpty(message.getUserIds())) { message.getUserIds().forEach(userId -> pushSessionManager.sendMessage(userId, message.getPayload())); diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/properties/MessageProperties.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/properties/MessageProperties.java index f444123ef..7d0aeaebf 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/properties/MessageProperties.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/properties/MessageProperties.java @@ -31,5 +31,25 @@ public class MessageProperties { /** * WebSocket 允许的跨域来源。 */ - private String allowedOrigins = "*"; + private String[] allowedOrigins = {"*"}; + + /** + * SSE 连接超时时间,单位毫秒。 + */ + private long sseTimeout = 86_400_000L; + + /** + * 本地连接心跳检测间隔,单位秒。 + */ + private long heartbeatInterval = 60L; + + /** + * WebSocket 单次发送超时时间,单位毫秒。 + */ + private int webSocketSendTimeLimit = 10_000; + + /** + * WebSocket 发送缓冲区大小。 + */ + private int webSocketBufferSizeLimit = 64_000; } From 3ca1e6d45d8196fccda0d285e14ff2273124e25c 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: Sat, 16 May 2026 15:46:34 +0800 Subject: [PATCH 24/35] =?UTF-8?q?refactor(common-redis):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20Redis=20=E5=85=AC=E5=85=B1=E8=83=BD=E5=8A=9B?= =?UTF-8?q?=E4=B8=8E=E7=BC=93=E5=AD=98=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复 RedisUtils TTL 保留兼容逻辑,补充订阅取消能力与空值防御 - CacheUtils 增加缓存存在性校验,避免空缓存链路 NPE - PlusSpringCacheManager 按完整 cacheName 缓存实例,避免不同参数配置被静默覆盖 - 补充分布式锁/重复提交相关异常与空 key 处理 --- .../redis/aspectj/RepeatSubmitAspect.java | 16 +++- .../common/redis/config/RedisConfig.java | 1 + .../config/properties/RedissonProperties.java | 4 +- .../redis/manager/CaffeineCacheDecorator.java | 36 +++++---- .../redis/manager/PlusSpringCacheManager.java | 79 +++++++++++++++---- .../common/redis/utils/CacheUtils.java | 22 ++++-- .../common/redis/utils/RedisUtils.java | 41 ++++++++-- 7 files changed, 153 insertions(+), 46 deletions(-) diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java index 462f16530..45ac5bbcc 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java @@ -83,7 +83,7 @@ public class RepeatSubmitAspect { if (r.getCode() == HttpStatus.SUCCESS) { return; } - RedisUtils.deleteObject(KEY_CACHE.get()); + deleteRepeatKey(); } } finally { KEY_CACHE.remove(); @@ -98,8 +98,18 @@ public class RepeatSubmitAspect { */ @AfterThrowing(value = "@annotation(repeatSubmit)", throwing = "e") public void doAfterThrowing(JoinPoint joinPoint, RepeatSubmit repeatSubmit, Exception e) { - RedisUtils.deleteObject(KEY_CACHE.get()); - KEY_CACHE.remove(); + try { + deleteRepeatKey(); + } finally { + KEY_CACHE.remove(); + } + } + + private void deleteRepeatKey() { + String key = KEY_CACHE.get(); + if (StringUtils.isNotBlank(key)) { + RedisUtils.deleteObject(key); + } } /** diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java index c016e166f..00bca0902 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java @@ -24,6 +24,7 @@ import tools.jackson.databind.module.SimpleModule; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; +import java.util.List; import java.util.TimeZone; /** diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java index ebec7861a..d04fb64d5 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java @@ -23,12 +23,12 @@ public class RedissonProperties { /** * 线程池数量,默认值 = 当前处理核数量 * 2 */ - private int threads; + private int threads = Runtime.getRuntime().availableProcessors() * 2; /** * Netty线程池数量,默认值 = 当前处理核数量 * 2 */ - private int nettyThreads; + private int nettyThreads = Runtime.getRuntime().availableProcessors() * 2; /** * 单机服务配置 diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java index 884a92ac4..8368aa3c7 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/CaffeineCacheDecorator.java @@ -1,6 +1,5 @@ package org.dromara.common.redis.manager; -import org.dromara.common.core.utils.SpringUtils; import org.springframework.cache.Cache; import java.util.concurrent.Callable; @@ -12,21 +11,21 @@ import java.util.concurrent.Callable; */ public class CaffeineCacheDecorator implements Cache { - private static final com.github.benmanes.caffeine.cache.Cache - CAFFEINE = SpringUtils.getBean("caffeine"); - private final String name; private final Cache cache; + private final com.github.benmanes.caffeine.cache.Cache caffeine; /** * 创建带 Caffeine 一级缓存的缓存装饰器。 * * @param name 缓存名称 - * @param cache 被装饰的缓存实例 + * @param cache 被装饰的缓存实例 + * @param caffeine 本地一级缓存实例 */ - public CaffeineCacheDecorator(String name, Cache cache) { + public CaffeineCacheDecorator(String name, Cache cache, com.github.benmanes.caffeine.cache.Cache caffeine) { this.name = name; this.cache = cache; + this.caffeine = caffeine; } /** @@ -67,7 +66,7 @@ public class CaffeineCacheDecorator implements Cache { */ @Override public ValueWrapper get(Object key) { - Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key)); + Object o = caffeine.get(getUniqueKey(key), k -> cache.get(key)); return (ValueWrapper) o; } @@ -81,7 +80,7 @@ public class CaffeineCacheDecorator implements Cache { @SuppressWarnings("unchecked") @Override public T get(Object key, Class type) { - Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, type)); + Object o = caffeine.get(getUniqueKey(key), k -> cache.get(key, type)); return (T) o; } @@ -93,7 +92,7 @@ public class CaffeineCacheDecorator implements Cache { */ @Override public void put(Object key, Object value) { - CAFFEINE.invalidate(getUniqueKey(key)); + caffeine.invalidate(getUniqueKey(key)); cache.put(key, value); } @@ -106,7 +105,7 @@ public class CaffeineCacheDecorator implements Cache { */ @Override public ValueWrapper putIfAbsent(Object key, Object value) { - CAFFEINE.invalidate(getUniqueKey(key)); + caffeine.invalidate(getUniqueKey(key)); return cache.putIfAbsent(key, value); } @@ -130,7 +129,7 @@ public class CaffeineCacheDecorator implements Cache { public boolean evictIfPresent(Object key) { boolean b = cache.evictIfPresent(key); if (b) { - CAFFEINE.invalidate(getUniqueKey(key)); + caffeine.invalidate(getUniqueKey(key)); } return b; } @@ -140,7 +139,7 @@ public class CaffeineCacheDecorator implements Cache { */ @Override public void clear() { - CAFFEINE.invalidateAll(); + clearLocalCache(); cache.clear(); } @@ -151,7 +150,11 @@ public class CaffeineCacheDecorator implements Cache { */ @Override public boolean invalidate() { - return cache.invalidate(); + boolean invalidated = cache.invalidate(); + if (invalidated) { + clearLocalCache(); + } + return invalidated; } /** @@ -164,8 +167,13 @@ public class CaffeineCacheDecorator implements Cache { @SuppressWarnings("unchecked") @Override public T get(Object key, Callable valueLoader) { - Object o = CAFFEINE.get(getUniqueKey(key), k -> cache.get(key, valueLoader)); + Object o = caffeine.get(getUniqueKey(key), k -> cache.get(key, valueLoader)); return (T) o; } + private void clearLocalCache() { + String prefix = name + ":"; + caffeine.asMap().keySet().removeIf(key -> key instanceof String cacheKey && cacheKey.startsWith(prefix)); + } + } diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java index 1a3939d5a..7e02a7dd2 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/manager/PlusSpringCacheManager.java @@ -55,10 +55,22 @@ public class PlusSpringCacheManager implements CacheManager { Map configMap = new ConcurrentHashMap<>(); ConcurrentMap instanceMap = new ConcurrentHashMap<>(); + private final com.github.benmanes.caffeine.cache.Cache caffeine; + /** * 创建基于 Redisson 的缓存管理器。 */ public PlusSpringCacheManager() { + this(null); + } + + /** + * 创建基于 Redisson 的缓存管理器。 + * + * @param caffeine 本地一级缓存实例 + */ + public PlusSpringCacheManager(com.github.benmanes.caffeine.cache.Cache caffeine) { + this.caffeine = caffeine; } @@ -110,7 +122,11 @@ public class PlusSpringCacheManager implements CacheManager { * @param config object */ public void setConfig(Map config) { - this.configMap = (Map) config; + if (config == null) { + this.configMap = new ConcurrentHashMap<>(); + return; + } + this.configMap = new ConcurrentHashMap<>((Map) config); } /** @@ -130,11 +146,12 @@ public class PlusSpringCacheManager implements CacheManager { */ @Override public Cache getCache(String name) { + String cacheName = name; // 重写 cacheName 支持多参数 String[] array = StringUtils.delimitedListToStringArray(name, "#"); name = array[0]; - Cache cache = instanceMap.get(name); + Cache cache = instanceMap.get(cacheName); if (cache != null) { return cache; } @@ -142,10 +159,28 @@ public class PlusSpringCacheManager implements CacheManager { return cache; } - CacheConfig config = configMap.get(name); + CacheConfig config = resolveCacheConfig(cacheName, name, array); + + int local = resolveLocal(array); + if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { + return createMap(cacheName, name, config, local); + } + + return createMapCache(cacheName, name, config, local); + } + + private CacheConfig resolveCacheConfig(String cacheName, String name, String[] array) { + CacheConfig config = configMap.get(cacheName); + if (config != null) { + return config; + } + + CacheConfig template = configMap.get(name); + if (template != null) { + config = copyConfig(template); + } if (config == null) { config = createDefaultConfig(); - configMap.put(name, config); } if (array.length > 1) { @@ -157,16 +192,28 @@ public class PlusSpringCacheManager implements CacheManager { if (array.length > 3) { config.setMaxSize(Integer.parseInt(array[3])); } + configMap.put(cacheName, config); + return config; + } + + private int resolveLocal(String[] array) { int local = 1; if (array.length > 4) { local = Integer.parseInt(array[4]); } + return local; + } - if (config.getMaxIdleTime() == 0 && config.getTTL() == 0 && config.getMaxSize() == 0) { - return createMap(name, config, local); + private CacheConfig copyConfig(CacheConfig source) { + CacheConfig target = new CacheConfig(); + target.setTTL(source.getTTL()); + target.setMaxIdleTime(source.getMaxIdleTime()); + target.setMaxSize(source.getMaxSize()); + target.setEvictionMode(source.getEvictionMode()); + for (MapEntryListener listener : source.getListeners()) { + target.addListener(listener); } - - return createMapCache(name, config, local); + return target; } /** @@ -177,17 +224,17 @@ public class PlusSpringCacheManager implements CacheManager { * @param local 是否启用本地一级缓存 * @return 缓存实例 */ - private Cache createMap(String name, CacheConfig config, int local) { + private Cache createMap(String cacheName, String name, CacheConfig config, int local) { RMap map = RedisUtils.getClient().getMap(name); Cache cache = new RedissonCache(map, allowNullValues); - if (local == 1) { - cache = new CaffeineCacheDecorator(name, cache); + if (local == 1 && caffeine != null) { + cache = new CaffeineCacheDecorator(cacheName, cache, caffeine); } if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } - Cache oldCache = instanceMap.putIfAbsent(name, cache); + Cache oldCache = instanceMap.putIfAbsent(cacheName, cache); if (oldCache != null) { cache = oldCache; } @@ -202,17 +249,17 @@ public class PlusSpringCacheManager implements CacheManager { * @param local 是否启用本地一级缓存 * @return 缓存实例 */ - private Cache createMapCache(String name, CacheConfig config, int local) { + private Cache createMapCache(String cacheName, String name, CacheConfig config, int local) { RMapCache map = RedisUtils.getClient().getMapCache(name); Cache cache = new RedissonCache(map, config, allowNullValues); - if (local == 1) { - cache = new CaffeineCacheDecorator(name, cache); + if (local == 1 && caffeine != null) { + cache = new CaffeineCacheDecorator(cacheName, cache, caffeine); } if (transactionAware) { cache = new TransactionAwareCacheDecorator(cache); } - Cache oldCache = instanceMap.putIfAbsent(name, cache); + Cache oldCache = instanceMap.putIfAbsent(cacheName, cache); if (oldCache != null) { cache = oldCache; } else { diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java index 28e67d5df..6f7dfe140 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/CacheUtils.java @@ -15,8 +15,6 @@ import org.springframework.cache.CacheManager; @SuppressWarnings(value = {"unchecked"}) public class CacheUtils { - private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class); - /** * 获取缓存值 * @@ -25,7 +23,7 @@ public class CacheUtils { * @return 缓存值 */ public static T get(String cacheNames, Object key) { - Cache.ValueWrapper wrapper = CACHE_MANAGER.getCache(cacheNames).get(key); + Cache.ValueWrapper wrapper = getRequiredCache(cacheNames).get(key); return wrapper != null ? (T) wrapper.get() : null; } @@ -37,7 +35,7 @@ public class CacheUtils { * @param value 缓存值 */ public static void put(String cacheNames, Object key, Object value) { - CACHE_MANAGER.getCache(cacheNames).put(key, value); + getRequiredCache(cacheNames).put(key, value); } /** @@ -47,7 +45,7 @@ public class CacheUtils { * @param key 缓存key */ public static void evict(String cacheNames, Object key) { - CACHE_MANAGER.getCache(cacheNames).evict(key); + getRequiredCache(cacheNames).evict(key); } /** @@ -56,7 +54,19 @@ public class CacheUtils { * @param cacheNames 缓存组名称 */ public static void clear(String cacheNames) { - CACHE_MANAGER.getCache(cacheNames).clear(); + getRequiredCache(cacheNames).clear(); + } + + private static Cache getRequiredCache(String cacheNames) { + Cache cache = CacheManagerHolder.CACHE_MANAGER.getCache(cacheNames); + if (cache == null) { + throw new IllegalArgumentException("Cache '" + cacheNames + "' does not exist."); + } + return cache; + } + + private static class CacheManagerHolder { + private static final CacheManager CACHE_MANAGER = SpringUtils.getBean(CacheManager.class); } } diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java index b3fd462a2..abb4a9d0f 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/utils/RedisUtils.java @@ -77,7 +77,9 @@ public class RedisUtils { public static void publish(String channelKey, T msg, Consumer consumer) { RTopic topic = CLIENT.getTopic(channelKey); topic.publish(msg); - consumer.accept(msg); + if (consumer != null) { + consumer.accept(msg); + } } /** @@ -99,8 +101,31 @@ public class RedisUtils { * @param consumer 自定义处理 */ public static void subscribe(String channelKey, Class clazz, Consumer consumer) { + subscribeAndGetListenerId(channelKey, clazz, consumer); + } + + /** + * 订阅通道接收消息,并返回监听器 ID。 + * + * @param channelKey 通道key + * @param clazz 消息类型 + * @param consumer 自定义处理 + * @return 监听器 ID,可用于取消订阅 + */ + public static int subscribeAndGetListenerId(String channelKey, Class clazz, Consumer consumer) { RTopic topic = CLIENT.getTopic(channelKey); - topic.addListener(clazz, (channel, msg) -> consumer.accept(msg)); + return topic.addListener(clazz, (channel, msg) -> consumer.accept(msg)); + } + + /** + * 取消通道订阅。 + * + * @param channelKey 通道key + * @param listenerId 监听器 ID + */ + public static void unsubscribe(String channelKey, int listenerId) { + RTopic topic = CLIENT.getTopic(channelKey); + topic.removeListener(listenerId); } /** @@ -128,7 +153,7 @@ public class RedisUtils { bucket.setAndKeepTTL(value); } catch (Exception e) { long timeToLive = bucket.remainTimeToLive(); - if (timeToLive == -1) { + if (timeToLive <= 0) { bucket.set(value); } else { bucket.set(value, Duration.ofMillis(timeToLive)); @@ -239,6 +264,9 @@ public class RedisUtils { * @param key 缓存的键值 */ public static boolean deleteObject(final String key) { + if (key == null) { + return false; + } return CLIENT.getBucket(key).delete(); } @@ -247,7 +275,10 @@ public class RedisUtils { * * @param collection 多个对象 */ - public static void deleteObject(final Collection collection) { + public static void deleteObject(final Collection collection) { + if (collection == null || collection.isEmpty()) { + return; + } RBatch batch = CLIENT.createBatch(); collection.forEach(t -> { batch.getBucket(t.toString()).deleteAsync(); @@ -407,7 +438,7 @@ public class RedisUtils { */ public static Map getCacheMap(final String key) { RMap rMap = CLIENT.getMap(key); - return rMap.getAll(rMap.keySet()); + return rMap.readAllMap(); } /** From 63cd82f4a8d0f55b9045e4ed4ce114a6a77a9e5d 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: Sat, 16 May 2026 15:58:41 +0800 Subject: [PATCH 25/35] =?UTF-8?q?refactor(common-satoken):=20=E6=94=B6?= =?UTF-8?q?=E6=95=9B=E7=99=BB=E5=BD=95=E5=8A=A9=E6=89=8B=E3=80=81=E6=9D=83?= =?UTF-8?q?=E9=99=90=E8=A7=A3=E6=9E=90=E4=B8=8E=20DAO=20=E7=BC=93=E5=AD=98?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - LoginHelper 改成更稳的空值/未登录处理,isLogin() 直接走 StpUtil.isLogin() - SaPermissionImpl 把菜单/角色权限提取收敛成一套逻辑 - PlusSaTokenDao 抽了公共写入、读取、TTL 转换逻辑,并修了 searchData 的本地缓存键 - SaTokenExceptionHandler 合并了角色/权限异常处理 --- .../satoken/core/dao/PlusSaTokenDao.java | 106 +++++++++++------- .../core/service/SaPermissionImpl.java | 73 ++++++------ .../handler/SaTokenExceptionHandler.java | 21 +--- .../common/satoken/utils/LoginHelper.java | 37 ++++-- 4 files changed, 133 insertions(+), 104 deletions(-) diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java index 09f82324e..ea19371be 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/dao/PlusSaTokenDao.java @@ -37,8 +37,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { */ @Override public String get(String key) { - Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key)); - return (String) o; + return getCacheValue(key); } /** @@ -46,16 +45,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { */ @Override public void set(String key, String value, long timeout) { - if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) { - return; - } - // 判断是否为永不过期 - if (timeout == NEVER_EXPIRE) { - RedisUtils.setCacheObject(key, value); - } else { - RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout)); - } - CAFFEINE.invalidate(key); + writeValue(key, value, timeout); } /** @@ -65,7 +55,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { public void update(String key, String value) { if (RedisUtils.hasKey(key)) { RedisUtils.setCacheObject(key, value, true); - CAFFEINE.invalidate(key); + invalidate(key); } } @@ -75,7 +65,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { @Override public void delete(String key) { if (RedisUtils.deleteObject(key)) { - CAFFEINE.invalidate(key); + invalidate(key); } } @@ -84,9 +74,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { */ @Override public long getTimeout(String key) { - long timeout = RedisUtils.getTimeToLive(key); - // 加1的目的 解决sa-token使用秒 redis是毫秒导致1秒的精度问题 手动补偿 - return timeout < 0 ? timeout : timeout / 1000 + 1; + return toTimeoutSeconds(RedisUtils.getTimeToLive(key)); } /** @@ -103,8 +91,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { */ @Override public Object getObject(String key) { - Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key)); - return o; + return getCacheValue(key); } /** @@ -113,11 +100,9 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { * @param key 键名称 * @return object */ - @SuppressWarnings("unchecked") @Override public T getObject(String key, Class classType) { - Object o = CAFFEINE.get(key, k -> RedisUtils.getCacheObject(key)); - return (T) o; + return classType.cast(getCacheValue(key)); } /** @@ -125,16 +110,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { */ @Override public void setObject(String key, Object object, long timeout) { - if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) { - return; - } - // 判断是否为永不过期 - if (timeout == NEVER_EXPIRE) { - RedisUtils.setCacheObject(key, object); - } else { - RedisUtils.setCacheObject(key, object, Duration.ofSeconds(timeout)); - } - CAFFEINE.invalidate(key); + writeValue(key, object, timeout); } /** @@ -144,7 +120,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { public void updateObject(String key, Object object) { if (RedisUtils.hasKey(key)) { RedisUtils.setCacheObject(key, object, true); - CAFFEINE.invalidate(key); + invalidate(key); } } @@ -154,7 +130,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { @Override public void deleteObject(String key) { if (RedisUtils.deleteObject(key)) { - CAFFEINE.invalidate(key); + invalidate(key); } } @@ -163,9 +139,7 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { */ @Override public long getObjectTimeout(String key) { - long timeout = RedisUtils.getTimeToLive(key); - // 加1的目的 解决sa-token使用秒 redis是毫秒导致1秒的精度问题 手动补偿 - return timeout < 0 ? timeout : timeout / 1000 + 1; + return toTimeoutSeconds(RedisUtils.getTimeToLive(key)); } /** @@ -182,11 +156,63 @@ public class PlusSaTokenDao implements SaTokenDaoBySessionFollowObject { @SuppressWarnings("unchecked") @Override public List searchData(String prefix, String keyword, int start, int size, boolean sortType) { - String keyStr = prefix + "*" + keyword + "*"; - return (List) CAFFEINE.get(keyStr, k -> { - Collection keys = RedisUtils.keys(keyStr); + String pattern = prefix + "*" + keyword + "*"; + String cacheKey = pattern + start + ":" + size + ":" + sortType; + return (List) CAFFEINE.get(cacheKey, k -> { + Collection keys = RedisUtils.keys(pattern); List list = new ArrayList<>(keys); return SaFoxUtil.searchList(list, start, size, sortType); }); } + + /** + * 从缓存读取对象。 + * + * @param key 缓存键 + * @return 缓存值 + */ + @SuppressWarnings("unchecked") + private T getCacheValue(String key) { + return (T) CAFFEINE.get(key, RedisUtils::getCacheObject); + } + + /** + * 写入缓存值并刷新本地缓存。 + * + * @param key 缓存键 + * @param value 缓存值 + * @param timeout 超时时间 + */ + private void writeValue(String key, Object value, long timeout) { + if (timeout == 0 || timeout <= NOT_VALUE_EXPIRE) { + return; + } + if (timeout == NEVER_EXPIRE) { + RedisUtils.setCacheObject(key, value); + } else { + RedisUtils.setCacheObject(key, value, Duration.ofSeconds(timeout)); + } + invalidate(key); + } + + /** + * 清除本地缓存。 + * + * @param key 缓存键 + */ + private void invalidate(String key) { + CAFFEINE.invalidate(key); + } + + /** + * 将 Redis TTL 转为秒。 + * + * @param timeoutRedis Redis TTL 毫秒值 + * @return Sa-Token 需要的秒值 + */ + private long toTimeoutSeconds(long timeoutRedis) { + // 加1的目的 解决sa-token使用秒 redis是毫秒导致1秒的精度问题 手动补偿 + return timeoutRedis < 0 ? timeoutRedis : timeoutRedis / 1000 + 1; + } + } diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java index 7b94b9c33..8423792ec 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/core/service/SaPermissionImpl.java @@ -3,16 +3,16 @@ package org.dromara.common.satoken.core.service; import cn.dev33.satoken.stp.StpInterface; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; -import org.dromara.common.core.enums.UserType; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.service.PermissionService; import org.dromara.common.core.utils.SpringUtils; -import org.dromara.common.core.utils.StringUtils; import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.system.api.model.LoginUser; +import java.util.Collection; import java.util.ArrayList; import java.util.List; +import java.util.function.BiFunction; /** * sa-token 权限管理实现类 @@ -30,26 +30,7 @@ public class SaPermissionImpl implements StpInterface { */ @Override public List getPermissionList(Object loginId, String loginType) { - LoginUser loginUser = LoginHelper.getLoginUser(); - if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) { - PermissionService permissionService = getPermissionService(); - if (ObjectUtil.isNotNull(permissionService)) { - List list = StringUtils.splitList(loginId.toString(), ":"); - return new ArrayList<>(permissionService.getMenuPermission(Long.parseLong(list.get(1)))); - } else { - throw new ServiceException("PermissionService 实现类不存在"); - } - } - UserType userType = UserType.getUserType(loginUser.getUserType()); - if (userType == UserType.APP_USER) { - // 其他端 自行根据业务编写 - } - if (CollUtil.isNotEmpty(loginUser.getMenuPermission())) { - // SYS_USER 默认返回权限 - return new ArrayList<>(loginUser.getMenuPermission()); - } else { - return new ArrayList<>(); - } + return resolvePermissionList(loginId, LoginUser::getMenuPermission, PermissionService::getMenuPermission); } /** @@ -61,26 +42,33 @@ public class SaPermissionImpl implements StpInterface { */ @Override public List getRoleList(Object loginId, String loginType) { + return resolvePermissionList(loginId, LoginUser::getRolePermission, PermissionService::getRolePermission); + } + + /** + * 解析当前登录对象的权限列表。 + * + * @param loginId 登录ID + * @param localPermissionExtractor 当前登录用户权限提取器 + * @param remotePermissionExtractor 远程权限提取器 + * @return 权限列表 + */ + private List resolvePermissionList(Object loginId, + java.util.function.Function> localPermissionExtractor, + BiFunction> remotePermissionExtractor) { LoginUser loginUser = LoginHelper.getLoginUser(); if (ObjectUtil.isNull(loginUser) || !loginUser.getLoginId().equals(loginId)) { PermissionService permissionService = getPermissionService(); if (ObjectUtil.isNotNull(permissionService)) { - List list = StringUtils.splitList(loginId.toString(), ":"); - return new ArrayList<>(permissionService.getRolePermission(Long.parseLong(list.get(1)))); - } else { - throw new ServiceException("PermissionService 实现类不存在"); + return new ArrayList<>(remotePermissionExtractor.apply(permissionService, resolveUserId(loginId))); } + throw new ServiceException("PermissionService 实现类不存在"); } - UserType userType = UserType.getUserType(loginUser.getUserType()); - if (userType == UserType.APP_USER) { - // 其他端 自行根据业务编写 - } - if (CollUtil.isNotEmpty(loginUser.getRolePermission())) { - // SYS_USER 默认返回权限 - return new ArrayList<>(loginUser.getRolePermission()); - } else { - return new ArrayList<>(); + Collection permissionList = localPermissionExtractor.apply(loginUser); + if (CollUtil.isNotEmpty(permissionList)) { + return new ArrayList<>(permissionList); } + return new ArrayList<>(); } /** @@ -96,4 +84,19 @@ public class SaPermissionImpl implements StpInterface { } } + /** + * 从登录ID中提取用户ID。 + * + * @param loginId 登录ID + * @return 用户ID + */ + private Long resolveUserId(Object loginId) { + String loginIdStr = loginId.toString(); + int separatorIndex = loginIdStr.indexOf(':'); + if (separatorIndex < 0 || separatorIndex == loginIdStr.length() - 1) { + throw new ServiceException("登录ID格式错误"); + } + return Long.parseLong(loginIdStr.substring(separatorIndex + 1)); + } + } diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java index 7710f9457..0a5ce1f51 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/handler/SaTokenExceptionHandler.java @@ -26,24 +26,11 @@ public class SaTokenExceptionHandler { * @param request 当前请求 * @return 统一失败响应 */ - @ExceptionHandler(NotPermissionException.class) - public R handleNotPermissionException(NotPermissionException e, HttpServletRequest request) { + @ExceptionHandler({NotPermissionException.class, NotRoleException.class}) + public R handleNotAccessException(RuntimeException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); - log.error("请求地址'{}',权限码校验失败'{}'", requestURI, e.getMessage()); - return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权"); - } - - /** - * 处理角色权限校验失败异常。 - * - * @param e 异常信息 - * @param request 当前请求 - * @return 统一失败响应 - */ - @ExceptionHandler(NotRoleException.class) - public R handleNotRoleException(NotRoleException e, HttpServletRequest request) { - String requestURI = request.getRequestURI(); - log.error("请求地址'{}',角色权限校验失败'{}'", requestURI, e.getMessage()); + String reason = e instanceof NotRoleException ? "角色权限校验失败" : "权限码校验失败"; + log.error("请求地址'{}',{}'{}'", requestURI, reason, e.getMessage()); return R.fail(HttpStatus.HTTP_FORBIDDEN, "没有访问权限,请联系管理员授权"); } diff --git a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java index 94df3b319..47b8be926 100644 --- a/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java +++ b/ruoyi-common/ruoyi-common-satoken/src/main/java/org/dromara/common/satoken/utils/LoginHelper.java @@ -3,6 +3,7 @@ package org.dromara.common.satoken.utils; import cn.dev33.satoken.session.SaSession; import cn.dev33.satoken.stp.StpUtil; import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.dev33.satoken.exception.NotLoginException; import cn.hutool.core.convert.Convert; import cn.hutool.core.util.ObjectUtil; import cn.hutool.http.useragent.UserAgent; @@ -98,13 +99,12 @@ public class LoginHelper { * * @return 登录用户 */ - @SuppressWarnings("unchecked") public static T getLoginUser() { - SaSession session = StpUtil.getTokenSession(); - if (ObjectUtil.isNull(session)) { + try { + return getLoginUser(StpUtil.getTokenSession()); + } catch (NotLoginException e) { return null; } - return (T) session.get(LOGIN_USER_KEY); } /** @@ -113,9 +113,27 @@ public class LoginHelper { * @param token Token * @return 登录用户 */ - @SuppressWarnings("unchecked") public static T getLoginUser(String token) { - SaSession session = StpUtil.getTokenSessionByToken(token); + if (StringUtils.isBlank(token)) { + return null; + } + SaSession session; + try { + session = StpUtil.getTokenSessionByToken(token); + } catch (NotLoginException e) { + return null; + } + return getLoginUser(session); + } + + /** + * 从会话中读取登录用户。 + * + * @param session 登录会话 + * @return 登录用户 + */ + @SuppressWarnings("unchecked") + private static T getLoginUser(SaSession session) { if (ObjectUtil.isNull(session)) { return null; } @@ -225,12 +243,7 @@ public class LoginHelper { * @return 是否已登录 */ public static boolean isLogin() { - try { - StpUtil.checkLogin(); - return true; - } catch (Exception e) { - return false; - } + return StpUtil.isLogin(); } } From f171ac03c4d19c439f93d01c95c75ff4fa094c7a 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: Sat, 16 May 2026 17:16:16 +0800 Subject: [PATCH 26/35] =?UTF-8?q?refactor(common-web):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=20Web=20=E5=9F=BA=E7=A1=80=E9=85=8D=E7=BD=AE=E4=B8=8E?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复可重复读取请求体包装器的 ServletInputStream 状态判断 - 将 XSS 过滤器改为构造器注入配置,移除静态 Spring Bean 获取 - 增加 CORS 配置属性,支持通过 web.cors.* 配置跨域规则 - 优化请求日志参数处理,避免 JSON 解析异常影响主请求并限制日志长度 - 收敛请求体解析异常响应,避免向前端暴露底层异常细节 --- ruoyi-common/ruoyi-common-web/pom.xml | 2 +- .../common/web/config/FilterConfig.java | 4 +- .../common/web/config/ResourcesConfig.java | 29 +++------ .../common/web/config/UndertowConfig.java | 63 ------------------- .../web/config/properties/CorsProperties.java | 41 ++++++++++++ .../web/filter/RepeatedlyRequestWrapper.java | 9 +-- .../dromara/common/web/filter/XssFilter.java | 12 ++-- .../web/handler/GlobalExceptionHandler.java | 4 +- .../PlusWebInvokeTimeInterceptor.java | 42 ++++++++++--- 9 files changed, 101 insertions(+), 105 deletions(-) delete mode 100644 ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java create mode 100644 ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CorsProperties.java diff --git a/ruoyi-common/ruoyi-common-web/pom.xml b/ruoyi-common/ruoyi-common-web/pom.xml index 65965eec0..cb6e35cd7 100644 --- a/ruoyi-common/ruoyi-common-web/pom.xml +++ b/ruoyi-common/ruoyi-common-web/pom.xml @@ -32,7 +32,7 @@ - + org.springframework.boot spring-boot-starter-jetty diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/FilterConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/FilterConfig.java index b095851d3..b7de20e86 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/FilterConfig.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/FilterConfig.java @@ -33,8 +33,8 @@ public class FilterConfig { order = FilterRegistrationBean.HIGHEST_PRECEDENCE + 1, dispatcherTypes = DispatcherType.REQUEST ) - public XssFilter xssFilter() { - return new XssFilter(); + public XssFilter xssFilter(XssProperties xssProperties) { + return new XssFilter(xssProperties); } /** diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java index c73309ba3..00f8dce5b 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java @@ -5,16 +5,17 @@ import cn.hutool.core.date.DateUtil; import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.json.enhance.JsonValueEnhancer; import org.dromara.common.web.advice.ResponseEnhancementAdvice; +import org.dromara.common.web.config.properties.CorsProperties; import org.dromara.common.web.handler.GlobalExceptionHandler; import org.dromara.common.web.interceptor.PlusWebInvokeTimeInterceptor; import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.format.FormatterRegistry; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import java.util.Date; @@ -25,6 +26,7 @@ import java.util.Date; * @author Lion Li */ @AutoConfiguration +@EnableConfigurationProperties(CorsProperties.class) public class ResourcesConfig implements WebMvcConfigurer { /** @@ -55,32 +57,19 @@ public class ResourcesConfig implements WebMvcConfigurer { }); } - /** - * 注册静态资源处理器。 - * - * @param registry 资源处理器注册表 - */ - @Override - public void addResourceHandlers(ResourceHandlerRegistry registry) { - } - /** * 跨域配置 * * @return 全局 Cors 过滤器 */ @Bean - public CorsFilter corsFilter() { + public CorsFilter corsFilter(CorsProperties corsProperties) { CorsConfiguration config = new CorsConfiguration(); - config.setAllowCredentials(true); - // 设置访问源地址 - config.addAllowedOriginPattern("*"); - // 设置访问源请求头 - config.addAllowedHeader("*"); - // 设置访问源请求方法 - config.addAllowedMethod("*"); - // 有效期 1800秒 - config.setMaxAge(1800L); + config.setAllowCredentials(corsProperties.getAllowCredentials()); + config.setAllowedOriginPatterns(corsProperties.getAllowedOriginPatterns()); + config.setAllowedHeaders(corsProperties.getAllowedHeaders()); + config.setAllowedMethods(corsProperties.getAllowedMethods()); + config.setMaxAge(corsProperties.getMaxAge()); // 添加映射路径,拦截一切请求 UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java deleted file mode 100644 index 8179bea85..000000000 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/UndertowConfig.java +++ /dev/null @@ -1,63 +0,0 @@ -//package org.dromara.common.web.config; -// -//import io.undertow.server.DefaultByteBufferPool; -//import io.undertow.server.handlers.DisallowedMethodsHandler; -//import io.undertow.util.HttpString; -//import io.undertow.websockets.jsr.WebSocketDeploymentInfo; -//import org.dromara.common.core.utils.SpringUtils; -//import org.springframework.boot.autoconfigure.AutoConfiguration; -//import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; -//import org.springframework.boot.web.server.WebServerFactoryCustomizer; -//import org.springframework.core.task.VirtualThreadTaskExecutor; -// -///** -// * Undertow 自定义配置 -// * -// * @author Lion Li -// */ -//@AutoConfiguration -//public class UndertowConfig implements WebServerFactoryCustomizer { -// -// /** -// * 自定义 Undertow 配置 -// *

-// * 主要配置内容包括: -// * 1. 配置 WebSocket 部署信息 -// * 2. 在虚拟线程模式下使用虚拟线程池 -// * 3. 禁用不安全的 HTTP 方法,如 CONNECT、TRACE、TRACK -// *

-// * -// * @param factory Undertow 的 Web 服务器工厂 -// */ -// @Override -// public void customize(UndertowServletWebServerFactory factory) { -// factory.addDeploymentInfoCustomizers(deploymentInfo -> { -// // 配置 WebSocket 部署信息,设置 WebSocket 使用的缓冲区池 -// WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo(); -// webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(true, 1024)); -// deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo); -// -// // 如果启用了虚拟线程,配置 Undertow 使用虚拟线程池 -// if (SpringUtils.isVirtual()) { -// // 创建虚拟线程池,线程池前缀为 "undertow-" -// VirtualThreadTaskExecutor executor = new VirtualThreadTaskExecutor("undertow-"); -// // 设置虚拟线程池为执行器和异步执行器 -// deploymentInfo.setExecutor(executor); -// deploymentInfo.setAsyncExecutor(executor); -// } -// -// // 配置禁止某些不安全的 HTTP 方法(如 CONNECT、TRACE、TRACK) -// deploymentInfo.addInitialHandlerChainWrapper(handler -> { -// // 禁止三个方法 CONNECT/TRACE/TRACK 也是不安全的 避免爬虫骚扰 -// HttpString[] disallowedHttpMethods = { -// HttpString.tryFromString("CONNECT"), -// HttpString.tryFromString("TRACE"), -// HttpString.tryFromString("TRACK") -// }; -// // 使用 DisallowedMethodsHandler 拦截并拒绝这些方法的请求 -// return new DisallowedMethodsHandler(handler, disallowedHttpMethods); -// }); -// }); -// } -// -//} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CorsProperties.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CorsProperties.java new file mode 100644 index 000000000..8b30dcc9c --- /dev/null +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/properties/CorsProperties.java @@ -0,0 +1,41 @@ +package org.dromara.common.web.config.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.ArrayList; +import java.util.List; + +/** + * 跨域配置属性。 + */ +@Data +@ConfigurationProperties(prefix = "web.cors") +public class CorsProperties { + + /** + * 是否允许携带凭证。 + */ + private Boolean allowCredentials = true; + + /** + * 允许的来源匹配规则。 + */ + private List allowedOriginPatterns = new ArrayList<>(List.of("*")); + + /** + * 允许的请求头。 + */ + private List allowedHeaders = new ArrayList<>(List.of("*")); + + /** + * 允许的请求方法。 + */ + private List allowedMethods = new ArrayList<>(List.of("*")); + + /** + * 预检请求缓存时间,单位秒。 + */ + private Long maxAge = 1800L; + +} diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/RepeatedlyRequestWrapper.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/RepeatedlyRequestWrapper.java index a282a9bfc..a5e14451c 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/RepeatedlyRequestWrapper.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/RepeatedlyRequestWrapper.java @@ -13,6 +13,7 @@ import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; /** * 构建可重复读取输入流的请求包装器,缓存请求体以支持多次读取。 @@ -45,7 +46,7 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper { */ @Override public BufferedReader getReader() throws IOException { - return new BufferedReader(new InputStreamReader(getInputStream())); + return new BufferedReader(new InputStreamReader(getInputStream(), StandardCharsets.UTF_8)); } /** @@ -65,17 +66,17 @@ public class RepeatedlyRequestWrapper extends HttpServletRequestWrapper { @Override public int available() throws IOException { - return body.length; + return bais.available(); } @Override public boolean isFinished() { - return false; + return bais.available() == 0; } @Override public boolean isReady() { - return false; + return true; } @Override diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssFilter.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssFilter.java index b63af29d7..6104f043b 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssFilter.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/filter/XssFilter.java @@ -1,6 +1,6 @@ package org.dromara.common.web.filter; -import org.dromara.common.core.utils.SpringUtils; +import lombok.RequiredArgsConstructor; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.web.config.properties.XssProperties; import org.springframework.http.HttpMethod; @@ -17,11 +17,14 @@ import java.util.List; * * @author ruoyi */ +@RequiredArgsConstructor public class XssFilter implements Filter { /** * 跳过 XSS 过滤的请求路径集合。 */ - public List excludes = new ArrayList<>(); + private final List excludes = new ArrayList<>(); + + private final XssProperties properties; /** * 初始化过滤器并加载配置中的排除路径。 @@ -31,8 +34,9 @@ public class XssFilter implements Filter { */ @Override public void init(FilterConfig filterConfig) throws ServletException { - XssProperties properties = SpringUtils.getBean(XssProperties.class); - excludes.addAll(properties.getExcludeUrls()); + if (properties.getExcludeUrls() != null) { + excludes.addAll(properties.getExcludeUrls()); + } } /** diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java index b69761af2..f75ffbc24 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/handler/GlobalExceptionHandler.java @@ -213,7 +213,7 @@ public class GlobalExceptionHandler { public R handleJsonParseException(JsonParseException e, HttpServletRequest request) { String requestURI = request.getRequestURI(); log.error("请求地址'{}' 发生 JSON 解析异常: {}", requestURI, e.getMessage()); - return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求数据格式错误(JSON 解析失败):" + e.getMessage()); + return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求数据格式错误"); } /** @@ -222,7 +222,7 @@ public class GlobalExceptionHandler { @ExceptionHandler(HttpMessageNotReadableException.class) public R handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request) { log.error("请求地址'{}', 参数解析失败: {}", request.getRequestURI(), e.getMessage()); - return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求参数格式错误:" + e.getMostSpecificCause().getMessage()); + return R.fail(HttpStatus.HTTP_BAD_REQUEST, "请求参数格式错误"); } /** diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java index 6e96d5296..a56153a65 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/interceptor/PlusWebInvokeTimeInterceptor.java @@ -14,7 +14,6 @@ import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.web.filter.RepeatedlyRequestWrapper; import org.springframework.http.MediaType; import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.ModelAndView; import tools.jackson.databind.JsonNode; import tools.jackson.databind.json.JsonMapper; import tools.jackson.databind.node.ArrayNode; @@ -36,6 +35,11 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { private final static ThreadLocal KEY_CACHE = new ThreadLocal<>(); + /** + * 请求参数日志最大长度。 + */ + private static final int MAX_PARAM_LOG_LENGTH = 4000; + /** * 请求进入控制器前记录入参并启动耗时统计。 * @@ -54,20 +58,17 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { if (request instanceof RepeatedlyRequestWrapper) { jsonParam = IoUtil.read(request.getReader()); if (StringUtils.isNotBlank(jsonParam)) { - JsonMapper jsonMapper = JsonUtils.getJsonMapper(); - JsonNode rootNode = jsonMapper.readTree(jsonParam); - removeSensitiveFields(rootNode, SystemConstants.EXCLUDE_PROPERTIES); - jsonParam = rootNode.toString(); + jsonParam = sanitizeJsonParam(jsonParam); } } - log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, jsonParam); + log.info("[PLUS]开始请求 => URL[{}],参数类型[json],参数:[{}]", url, limit(jsonParam)); } else { Map parameterMap = request.getParameterMap(); if (MapUtil.isNotEmpty(parameterMap)) { Map map = new LinkedHashMap<>(parameterMap); MapUtil.removeAny(map, SystemConstants.EXCLUDE_PROPERTIES); String parameters = JsonUtils.toJsonString(map); - log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, parameters); + log.info("[PLUS]开始请求 => URL[{}],参数类型[param],参数:[{}]", url, limit(parameters)); } else { log.info("[PLUS]开始请求 => URL[{}],无参数", url); } @@ -110,9 +111,32 @@ public class PlusWebInvokeTimeInterceptor implements HandlerInterceptor { } } - @Override - public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { + /** + * 清洗 JSON 请求参数日志,解析失败时不影响主请求。 + * + * @param jsonParam 原始 JSON 字符串 + * @return 清洗后的参数日志 + */ + private String sanitizeJsonParam(String jsonParam) { + try { + JsonMapper jsonMapper = JsonUtils.getJsonMapper(); + JsonNode rootNode = jsonMapper.readTree(jsonParam); + removeSensitiveFields(rootNode, SystemConstants.EXCLUDE_PROPERTIES); + return rootNode.toString(); + } catch (Exception e) { + log.debug("[PLUS]请求参数 JSON 解析失败,跳过结构化脱敏: {}", e.getMessage()); + return jsonParam; + } + } + /** + * 限制日志字段长度。 + * + * @param value 原始字符串 + * @return 截断后的字符串 + */ + private String limit(String value) { + return StringUtils.substring(value, 0, MAX_PARAM_LOG_LENGTH); } /** From 84afd6e6c061efcfdd1d48dc8e6e0e95648228a4 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: Sat, 16 May 2026 18:44:24 +0800 Subject: [PATCH 27/35] =?UTF-8?q?refactor:=20=E4=BD=BF=E7=94=A8=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E8=A7=A3=E8=80=A6=E4=B8=9A=E5=8A=A1=E5=89=AF=E4=BD=9C?= =?UTF-8?q?=E7=94=A8=E5=B9=B6=E4=BC=98=E5=8C=96=E8=99=9A=E6=8B=9F=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E5=B9=B6=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增登录成功事件,解耦在线用户缓存、登录日志和最近登录信息更新 - 新增在线用户清理事件,角色授权变更后异步清理受影响用户会话 - 新增 OSS 配置变更事件,统一处理配置初始化、删除和默认配置切换后的缓存刷新 - 新增工作流抄送、待办消息、结果消息事件,拆分全局监听器中的副作用逻辑 - 扩展 ThreadUtils 支持带返回值的虚拟线程批量执行 - 在线用户监控批量读取 Redis 会话信息时使用虚拟线程并发处理 - OSS 多文件查询、URL 解析和 DTO 转换使用虚拟线程并发处理 - 工作流办理人回显和用户解析按类型并发查询 - 工作流多渠道消息发送改为并发执行 - WebSocket 与 SSE 广播消息按用户并发发送 --- .../web/event/UserLoginSuccessEvent.java | 13 +++ .../web/listener/UserActionListener.java | 48 +---------- .../listener/UserLoginSuccessListener.java | 71 ++++++++++++++++ .../dromara/web/service/SysLoginService.java | 2 +- .../common/core/utils/ThreadUtils.java | 42 ++++++++- .../push/core/SseEmitterSessionManager.java | 9 +- .../push/core/WebSocketSessionManager.java | 7 +- .../monitor/SysUserOnlineController.java | 27 +++--- .../controller/system/SysRoleController.java | 8 +- .../system/event/OnlineUserCleanEvent.java | 30 +++++++ .../system/event/OssConfigChangeEvent.java | 30 +++++++ .../listener/OnlineUserCleanListener.java | 39 +++++++++ .../listener/OssConfigChangeListener.java | 48 +++++++++++ .../service/impl/SysOssConfigServiceImpl.java | 35 +++++--- .../service/impl/SysOssServiceImpl.java | 45 ++++++---- .../service/impl/SysRoleServiceImpl.java | 8 +- .../workflow/event/WorkflowCopyEvent.java | 23 +++++ .../event/WorkflowResultMessageEvent.java | 23 +++++ .../event/WorkflowTaskMessageEvent.java | 23 +++++ .../listener/WorkflowGlobalListener.java | 26 ++---- .../listener/WorkflowSideEffectListener.java | 85 +++++++++++++++++++ .../service/impl/FlwCommonServiceImpl.java | 52 +++++++----- .../impl/FlwTaskAssigneeServiceImpl.java | 31 ++++++- 23 files changed, 579 insertions(+), 146 deletions(-) create mode 100644 ruoyi-admin/src/main/java/org/dromara/web/event/UserLoginSuccessEvent.java create mode 100644 ruoyi-admin/src/main/java/org/dromara/web/listener/UserLoginSuccessListener.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OnlineUserCleanEvent.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OssConfigChangeEvent.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OnlineUserCleanListener.java create mode 100644 ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OssConfigChangeListener.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowCopyEvent.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowResultMessageEvent.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowTaskMessageEvent.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowSideEffectListener.java diff --git a/ruoyi-admin/src/main/java/org/dromara/web/event/UserLoginSuccessEvent.java b/ruoyi-admin/src/main/java/org/dromara/web/event/UserLoginSuccessEvent.java new file mode 100644 index 000000000..a1a429b7b --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/event/UserLoginSuccessEvent.java @@ -0,0 +1,13 @@ +package org.dromara.web.event; + +import cn.dev33.satoken.stp.parameter.SaLoginParameter; + +/** + * 用户登录成功事件。 + * + * @param loginId 登录标识 + * @param tokenValue token 值 + * @param loginParameter 登录参数 + */ +public record UserLoginSuccessEvent(Object loginId, String tokenValue, SaLoginParameter loginParameter) { +} diff --git a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java index 60e68a2d4..e2855d965 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserActionListener.java @@ -2,73 +2,29 @@ package org.dromara.web.listener; import cn.dev33.satoken.listener.SaTokenListener; import cn.dev33.satoken.stp.parameter.SaLoginParameter; -import cn.hutool.http.useragent.UserAgent; -import cn.hutool.http.useragent.UserAgentUtil; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.constant.CacheNames; -import org.dromara.common.core.constant.Constants; -import org.dromara.common.core.utils.MessageUtils; -import org.dromara.common.core.utils.ServletUtils; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; -import org.dromara.common.core.utils.ip.AddressUtils; -import org.dromara.common.log.event.LoginInfoEvent; import org.dromara.common.redis.utils.RedisUtils; -import org.dromara.common.satoken.utils.LoginHelper; -import org.dromara.system.api.domain.UserOnlineDTO; -import org.dromara.web.service.SysLoginService; +import org.dromara.web.event.UserLoginSuccessEvent; import org.springframework.stereotype.Component; -import java.time.Duration; - /** * 用户行为监听器,用于同步在线状态和登录日志。 * * @author Lion Li */ -@RequiredArgsConstructor @Component @Slf4j public class UserActionListener implements SaTokenListener { - private final SysLoginService loginService; - /** * 登录成功后记录在线信息并写入登录日志。 */ @Override public void doLogin(String loginType, Object loginId, String tokenValue, SaLoginParameter loginParameter) { - UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); - String ip = ServletUtils.getClientIP(); - UserOnlineDTO dto = new UserOnlineDTO(); - dto.setIpaddr(ip); - dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); - dto.setBrowser(userAgent.getBrowser().getName()); - dto.setOs(userAgent.getOs().getName()); - dto.setLoginTime(System.currentTimeMillis()); - dto.setTokenId(tokenValue); - String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY); - dto.setUserName(username); - dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY)); - dto.setDeviceType(loginParameter.getDeviceType()); - dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY)); - if (loginParameter.getTimeout() == -1) { - RedisUtils.setCacheObject(CacheNames.ONLINE_TOKEN_KEY + tokenValue, dto); - } else { - RedisUtils.setCacheObject(CacheNames.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout())); - } - - // 记录登录日志 - LoginInfoEvent loginInfoEvent = new LoginInfoEvent(); - loginInfoEvent.setUsername(username); - loginInfoEvent.setStatus(Constants.LOGIN_SUCCESS); - loginInfoEvent.setMessage(MessageUtils.message("user.login.success")); - loginInfoEvent.setRequest(ServletUtils.getRequest()); - SpringUtils.context().publishEvent(loginInfoEvent); - // 更新登录信息 - loginService.recordLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip); - log.info("user doLogin, userId:{}, token:***{}", loginId, StringUtils.right(tokenValue, 8)); + SpringUtils.context().publishEvent(new UserLoginSuccessEvent(loginId, tokenValue, loginParameter)); } /** diff --git a/ruoyi-admin/src/main/java/org/dromara/web/listener/UserLoginSuccessListener.java b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserLoginSuccessListener.java new file mode 100644 index 000000000..1255f9b6d --- /dev/null +++ b/ruoyi-admin/src/main/java/org/dromara/web/listener/UserLoginSuccessListener.java @@ -0,0 +1,71 @@ +package org.dromara.web.listener; + +import cn.dev33.satoken.stp.parameter.SaLoginParameter; +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.constant.CacheNames; +import org.dromara.common.core.constant.Constants; +import org.dromara.common.core.utils.MessageUtils; +import org.dromara.common.core.utils.ServletUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.ip.AddressUtils; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.system.api.domain.UserOnlineDTO; +import org.dromara.web.event.UserLoginSuccessEvent; +import org.dromara.web.service.SysLoginService; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.time.Duration; + +/** + * 用户登录成功监听器。 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@Component +@Slf4j +public class UserLoginSuccessListener { + + private final SysLoginService loginService; + + /** + * 登录成功后记录在线信息、登录日志与最近登录信息。 + * + * @param event 用户登录成功事件 + */ + @EventListener + public void handleLoginSuccess(UserLoginSuccessEvent event) { + SaLoginParameter loginParameter = event.loginParameter(); + UserAgent userAgent = UserAgentUtil.parse(ServletUtils.getRequest().getHeader("User-Agent")); + String ip = ServletUtils.getClientIP(); + String username = (String) loginParameter.getExtra(LoginHelper.USER_NAME_KEY); + String tokenValue = event.tokenValue(); + + UserOnlineDTO dto = new UserOnlineDTO(); + dto.setIpaddr(ip); + dto.setLoginLocation(AddressUtils.getRealAddressByIP(ip)); + dto.setBrowser(userAgent.getBrowser().getName()); + dto.setOs(userAgent.getOs().getName()); + dto.setLoginTime(System.currentTimeMillis()); + dto.setTokenId(tokenValue); + dto.setUserName(username); + dto.setClientKey((String) loginParameter.getExtra(LoginHelper.CLIENT_KEY)); + dto.setDeviceType(loginParameter.getDeviceType()); + dto.setDeptName((String) loginParameter.getExtra(LoginHelper.DEPT_NAME_KEY)); + if (loginParameter.getTimeout() == -1) { + RedisUtils.setCacheObject(CacheNames.ONLINE_TOKEN_KEY + tokenValue, dto); + } else { + RedisUtils.setCacheObject(CacheNames.ONLINE_TOKEN_KEY + tokenValue, dto, Duration.ofSeconds(loginParameter.getTimeout())); + } + + loginService.recordLoginInfo(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")); + loginService.updateLastLoginInfo((Long) loginParameter.getExtra(LoginHelper.USER_KEY), ip); + log.info("user doLogin, userId:{}, token:***{}", event.loginId(), StringUtils.right(tokenValue, 8)); + } + +} diff --git a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java index 79fd7e51a..77a03c270 100644 --- a/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java +++ b/ruoyi-admin/src/main/java/org/dromara/web/service/SysLoginService.java @@ -175,7 +175,7 @@ public class SysLoginService { * @param userId 用户ID * @param ip 登录IP */ - public void recordLoginInfo(Long userId, String ip) { + public void updateLastLoginInfo(Long userId, String ip) { SysUser sysUser = new SysUser(); sysUser.setUserId(userId); sysUser.setLoginIp(ip); diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ThreadUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ThreadUtils.java index a03e96819..9a3bd0ad1 100644 --- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ThreadUtils.java +++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/ThreadUtils.java @@ -2,11 +2,12 @@ package org.dromara.common.core.utils; import lombok.AccessLevel; import lombok.NoArgsConstructor; -import org.dromara.common.core.exception.ServiceException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.concurrent.*; +import java.util.function.Supplier; /** * 线程工具 @@ -38,4 +39,43 @@ public class ThreadUtils { } + /** + * 批量执行有返回值的任务 + * + * @param supplierList 任务列表 + * @param 返回值类型 + * @return 按提交顺序返回的任务结果 + */ + @SafeVarargs + public static List virtualSubmitAll(Supplier... supplierList) { + return virtualSubmitAll(List.of(supplierList)); + } + + /** + * 批量执行有返回值的任务 + * + * @param supplierList 任务列表 + * @param 返回值类型 + * @return 按提交顺序返回的任务结果 + */ + public static List virtualSubmitAll(Collection> supplierList) { + List> futureList = new ArrayList<>(); + try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) { + for (Supplier supplier : supplierList) { + futureList.add(executor.submit(supplier::get)); + } + List resultList = new ArrayList<>(futureList.size()); + for (Future future : futureList) { + resultList.add(future.get()); + } + return resultList; + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("线程执行被中断", e); + } catch (ExecutionException e) { + Throwable cause = e.getCause() == null ? e : e.getCause(); + throw new RuntimeException("线程执行异常:" + cause.getMessage(), cause); + } + } + } diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java index c7f36fb85..0e4690348 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java @@ -3,6 +3,7 @@ package org.dromara.common.push.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.ThreadUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.push.constant.MessageConstants; import org.dromara.common.push.dto.PushDTO; @@ -227,9 +228,11 @@ public class SseEmitterSessionManager implements PushSessionManager { * @param message 要发送的消息内容 */ public void sendMessage(String message) { - for (Long userId : USER_TOKEN_EMITTERS.keySet()) { - sendMessage(userId, message); - } + List userIds = new ArrayList<>(USER_TOKEN_EMITTERS.keySet()); + Runnable[] sendTasks = userIds.stream() + .map(userId -> (Runnable) () -> sendMessage(userId, message)) + .toArray(Runnable[]::new); + ThreadUtils.virtualInvokeAll(sendTasks); } /** diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java index 4061aad6e..2c8f2251b 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/WebSocketSessionManager.java @@ -3,6 +3,7 @@ package org.dromara.common.push.core; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.map.MapUtil; import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.utils.ThreadUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.push.dto.PushDTO; import org.dromara.common.push.properties.MessageProperties; @@ -181,7 +182,11 @@ public class WebSocketSessionManager implements PushSessionManager { if (payload == null) { return; } - USER_TOKEN_SESSIONS.keySet().forEach(userId -> sendMessage(userId, payload)); + List userIds = new ArrayList<>(USER_TOKEN_SESSIONS.keySet()); + Runnable[] sendTasks = userIds.stream() + .map(userId -> (Runnable) () -> sendMessage(userId, payload)) + .toArray(Runnable[]::new); + ThreadUtils.virtualInvokeAll(sendTasks); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java index e32a49d35..8741762db 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/monitor/SysUserOnlineController.java @@ -10,6 +10,7 @@ import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.ThreadUtils; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; import org.dromara.common.redis.annotation.RepeatSubmit; @@ -19,11 +20,10 @@ import org.dromara.system.api.domain.UserOnlineDTO; import org.dromara.system.domain.SysUserOnline; import org.springframework.web.bind.annotation.*; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; +import java.util.function.Supplier; /** * 在线用户监控 @@ -47,15 +47,16 @@ public class SysUserOnlineController extends BaseController { public R> list(String ipaddr, String userName) { // 获取所有未过期的 token Collection keys = RedisUtils.keys(CacheNames.ONLINE_TOKEN_KEY + "*"); - List userOnlineDTOList = new ArrayList<>(); - for (String key : keys) { + List> suppliers = keys.stream().map(key -> (Supplier) () -> { String token = StringUtils.substringAfterLast(key, ":"); // 如果已经过期则跳过 if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) { - continue; + return null; } - userOnlineDTOList.add(RedisUtils.getCacheObject(CacheNames.ONLINE_TOKEN_KEY + token)); - } + return RedisUtils.getCacheObject(CacheNames.ONLINE_TOKEN_KEY + token); + }).toList(); + List userOnlineDTOList = ThreadUtils.virtualSubmitAll(suppliers); + userOnlineDTOList.removeAll(Collections.singleton(null)); if (StringUtils.isNotEmpty(ipaddr) && StringUtils.isNotEmpty(userName)) { userOnlineDTOList = StreamUtils.filter(userOnlineDTOList, userOnline -> StringUtils.equals(ipaddr, userOnline.getIpaddr()) && @@ -71,7 +72,6 @@ public class SysUserOnlineController extends BaseController { ); } Collections.reverse(userOnlineDTOList); - userOnlineDTOList.removeAll(Collections.singleton(null)); List userOnlineList = BeanUtil.copyToList(userOnlineDTOList, SysUserOnline.class); return R.ok(PageResult.build(userOnlineList)); } @@ -103,10 +103,13 @@ public class SysUserOnlineController extends BaseController { public R> getInfo() { // 获取指定账号 id 的 token 集合 List tokenIds = StpUtil.getTokenValueListByLoginId(StpUtil.getLoginIdAsString()); - List userOnlineDTOList = tokenIds.stream() - .filter(token -> StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) >= -1) - .map(token -> (UserOnlineDTO) RedisUtils.getCacheObject(CacheNames.ONLINE_TOKEN_KEY + token)) - .collect(Collectors.toList()); + List> suppliers = tokenIds.stream().map(token -> (Supplier) () -> { + if (StpUtil.stpLogic.getTokenActiveTimeoutByToken(token) < -1) { + return null; + } + return RedisUtils.getCacheObject(CacheNames.ONLINE_TOKEN_KEY + token); + }).toList(); + List userOnlineDTOList = ThreadUtils.virtualSubmitAll(suppliers); //复制和处理 SysUserOnline 对象列表 Collections.reverse(userOnlineDTOList); userOnlineDTOList.removeAll(Collections.singleton(null)); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java index 7885fa91e..e25b16ea6 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/controller/system/SysRoleController.java @@ -6,6 +6,7 @@ import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.log.annotation.Log; import org.dromara.common.log.enums.BusinessType; @@ -18,6 +19,7 @@ import org.dromara.system.domain.bo.SysRoleBo; import org.dromara.system.domain.bo.SysUserBo; import org.dromara.system.domain.vo.SysRoleVo; import org.dromara.system.domain.vo.SysUserVo; +import org.dromara.system.event.OnlineUserCleanEvent; import org.dromara.system.service.ISysDeptService; import org.dromara.system.service.ISysRoleService; import org.dromara.system.service.ISysUserService; @@ -123,7 +125,7 @@ public class SysRoleController extends BaseController { } if (roleService.updateRoleBaseInfo(role) > 0) { - roleService.cleanOnlineUserByRole(role.getRoleId()); + SpringUtils.context().publishEvent(OnlineUserCleanEvent.byRole(role.getRoleId())); return R.ok(); } return R.fail("修改角色'" + role.getRoleName() + "'失败,请联系管理员"); @@ -143,7 +145,7 @@ public class SysRoleController extends BaseController { roleService.checkRoleAllowed(role); roleService.checkRoleDataScope(role.getRoleId()); if (roleService.updateRolePermission(role) > 0) { - roleService.cleanOnlineUserByRole(role.getRoleId()); + SpringUtils.context().publishEvent(OnlineUserCleanEvent.byRole(role.getRoleId())); return R.ok(); } return R.fail("修改角色'" + role.getRoleName() + "'权限失败,请联系管理员"); @@ -163,7 +165,7 @@ public class SysRoleController extends BaseController { roleService.checkRoleAllowed(role); roleService.checkRoleDataScope(role.getRoleId()); if (roleService.updateRoleStatus(role.getRoleId(), role.getStatus()) > 0) { - roleService.cleanOnlineUserByRole(role.getRoleId()); + SpringUtils.context().publishEvent(OnlineUserCleanEvent.byRole(role.getRoleId())); return R.ok(); } return R.fail("修改角色'" + role.getRoleName() + "'状态失败,请联系管理员"); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OnlineUserCleanEvent.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OnlineUserCleanEvent.java new file mode 100644 index 000000000..5fca1f224 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OnlineUserCleanEvent.java @@ -0,0 +1,30 @@ +package org.dromara.system.event; + +import cn.hutool.core.collection.CollUtil; + +import java.util.Collection; +import java.util.List; + +/** + * 在线用户清理事件。 + * + * @param roleId 角色 ID,存在时清理拥有该角色的在线用户 + * @param userIds 用户 ID 集合,存在时清理指定在线用户 + */ +public record OnlineUserCleanEvent(Long roleId, Collection userIds) { + + public OnlineUserCleanEvent { + if (CollUtil.isNotEmpty(userIds)) { + userIds = List.copyOf(userIds); + } + } + + public static OnlineUserCleanEvent byRole(Long roleId) { + return new OnlineUserCleanEvent(roleId, null); + } + + public static OnlineUserCleanEvent byUsers(Collection userIds) { + return new OnlineUserCleanEvent(null, userIds); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OssConfigChangeEvent.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OssConfigChangeEvent.java new file mode 100644 index 000000000..182c3d9ae --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OssConfigChangeEvent.java @@ -0,0 +1,30 @@ +package org.dromara.system.event; + +/** + * OSS 配置变更事件。 + * + * @param configKey 当前配置 key + * @param oldConfigKey 变更前配置 key + * @param configJson 当前配置 JSON,为空表示清理缓存 + * @param defaultConfig 是否设置为默认配置 + */ +public record OssConfigChangeEvent( + String configKey, + String oldConfigKey, + String configJson, + boolean defaultConfig +) { + + public static OssConfigChangeEvent save(String configKey, String oldConfigKey, String configJson) { + return new OssConfigChangeEvent(configKey, oldConfigKey, configJson, false); + } + + public static OssConfigChangeEvent remove(String configKey) { + return new OssConfigChangeEvent(configKey, null, null, false); + } + + public static OssConfigChangeEvent useDefault(String configKey) { + return new OssConfigChangeEvent(configKey, null, null, true); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OnlineUserCleanListener.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OnlineUserCleanListener.java new file mode 100644 index 000000000..d1ab0d077 --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OnlineUserCleanListener.java @@ -0,0 +1,39 @@ +package org.dromara.system.listener; + +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import org.dromara.system.event.OnlineUserCleanEvent; +import org.dromara.system.service.ISysRoleService; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +/** + * 在线用户清理监听器。 + * + * @author Lion Li + */ +@Component +@RequiredArgsConstructor +public class OnlineUserCleanListener { + + private final ISysRoleService roleService; + + /** + * 权限或用户角色关系变化后清理受影响的在线用户。 + * + * @param event 在线用户清理事件 + */ + @Async + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true) + public void cleanOnlineUser(OnlineUserCleanEvent event) { + if (event.roleId() != null) { + roleService.cleanOnlineUserByRole(event.roleId()); + } + if (CollUtil.isNotEmpty(event.userIds())) { + roleService.cleanOnlineUser(event.userIds()); + } + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OssConfigChangeListener.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OssConfigChangeListener.java new file mode 100644 index 000000000..330ab24ed --- /dev/null +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/listener/OssConfigChangeListener.java @@ -0,0 +1,48 @@ +package org.dromara.system.listener; + +import org.dromara.common.core.constant.CacheNames; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.oss.constant.OssConstant; +import org.dromara.common.oss.factory.OssFactory; +import org.dromara.common.redis.utils.CacheUtils; +import org.dromara.common.redis.utils.RedisUtils; +import org.dromara.system.event.OssConfigChangeEvent; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +/** + * OSS 配置变更监听器。 + * + * @author Lion Li + */ +@Component +public class OssConfigChangeListener { + + /** + * 数据提交后同步刷新 OSS 配置缓存与客户端实例。 + * + * @param event OSS 配置变更事件 + */ + @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT, fallbackExecution = true) + public void refreshOssConfig(OssConfigChangeEvent event) { + if (event.defaultConfig()) { + RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, event.configKey()); + return; + } + if (StringUtils.isNotBlank(event.oldConfigKey()) && !StringUtils.equals(event.oldConfigKey(), event.configKey())) { + CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, event.oldConfigKey()); + OssFactory.remove(event.oldConfigKey()); + } + if (StringUtils.isBlank(event.configKey())) { + return; + } + if (StringUtils.isBlank(event.configJson())) { + CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, event.configKey()); + } else { + CacheUtils.put(CacheNames.SYS_OSS_CONFIG, event.configKey(), event.configJson()); + } + OssFactory.remove(event.configKey()); + } + +} diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java index a59013f4c..e38fce246 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java @@ -12,17 +12,18 @@ import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.ObjectUtils; +import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.query.QueryBuilder; import org.dromara.common.oss.constant.OssConstant; -import org.dromara.common.oss.factory.OssFactory; import org.dromara.common.redis.utils.CacheUtils; import org.dromara.common.redis.utils.RedisUtils; import org.dromara.system.domain.SysOssConfig; import org.dromara.system.domain.bo.SysOssConfigBo; import org.dromara.system.domain.vo.SysOssConfigVo; +import org.dromara.system.event.OssConfigChangeEvent; import org.dromara.system.mapper.SysOssConfigMapper; import org.dromara.system.service.ISysOssConfigService; import org.springframework.stereotype.Service; @@ -30,7 +31,6 @@ import org.springframework.transaction.annotation.Transactional; import java.util.Collection; import java.util.List; -import java.util.Objects; /** * 对象存储配置Service业务层处理 @@ -117,7 +117,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { if (flag) { // 从数据库查询完整的数据做缓存 config = ossConfigMapper.selectById(config.getOssConfigId()); - CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); + publishOssConfigSaved(config, null); } return flag; } @@ -143,12 +143,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { if (flag) { // 从数据库查询完整的数据做缓存 config = ossConfigMapper.selectById(config.getOssConfigId()); - if (ObjectUtil.isNotNull(oldConfig) && !Objects.equals(oldConfig.getConfigKey(), config.getConfigKey())) { - CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, oldConfig.getConfigKey()); - OssFactory.remove(oldConfig.getConfigKey()); - } - CacheUtils.put(CacheNames.SYS_OSS_CONFIG, config.getConfigKey(), JsonUtils.toJsonString(config)); - OssFactory.remove(config.getConfigKey()); + publishOssConfigSaved(config, ObjectUtils.notNullGetter(oldConfig, SysOssConfig::getConfigKey)); } return flag; } @@ -188,10 +183,8 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { } boolean flag = ossConfigMapper.deleteByIds(ids) > 0; if (flag) { - list.forEach(sysOssConfig -> { - CacheUtils.evict(CacheNames.SYS_OSS_CONFIG, sysOssConfig.getConfigKey()); - OssFactory.remove(sysOssConfig.getConfigKey()); - }); + list.forEach(sysOssConfig -> + SpringUtils.context().publishEvent(OssConfigChangeEvent.remove(sysOssConfig.getConfigKey()))); } return flag; } @@ -227,9 +220,23 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { int row = ossConfigMapper.lambda().set(SysOssConfig::getStatus, SystemConstants.NO).updateCount(); row += ossConfigMapper.updateById(sysOssConfig); if (row > 0) { - RedisUtils.setCacheObject(OssConstant.DEFAULT_CONFIG_KEY, sysOssConfig.getConfigKey()); + SpringUtils.context().publishEvent(OssConfigChangeEvent.useDefault(sysOssConfig.getConfigKey())); } return row; } + /** + * 发布 OSS 配置保存事件。 + * + * @param config 当前配置 + * @param oldConfigKey 变更前配置 key + */ + private void publishOssConfigSaved(SysOssConfig config, String oldConfigKey) { + SpringUtils.context().publishEvent(OssConfigChangeEvent.save( + config.getConfigKey(), + oldConfigKey, + JsonUtils.toJsonString(config) + )); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java index 69cee9e22..a57d70806 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java @@ -15,6 +15,7 @@ import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.ThreadUtils; import org.dromara.common.core.utils.file.FileUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.common.mybatis.core.page.PageQuery; @@ -44,10 +45,11 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.time.Duration; -import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Supplier; /** * 文件上传 服务层实现 @@ -85,19 +87,21 @@ public class SysOssServiceImpl implements ISysOssService, OssService { */ @Override public List listByIds(Collection ossIds) { - List list = new ArrayList<>(); SysOssServiceImpl ossService = SpringUtils.getAopProxy(this); - for (Long id : ossIds) { + List> suppliers = ossIds.stream().map(id -> (Supplier) () -> { SysOssVo vo = ossService.getById(id); if (ObjectUtil.isNotNull(vo)) { try { - list.add(this.matchingUrl(vo)); + return this.matchingUrl(vo); } catch (Exception ignored) { // 如果oss异常无法连接则将数据直接返回 - list.add(vo); + return vo; } } - } + return null; + }).toList(); + List list = ThreadUtils.virtualSubmitAll(suppliers); + list.removeAll(Collections.singleton(null)); return list; } @@ -109,19 +113,22 @@ public class SysOssServiceImpl implements ISysOssService, OssService { */ @Override public String selectUrlByIds(String ossIds) { - List list = new ArrayList<>(); + List ids = StringUtils.splitTo(ossIds, Convert::toLong); SysOssServiceImpl ossService = SpringUtils.getAopProxy(this); - for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { + List> suppliers = ids.stream().map(id -> (Supplier) () -> { SysOssVo vo = ossService.getById(id); if (ObjectUtil.isNotNull(vo)) { try { - list.add(this.matchingUrl(vo).getUrl()); + return this.matchingUrl(vo).getUrl(); } catch (Exception ignored) { // 如果oss异常无法连接则将数据直接返回 - list.add(vo.getUrl()); + return vo.getUrl(); } } - } + return null; + }).toList(); + List list = ThreadUtils.virtualSubmitAll(suppliers); + list.removeAll(Collections.singleton(null)); return StringUtils.joinComma(list); } @@ -133,19 +140,23 @@ public class SysOssServiceImpl implements ISysOssService, OssService { */ @Override public List selectByIds(String ossIds) { - List list = new ArrayList<>(); - for (Long id : StringUtils.splitTo(ossIds, Convert::toLong)) { - SysOssVo vo = SpringUtils.getAopProxy(this).getById(id); + List ids = StringUtils.splitTo(ossIds, Convert::toLong); + var ossService = SpringUtils.getAopProxy(this); + List> suppliers = ids.stream().map(id -> (Supplier) () -> { + SysOssVo vo = ossService.getById(id); if (ObjectUtil.isNotNull(vo)) { try { vo.setUrl(this.matchingUrl(vo).getUrl()); - list.add(BeanUtil.toBean(vo, OssDTO.class)); + return BeanUtil.toBean(vo, OssDTO.class); } catch (Exception ignored) { // 如果oss异常无法连接则将数据直接返回 - list.add(BeanUtil.toBean(vo, OssDTO.class)); + return BeanUtil.toBean(vo, OssDTO.class); } } - } + return null; + }).toList(); + List list = ThreadUtils.virtualSubmitAll(suppliers); + list.removeAll(Collections.singleton(null)); return list; } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java index 0b59bf510..9b7c51ba4 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysRoleServiceImpl.java @@ -13,6 +13,7 @@ import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.domain.PageResult; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.mybatis.core.page.PageQuery; @@ -26,6 +27,7 @@ import org.dromara.system.domain.SysRoleMenu; import org.dromara.system.domain.SysUserRole; import org.dromara.system.domain.bo.SysRoleBo; import org.dromara.system.domain.vo.SysRoleVo; +import org.dromara.system.event.OnlineUserCleanEvent; import org.dromara.system.mapper.SysRoleDeptMapper; import org.dromara.system.mapper.SysRoleMapper; import org.dromara.system.mapper.SysRoleMenuMapper; @@ -464,7 +466,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { .eq(SysUserRole::getUserId, userRole.getUserId()) .deleteCount(); if (rows > 0) { - cleanOnlineUser(List.of(userRole.getUserId())); + SpringUtils.context().publishEvent(OnlineUserCleanEvent.byUsers(List.of(userRole.getUserId()))); } return rows; } @@ -486,7 +488,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { .in(SysUserRole::getUserId, userIds) .deleteCount(); if (rows > 0) { - cleanOnlineUser(userIds); + SpringUtils.context().publishEvent(OnlineUserCleanEvent.byUsers(userIds)); } return rows; } @@ -515,7 +517,7 @@ public class SysRoleServiceImpl implements ISysRoleService, RoleService { rows = userRoleMapper.insertBatch(list) ? list.size() : 0; } if (rows > 0) { - cleanOnlineUser(userIds); + SpringUtils.context().publishEvent(OnlineUserCleanEvent.byUsers(userIds)); } return rows; } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowCopyEvent.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowCopyEvent.java new file mode 100644 index 000000000..06336313e --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowCopyEvent.java @@ -0,0 +1,23 @@ +package org.dromara.workflow.event; + +import cn.hutool.core.collection.CollUtil; +import org.dromara.warm.flow.core.entity.Task; +import org.dromara.workflow.domain.bo.FlowCopyBo; + +import java.util.List; + +/** + * 工作流抄送事件。 + * + * @param task 当前任务 + * @param flowCopyList 抄送人列表 + */ +public record WorkflowCopyEvent(Task task, List flowCopyList) { + + public WorkflowCopyEvent { + if (CollUtil.isNotEmpty(flowCopyList)) { + flowCopyList = List.copyOf(flowCopyList); + } + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowResultMessageEvent.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowResultMessageEvent.java new file mode 100644 index 000000000..58a37db10 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowResultMessageEvent.java @@ -0,0 +1,23 @@ +package org.dromara.workflow.event; + +import cn.hutool.core.collection.CollUtil; + +import java.util.List; + +/** + * 工作流结果消息事件。 + * + * @param flowName 流程名称 + * @param status 流程状态 + * @param createBy 发起人用户 ID + * @param messageType 消息类型 + */ +public record WorkflowResultMessageEvent(String flowName, String status, String createBy, List messageType) { + + public WorkflowResultMessageEvent { + if (CollUtil.isNotEmpty(messageType)) { + messageType = List.copyOf(messageType); + } + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowTaskMessageEvent.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowTaskMessageEvent.java new file mode 100644 index 000000000..68c221a1b --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/event/WorkflowTaskMessageEvent.java @@ -0,0 +1,23 @@ +package org.dromara.workflow.event; + +import cn.hutool.core.collection.CollUtil; + +import java.util.List; + +/** + * 工作流待办消息事件。 + * + * @param flowName 流程名称 + * @param instanceId 流程实例 ID + * @param messageType 消息类型 + * @param notice 通知内容 + */ +public record WorkflowTaskMessageEvent(String flowName, Long instanceId, List messageType, String notice) { + + public WorkflowTaskMessageEvent { + if (CollUtil.isNotEmpty(messageType)) { + messageType = List.copyOf(messageType); + } + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index dc021f646..66f93bb49 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -9,10 +9,10 @@ import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.enums.BusinessStatusEnum; +import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; import org.dromara.system.api.UserService; -import org.dromara.system.api.domain.UserDTO; import org.dromara.warm.flow.core.FlowEngine; import org.dromara.warm.flow.core.dto.FlowParams; import org.dromara.warm.flow.core.entity.Definition; @@ -22,10 +22,12 @@ import org.dromara.warm.flow.core.listener.GlobalListener; import org.dromara.warm.flow.core.listener.ListenerVariable; import org.dromara.workflow.common.ConditionalOnEnable; import org.dromara.workflow.common.constant.FlowConstant; -import org.dromara.workflow.common.enums.MessageTypeEnum; import org.dromara.workflow.common.enums.TaskStatusEnum; import org.dromara.workflow.domain.bo.FlowCopyBo; import org.dromara.workflow.domain.vo.NodeExtVo; +import org.dromara.workflow.event.WorkflowCopyEvent; +import org.dromara.workflow.event.WorkflowResultMessageEvent; +import org.dromara.workflow.event.WorkflowTaskMessageEvent; import org.dromara.workflow.handler.FlowProcessEventHandler; import org.dromara.workflow.service.IFlwCommonService; import org.dromara.workflow.service.IFlwInstanceService; @@ -232,7 +234,7 @@ public class WorkflowGlobalListener implements GlobalListener { List flowCopyList = MapUtil.get(variable, FlowConstant.FLOW_COPY_LIST, new TypeReference<>() { }); // 添加抄送人 - flwTaskService.setCopy(task, flowCopyList); + SpringUtils.context().publishEvent(new WorkflowCopyEvent(task, flowCopyList)); } if (variable.containsKey(FlowConstant.MESSAGE_TYPE)) { List messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() { @@ -240,7 +242,7 @@ public class WorkflowGlobalListener implements GlobalListener { String notice = MapUtil.getStr(variable, FlowConstant.MESSAGE_NOTICE); // 退回到申请人时只保留“已退回”结果消息,避免再追加一条“新的待办”形成重复提醒。 if (shouldSendTaskMessage(flowParams, definition, nextTasks)) { - flwCommonService.sendMessage(definition.getFlowName(), instance.getId(), messageType, notice); + SpringUtils.context().publishEvent(new WorkflowTaskMessageEvent(definition.getFlowName(), instance.getId(), messageType, notice)); } } FlowEngine.insService().removeVariables(instance.getId(), @@ -270,25 +272,13 @@ public class WorkflowGlobalListener implements GlobalListener { if (StringUtils.isBlank(instance.getCreateBy())) { return; } - BusinessStatusEnum statusEnum = BusinessStatusEnum.getByStatus(status); - if (statusEnum == null) { - return; - } - Long createBy = Convert.toLong(instance.getCreateBy(), null); - if (createBy == null) { - return; - } - UserDTO initiator = userService.selectById(createBy); - if (initiator == null || initiator.getUserId() == null) { - return; - } // 已完成、已退回这类结果消息只发给发起人,不再混入处理人待办消息。 - List messageType = Collections.singletonList(MessageTypeEnum.SYSTEM_MESSAGE.getCode()); + List messageType = null; if (MapUtil.isNotEmpty(variable) && variable.containsKey(FlowConstant.MESSAGE_TYPE)) { messageType = MapUtil.get(variable, FlowConstant.MESSAGE_TYPE, new TypeReference<>() { }); } - flwCommonService.sendResultMessage(definition.getFlowName(), statusEnum, messageType, Collections.singletonList(initiator)); + SpringUtils.context().publishEvent(new WorkflowResultMessageEvent(definition.getFlowName(), status, instance.getCreateBy(), messageType)); } /** diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowSideEffectListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowSideEffectListener.java new file mode 100644 index 000000000..315646810 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowSideEffectListener.java @@ -0,0 +1,85 @@ +package org.dromara.workflow.listener; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.enums.BusinessStatusEnum; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.system.api.UserService; +import org.dromara.system.api.domain.UserDTO; +import org.dromara.workflow.common.ConditionalOnEnable; +import org.dromara.workflow.common.enums.MessageTypeEnum; +import org.dromara.workflow.event.WorkflowCopyEvent; +import org.dromara.workflow.event.WorkflowResultMessageEvent; +import org.dromara.workflow.event.WorkflowTaskMessageEvent; +import org.dromara.workflow.service.IFlwCommonService; +import org.dromara.workflow.service.IFlwTaskService; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; + +/** + * 工作流副作用事件监听器。 + * + * @author may + */ +@ConditionalOnEnable +@Component +@RequiredArgsConstructor +public class WorkflowSideEffectListener { + + private final IFlwTaskService flwTaskService; + private final IFlwCommonService flwCommonService; + private final UserService userService; + + /** + * 保存工作流抄送记录。 + * + * @param event 工作流抄送事件 + */ + @EventListener + public void handleCopy(WorkflowCopyEvent event) { + flwTaskService.setCopy(event.task(), event.flowCopyList()); + } + + /** + * 发送工作流待办消息。 + * + * @param event 工作流待办消息事件 + */ + @EventListener + public void handleTaskMessage(WorkflowTaskMessageEvent event) { + flwCommonService.sendMessage(event.flowName(), event.instanceId(), event.messageType(), event.notice()); + } + + /** + * 发送工作流结果消息。 + * + * @param event 工作流结果消息事件 + */ + @EventListener + public void handleResultMessage(WorkflowResultMessageEvent event) { + if (!StringUtils.equalsAny(event.status(), BusinessStatusEnum.FINISH.getStatus(), BusinessStatusEnum.BACK.getStatus())) { + return; + } + Long createBy = Convert.toLong(event.createBy(), null); + if (createBy == null) { + return; + } + BusinessStatusEnum status = BusinessStatusEnum.getByStatus(event.status()); + if (status == null) { + return; + } + UserDTO initiator = userService.selectById(createBy); + if (initiator == null || initiator.getUserId() == null) { + return; + } + List messageType = CollUtil.isNotEmpty(event.messageType()) + ? event.messageType() + : Collections.singletonList(MessageTypeEnum.SYSTEM_MESSAGE.getCode()); + flwCommonService.sendResultMessage(event.flowName(), status, messageType, Collections.singletonList(initiator)); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java index 9f790c187..7b0c50786 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwCommonServiceImpl.java @@ -11,6 +11,7 @@ import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.SpringUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.ThreadUtils; import org.dromara.common.mail.core.MailBuilder; import org.dromara.system.api.MessageService; import org.dromara.system.api.domain.PushPayloadDTO; @@ -107,23 +108,29 @@ public class FlwCommonServiceImpl implements IFlwCommonService { Set emails = StreamUtils.toSet(userList, UserDTO::getEmail); emails.removeIf(StringUtils::isBlank); - for (String code : messageType) { - MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code); - if (ObjectUtil.isEmpty(messageTypeEnum)) { - continue; - } - try { - switch (messageTypeEnum) { - case SYSTEM_MESSAGE -> { - // 站内消息直接携带前端路由,消息盒子点击后可按路径分流。 - messageService.publishMessage(userIds, PushPayloadDTO.of( - PushTypeEnum.MESSAGE, - PushSourceEnum.WORKFLOW, - message, null, path - )); - } - case EMAIL_MESSAGE -> MailBuilder.of().to(emails).subject(subject).text(message).send(); - case SMS_MESSAGE -> { + Runnable[] sendTasks = messageType.stream() + .map(code -> (Runnable) () -> sendMessageByType(code, message, subject, path, userIds, emails, userList.size())) + .toArray(Runnable[]::new); + ThreadUtils.virtualInvokeAll(sendTasks); + } + + private void sendMessageByType(String code, String message, String subject, String path, List userIds, Set emails, int userCount) { + MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code); + if (ObjectUtil.isEmpty(messageTypeEnum)) { + return; + } + try { + switch (messageTypeEnum) { + case SYSTEM_MESSAGE -> { + // 站内消息直接携带前端路由,消息盒子点击后可按路径分流。 + messageService.publishMessage(userIds, PushPayloadDTO.of( + PushTypeEnum.MESSAGE, + PushSourceEnum.WORKFLOW, + message, null, path + )); + } + case EMAIL_MESSAGE -> MailBuilder.of().to(emails).subject(subject).text(message).send(); + case SMS_MESSAGE -> { // LinkedHashMap map = new LinkedHashMap<>(1); // // 根据具体短信服务商参数用法传参 // map.put("code", "1234"); @@ -132,14 +139,13 @@ public class FlwCommonServiceImpl implements IFlwCommonService { // // 指定获取一个短信服务商 configKey // SmsBlend smsBlend = SmsFactory.getSmsBlend("config1"); // SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map); - log.info("【短信发送 - TODO】用户数量={} 内容={}", userList.size(), message); - } - default -> log.warn("【消息发送】未处理的消息类型:{}", messageTypeEnum); + log.info("【短信发送 - TODO】用户数量={} 内容={}", userCount, message); } - } catch (Exception ex) { - // 记录错误但不抛出,确保主逻辑不受影响 - log.error("【消息发送失败】类型={},原因={}", messageTypeEnum, ex.getMessage(), ex); + default -> log.warn("【消息发送】未处理的消息类型:{}", messageTypeEnum); } + } catch (Exception ex) { + // 记录错误但不抛出,确保主逻辑不受影响 + log.error("【消息发送失败】类型={},原因={}", messageTypeEnum, ex.getMessage(), ex); } } diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java index 274d62eef..58a3af63b 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java @@ -11,6 +11,7 @@ import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.utils.DateUtils; import org.dromara.common.core.utils.StreamUtils; import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.ThreadUtils; import org.dromara.system.api.*; import org.dromara.system.api.domain.DeptDTO; import org.dromara.system.api.domain.TaskAssigneeDTO; @@ -29,6 +30,7 @@ import org.dromara.workflow.service.IFlwTaskAssigneeService; import org.springframework.stereotype.Service; import java.util.*; +import java.util.function.Supplier; import java.util.stream.Collectors; /** @@ -103,8 +105,7 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand } // 查询所有类型对应的 ID 名称映射 - Map> nameMap = new EnumMap<>(TaskAssigneeEnum.class); - typeIdMap.forEach((type, ids) -> nameMap.put(type, this.getNamesByType(type, ids))); + Map> nameMap = this.getNamesByTypes(typeIdMap); // 组装返回结果,保持原始顺序 return parsedMap.entrySet().stream() .map(entry -> { @@ -213,12 +214,34 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); } } - return typeIdMap.entrySet().stream() - .flatMap(entry -> this.getUsersByType(entry.getKey(), entry.getValue()).stream()) + return this.getUsersByTypes(typeIdMap).stream() .distinct() .toList(); } + private List getUsersByTypes(Map> typeIdMap) { + List>> suppliers = typeIdMap.entrySet().stream() + .map(entry -> (Supplier>) () -> this.getUsersByType(entry.getKey(), entry.getValue())) + .toList(); + return ThreadUtils.virtualSubmitAll(suppliers).stream() + .filter(CollUtil::isNotEmpty) + .flatMap(Collection::stream) + .toList(); + } + + private Map> getNamesByTypes(Map> typeIdMap) { + List types = new ArrayList<>(typeIdMap.keySet()); + List>> suppliers = types.stream() + .map(type -> (Supplier>) () -> this.getNamesByType(type, typeIdMap.get(type))) + .toList(); + List> names = ThreadUtils.virtualSubmitAll(suppliers); + Map> nameMap = new EnumMap<>(TaskAssigneeEnum.class); + for (int i = 0; i < types.size(); i++) { + nameMap.put(types.get(i), names.get(i)); + } + return nameMap; + } + /** * 根据指定的任务分配类型(TaskAssigneeEnum)和 ID 列表,获取对应的用户信息列表 * From a2300b3a290d021d28a321673b1eabdfe1458715 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Mon, 18 May 2026 10:10:16 +0800 Subject: [PATCH 28/35] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=BC=BA=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=B3=A8=E9=87=8A=E4=BB=A5=E6=8F=90=E5=8D=87=E5=8F=AF?= =?UTF-8?q?=E8=AF=BB=E6=80=A7=E5=92=8C=E7=BB=B4=E6=8A=A4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/doc/config/SpringDocConfig.java | 3 ++ .../config/ApiDecryptAutoConfiguration.java | 8 +++++ .../config/EncryptorAutoConfiguration.java | 31 ++++++++++++++++++- .../encrypt/core/EncryptContextFactory.java | 5 +++ .../encrypt/core/EncryptedFieldProcessor.java | 6 ++++ .../common/encrypt/filter/CryptoFilter.java | 7 +++++ .../filter/DecryptRequestBodyWrapper.java | 8 +++++ .../filter/EncryptResponseBodyWrapper.java | 18 +++++++++++ .../excel/core/DefaultExcelListener.java | 11 +++++++ .../common/excel/core/ExcelDownHandler.java | 5 +++ .../excel/handler/DataWriteHandler.java | 5 +++ .../common/mail/config/MailConfig.java | 6 ++++ .../oss/client/AbstractOssClientImpl.java | 6 ++++ .../oss/client/DefaultOssClientImpl.java | 6 ++++ .../push/core/SseEmitterSessionManager.java | 6 ++++ .../redis/aspectj/RepeatSubmitAspect.java | 6 ++++ .../common/redis/config/RedisConfig.java | 5 +++ .../config/properties/RedissonProperties.java | 6 ++++ .../common/web/config/ResourcesConfig.java | 6 ++++ .../service/impl/ExportExcelServiceImpl.java | 7 +++++ .../system/event/OnlineUserCleanEvent.java | 12 +++++++ .../system/event/OssConfigChangeEvent.java | 20 ++++++++++++ 22 files changed, 192 insertions(+), 1 deletion(-) diff --git a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java index cc55c7814..fabe10f4e 100644 --- a/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java +++ b/ruoyi-common/ruoyi-common-doc/src/main/java/org/dromara/common/doc/config/SpringDocConfig.java @@ -143,6 +143,9 @@ public class SpringDocConfig { */ static class PlusPaths extends Paths { + /** + * 构造路径缓存标记对象。 + */ public PlusPaths() { super(); } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java index 559002335..56e6ee110 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/ApiDecryptAutoConfiguration.java @@ -22,6 +22,14 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl @ConditionalOnProperty(value = "api-decrypt.enabled", havingValue = "true") public class ApiDecryptAutoConfiguration { + /** + * 注册 API 加解密过滤器。 + * + * @param properties API 解密配置 + * @param requestMappingHandlerMapping 请求映射处理器 + * @param handlerExceptionResolver 异常处理器 + * @return API 加解密过滤器 + */ @Bean @FilterRegistration( name = "cryptoFilter", diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java index dfe1915d1..686e2fcc4 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/config/EncryptorAutoConfiguration.java @@ -31,27 +31,57 @@ public class EncryptorAutoConfiguration { @Autowired private EncryptorProperties properties; + /** + * 创建字段加解密管理器。 + * + * @param mybatisPlusProperties MyBatis-Plus 配置 + * @return 字段加解密管理器 + */ @Bean public EncryptorManager encryptorManager(MybatisPlusProperties mybatisPlusProperties) { validateEncryptorProperties(properties); return new EncryptorManager(mybatisPlusProperties.getTypeAliasesPackage()); } + /** + * 创建加密上下文工厂。 + * + * @return 加密上下文工厂 + */ @Bean public EncryptContextFactory encryptContextFactory() { return new EncryptContextFactory(properties); } + /** + * 创建加密字段处理器。 + * + * @param encryptorManager 加解密管理器 + * @param encryptContextFactory 加密上下文工厂 + * @return 加密字段处理器 + */ @Bean public EncryptedFieldProcessor encryptedFieldProcessor(EncryptorManager encryptorManager, EncryptContextFactory encryptContextFactory) { return new EncryptedFieldProcessor(encryptorManager, encryptContextFactory); } + /** + * 创建 MyBatis 入参加密拦截器。 + * + * @param encryptedFieldProcessor 加密字段处理器 + * @return MyBatis 入参加密拦截器 + */ @Bean public MybatisEncryptInterceptor mybatisEncryptInterceptor(EncryptedFieldProcessor encryptedFieldProcessor) { return new MybatisEncryptInterceptor(encryptedFieldProcessor); } + /** + * 创建 MyBatis 出参解密拦截器。 + * + * @param encryptedFieldProcessor 加密字段处理器 + * @return MyBatis 出参解密拦截器 + */ @Bean public MybatisDecryptInterceptor mybatisDecryptInterceptor(EncryptedFieldProcessor encryptedFieldProcessor) { return new MybatisDecryptInterceptor(encryptedFieldProcessor); @@ -74,4 +104,3 @@ public class EncryptorAutoConfiguration { } - diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContextFactory.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContextFactory.java index 12c864052..827f5d182 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContextFactory.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptContextFactory.java @@ -17,6 +17,11 @@ public class EncryptContextFactory { private final EncryptorProperties defaultProperties; + /** + * 构造加密上下文工厂。 + * + * @param defaultProperties 默认加密配置 + */ public EncryptContextFactory(EncryptorProperties defaultProperties) { this.defaultProperties = defaultProperties; } diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptedFieldProcessor.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptedFieldProcessor.java index f8d4f340c..a1576c82e 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptedFieldProcessor.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/core/EncryptedFieldProcessor.java @@ -19,6 +19,12 @@ public class EncryptedFieldProcessor { private final EncryptorManager encryptorManager; private final EncryptContextFactory contextFactory; + /** + * 构造加密字段处理器。 + * + * @param encryptorManager 加解密管理器 + * @param contextFactory 加密上下文工厂 + */ public EncryptedFieldProcessor(EncryptorManager encryptorManager, EncryptContextFactory contextFactory) { this.encryptorManager = encryptorManager; this.contextFactory = contextFactory; diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java index f3eb24ab5..8a13a00af 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/CryptoFilter.java @@ -29,6 +29,13 @@ public class CryptoFilter implements Filter { private final RequestMappingHandlerMapping requestMappingHandlerMapping; private final HandlerExceptionResolver handlerExceptionResolver; + /** + * 构造加解密过滤器。 + * + * @param properties API 解密配置 + * @param requestMappingHandlerMapping 请求映射处理器 + * @param handlerExceptionResolver 异常处理器 + */ public CryptoFilter(ApiDecryptProperties properties, RequestMappingHandlerMapping requestMappingHandlerMapping, HandlerExceptionResolver handlerExceptionResolver) { diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java index b3afab84f..b5ca633f8 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/DecryptRequestBodyWrapper.java @@ -25,6 +25,14 @@ public class DecryptRequestBodyWrapper extends HttpServletRequestWrapper { private final byte[] body; + /** + * 解密请求体并缓存为可重复读取的 JSON 请求体。 + * + * @param request 原始请求 + * @param privateKey RSA 私钥 + * @param headerFlag 加密密钥请求头 + * @throws IOException 读取请求体异常 + */ public DecryptRequestBodyWrapper(HttpServletRequest request, String privateKey, String headerFlag) throws IOException { super(request); // 获取 AES 密码 采用 RSA 加密 diff --git a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java index 06cb1f096..680111d49 100644 --- a/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java +++ b/ruoyi-common/ruoyi-common-encrypt/src/main/java/org/dromara/common/encrypt/filter/EncryptResponseBodyWrapper.java @@ -26,6 +26,12 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { private PrintWriter printWriter; private Charset charset; + /** + * 构造加密响应包装器。 + * + * @param response 原始响应 + * @throws IOException 创建输出流异常 + */ public EncryptResponseBodyWrapper(HttpServletResponse response) throws IOException { super(response); this.byteArrayOutputStream = new ByteArrayOutputStream(); @@ -62,11 +68,23 @@ public class EncryptResponseBodyWrapper extends HttpServletResponseWrapper { byteArrayOutputStream.reset(); } + /** + * 获取已缓存的响应字节。 + * + * @return 响应字节数组 + * @throws IOException 刷新响应缓冲异常 + */ public byte[] getResponseData() throws IOException { flushBuffer(); return byteArrayOutputStream.toByteArray(); } + /** + * 获取已缓存的响应内容。 + * + * @return 响应文本 + * @throws IOException 刷新响应缓冲异常 + */ public String getContent() throws IOException { flushBuffer(); return byteArrayOutputStream.toString(charset); diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java index ac5bf599d..dadf6b147 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/DefaultExcelListener.java @@ -46,10 +46,21 @@ public class DefaultExcelListener extends AnalysisEventListener implements */ private Boolean failFast = Boolean.TRUE; + /** + * 构造 Excel 导入监听器。 + * + * @param isValidate 是否执行 Validator 校验 + */ public DefaultExcelListener(boolean isValidate) { this.isValidate = isValidate; } + /** + * 构造 Excel 导入监听器。 + * + * @param isValidate 是否执行 Validator 校验 + * @param failFast 发生异常时是否立即终止读取 + */ public DefaultExcelListener(boolean isValidate, boolean failFast) { this.isValidate = isValidate; this.failFast = failFast; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java index e16bc49f7..801764c03 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java @@ -65,6 +65,11 @@ public class ExcelDownHandler implements SheetWriteHandler { */ private int currentLinkedOptionsSheetIndex; + /** + * 构造 Excel 下拉选处理器。 + * + * @param options 外部指定的下拉选项 + */ public ExcelDownHandler(List options) { this.dropDownOptions = options; this.currentOptionsColumnIndex = 0; diff --git a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java index 541801764..baca75fb0 100644 --- a/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java +++ b/ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/handler/DataWriteHandler.java @@ -40,6 +40,11 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler { private final Map headColumnMap; + /** + * 构造批注与必填样式处理器。 + * + * @param clazz 表头类型 + */ public DataWriteHandler(Class clazz) { notationMap = getNotationMap(clazz); headColumnMap = getRequiredMap(clazz); diff --git a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java index a0042dddd..dfe4793a9 100644 --- a/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java +++ b/ruoyi-common/ruoyi-common-mail/src/main/java/org/dromara/common/mail/config/MailConfig.java @@ -16,6 +16,12 @@ import org.springframework.context.annotation.Bean; @EnableConfigurationProperties(MailProperties.class) public class MailConfig { + /** + * 创建邮件账户配置。 + * + * @param mailProperties 邮件配置属性 + * @return 邮件账户 + */ @Bean @ConditionalOnProperty(value = "mail.enabled", havingValue = "true") public MailAccount mailAccount(MailProperties mailProperties) { diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/AbstractOssClientImpl.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/AbstractOssClientImpl.java index 82cdb1494..76c8858b7 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/AbstractOssClientImpl.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/AbstractOssClientImpl.java @@ -86,6 +86,12 @@ public abstract class AbstractOssClientImpl implements OssClient { */ protected ExecutorService asyncExecutor; + /** + * 构造 S3 存储客户端基础实现。 + * + * @param clientId 客户端 ID + * @param config S3 存储客户端配置 + */ public AbstractOssClientImpl(String clientId, OssClientConfig config) { Assert.notNull(config, () -> S3StorageException.form("S3StorageClientConfig must not be null")); // 如果没有设置存储客户端ID,则随机生成一个 diff --git a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/DefaultOssClientImpl.java b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/DefaultOssClientImpl.java index 3c358f49c..ccbbae447 100644 --- a/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/DefaultOssClientImpl.java +++ b/ruoyi-common/ruoyi-common-oss/src/main/java/org/dromara/common/oss/client/DefaultOssClientImpl.java @@ -25,6 +25,12 @@ import java.util.concurrent.Executors; */ public class DefaultOssClientImpl extends AbstractOssClientImpl { + /** + * 构造默认 S3 存储客户端。 + * + * @param clientId 客户端 ID + * @param config S3 存储客户端配置 + */ public DefaultOssClientImpl(String clientId, OssClientConfig config) { super(clientId, config); } diff --git a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java index 0e4690348..19ca28f60 100644 --- a/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java +++ b/ruoyi-common/ruoyi-common-push/src/main/java/org/dromara/common/push/core/SseEmitterSessionManager.java @@ -33,6 +33,12 @@ public class SseEmitterSessionManager implements PushSessionManager { private final MessageProperties messageProperties; + /** + * 构造 SSE 会话管理器并启动心跳检测。 + * + * @param scheduledExecutorService 定时任务线程池 + * @param messageProperties 消息推送配置 + */ public SseEmitterSessionManager(ScheduledExecutorService scheduledExecutorService, MessageProperties messageProperties) { this.messageProperties = messageProperties; // 定时执行 SSE 心跳检测 diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java index 45ac5bbcc..df025d160 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/aspectj/RepeatSubmitAspect.java @@ -39,6 +39,12 @@ public class RepeatSubmitAspect { private static final ThreadLocal KEY_CACHE = new ThreadLocal<>(); + /** + * 请求进入前校验是否重复提交。 + * + * @param point 切点 + * @param repeatSubmit 防重复提交注解 + */ @Before("@annotation(repeatSubmit)") public void doBefore(JoinPoint point, RepeatSubmit repeatSubmit) throws Throwable { // 如果注解不为0 则使用注解数值 diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java index 00bca0902..7941ef096 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/RedisConfig.java @@ -40,6 +40,11 @@ public class RedisConfig { @Autowired private RedissonProperties redissonProperties; + /** + * 自定义 Redisson 序列化、线程与连接模式配置。 + * + * @return Redisson 自动配置定制器 + */ @Bean public RedissonAutoConfigurationCustomizer redissonCustomizer() { return config -> { diff --git a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java index d04fb64d5..a58a8e5c6 100644 --- a/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java +++ b/ruoyi-common/ruoyi-common-redis/src/main/java/org/dromara/common/redis/config/properties/RedissonProperties.java @@ -40,6 +40,9 @@ public class RedissonProperties { */ private ClusterServersConfig clusterServersConfig; + /** + * Redisson 单机服务配置。 + */ @Data @NoArgsConstructor public static class SingleServerConfig { @@ -76,6 +79,9 @@ public class RedissonProperties { } + /** + * Redisson 集群服务配置。 + */ @Data @NoArgsConstructor public static class ClusterServersConfig { diff --git a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java index 00f8dce5b..ab3b95d15 100644 --- a/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java +++ b/ruoyi-common/ruoyi-common-web/src/main/java/org/dromara/common/web/config/ResourcesConfig.java @@ -87,6 +87,12 @@ public class ResourcesConfig implements WebMvcConfigurer { return new GlobalExceptionHandler(); } + /** + * 注册响应增强处理器。 + * + * @param jsonValueEnhancer JSON 字段增强器 + * @return 响应增强处理器 + */ @Bean public ResponseEnhancementAdvice responseEnhancementAdvice(JsonValueEnhancer jsonValueEnhancer) { return new ResponseEnhancementAdvice(jsonValueEnhancer); diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java index d8adfd889..45739023f 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java @@ -232,6 +232,13 @@ public class ExportExcelServiceImpl implements IExportExcelService { */ private DemoCityData pData; + /** + * 构造城市演示数据。 + * + * @param id 数据库 id + * @param pid 父级 id + * @param name 名称 + */ public DemoCityData(Integer id, Integer pid, String name) { this.id = id; this.pid = pid; diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OnlineUserCleanEvent.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OnlineUserCleanEvent.java index 5fca1f224..e77c014bc 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OnlineUserCleanEvent.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OnlineUserCleanEvent.java @@ -19,10 +19,22 @@ public record OnlineUserCleanEvent(Long roleId, Collection userIds) { } } + /** + * 创建按角色清理在线用户事件。 + * + * @param roleId 角色 ID + * @return 在线用户清理事件 + */ public static OnlineUserCleanEvent byRole(Long roleId) { return new OnlineUserCleanEvent(roleId, null); } + /** + * 创建按用户清理在线用户事件。 + * + * @param userIds 用户 ID 集合 + * @return 在线用户清理事件 + */ public static OnlineUserCleanEvent byUsers(Collection userIds) { return new OnlineUserCleanEvent(null, userIds); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OssConfigChangeEvent.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OssConfigChangeEvent.java index 182c3d9ae..081ffb861 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OssConfigChangeEvent.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/event/OssConfigChangeEvent.java @@ -15,14 +15,34 @@ public record OssConfigChangeEvent( boolean defaultConfig ) { + /** + * 创建保存 OSS 配置后的变更事件。 + * + * @param configKey 当前配置 key + * @param oldConfigKey 变更前配置 key + * @param configJson 当前配置 JSON + * @return OSS 配置变更事件 + */ public static OssConfigChangeEvent save(String configKey, String oldConfigKey, String configJson) { return new OssConfigChangeEvent(configKey, oldConfigKey, configJson, false); } + /** + * 创建删除 OSS 配置后的变更事件。 + * + * @param configKey 配置 key + * @return OSS 配置变更事件 + */ public static OssConfigChangeEvent remove(String configKey) { return new OssConfigChangeEvent(configKey, null, null, false); } + /** + * 创建切换默认 OSS 配置后的变更事件。 + * + * @param configKey 默认配置 key + * @return OSS 配置变更事件 + */ public static OssConfigChangeEvent useDefault(String configKey) { return new OssConfigChangeEvent(configKey, null, null, true); } From f237d0c39178b4ab8c10adbaa933e2b1368cc187 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: Mon, 18 May 2026 17:24:59 +0800 Subject: [PATCH 29/35] =?UTF-8?q?add=20=E5=A2=9E=E5=8A=A0=20ruoyi-common-m?= =?UTF-8?q?cp=20=E6=A8=A1=E5=9D=97=20=E6=94=AF=E6=8C=81=E4=BD=9C=E4=B8=BAs?= =?UTF-8?q?erver=E6=8F=90=E4=BE=9Bmcp=E6=8E=A5=E5=8F=A3=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=20=E4=B8=8E=20=E6=8E=A5=E5=85=A5=E5=A4=9A?= =?UTF-8?q?=E4=B8=AAclient=E5=81=9A=E6=95=B0=E6=8D=AE=E9=87=87=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 11 ++ ruoyi-admin/pom.xml | 5 + .../src/main/resources/application.yml | 46 +++++++ ruoyi-common/pom.xml | 1 + ruoyi-common/ruoyi-common-bom/pom.xml | 7 + ruoyi-common/ruoyi-common-mcp/pom.xml | 34 +++++ .../mcp/config/McpAutoConfiguration.java | 29 ++++ .../common/mcp/core/McpClientTemplate.java | 125 ++++++++++++++++++ .../mcp/core/McpResourceReadResult.java | 20 +++ .../common/mcp/core/McpToolCallResult.java | 27 ++++ ...ot.autoconfigure.AutoConfiguration.imports | 1 + ruoyi-modules/ruoyi-demo/pom.xml | 5 + .../demo/controller/McpDemoController.java | 64 +++++++++ .../demo/mcp/McpDemoClientService.java | 88 ++++++++++++ .../dromara/demo/mcp/McpDemoServerTool.java | 85 ++++++++++++ 15 files changed, 548 insertions(+) create mode 100644 ruoyi-common/ruoyi-common-mcp/pom.xml create mode 100644 ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/config/McpAutoConfiguration.java create mode 100644 ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpClientTemplate.java create mode 100644 ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpResourceReadResult.java create mode 100644 ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpToolCallResult.java create mode 100644 ruoyi-common/ruoyi-common-mcp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java create mode 100644 ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java create mode 100644 ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoServerTool.java diff --git a/pom.xml b/pom.xml index 19b221a43..9b72103fc 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,8 @@ 1.8.7 2.5.12 + + 2.0.0-M6 3.5.0 @@ -113,6 +115,15 @@ import + + + org.springframework.ai + spring-ai-bom + ${spring-ai.version} + pom + import + + cn.hutool diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml index 80ef4b3d5..974128778 100644 --- a/ruoyi-admin/pom.xml +++ b/ruoyi-admin/pom.xml @@ -60,6 +60,11 @@ ruoyi-common-mail + + org.dromara + ruoyi-common-mcp + + org.dromara ruoyi-api diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index 015f6a284..beab6585f 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -281,3 +281,49 @@ mqtt.client: keystore-pass: truststore-path: truststore-pass: + +--- # MCP 服务端配置 +spring.ai.mcp: + server: + # 与 ruoyi-admin 共用端口,默认端点为 /mcp + enabled: true + protocol: STREAMABLE + name: ${spring.application.name} + version: ${project.version} + type: SYNC + annotation-scanner: + enabled: true + streamable-http: + mcp-endpoint: /mcp + +--- # MCP 客户端配置 +spring.ai.mcp: + client: + # 需要接入外部 MCP Server 时再打开,并配置 streamable-http/sse/stdio connections + enabled: false + name: ${spring.application.name}-mcp-client + version: ${project.version} + type: SYNC + toolcallback: + enabled: false + # Streamable HTTP 多服务端示例 + streamable-http: + connections: + knowledge: + url: http://localhost:9001 + crm: + url: http://localhost:9002 + # SSE 多服务端示例 + sse: + connections: + search: + url: http://localhost:9003 + # STDIO 多服务端示例 + stdio: + connections: + filesystem: + command: npx + args: + - -y + - '@modelcontextprotocol/server-filesystem' + - D:/data diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index f6a30c47f..fb14db339 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -37,6 +37,7 @@ ruoyi-common-encrypt ruoyi-common-push ruoyi-common-mqtt + ruoyi-common-mcp diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index b60c35f40..809474e19 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -159,6 +159,13 @@ ${revision} + + + org.dromara + ruoyi-common-mcp + ${revision} + + diff --git a/ruoyi-common/ruoyi-common-mcp/pom.xml b/ruoyi-common/ruoyi-common-mcp/pom.xml new file mode 100644 index 000000000..9c3ba8ab5 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mcp/pom.xml @@ -0,0 +1,34 @@ + + + + org.dromara + ruoyi-common + ${revision} + + 4.0.0 + + ruoyi-common-mcp + + + ruoyi-common-mcp mcp模块 + + + + + org.dromara + ruoyi-common-core + + + + org.springframework.ai + spring-ai-starter-mcp-server-webmvc + + + + org.springframework.ai + spring-ai-starter-mcp-client + + + diff --git a/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/config/McpAutoConfiguration.java b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/config/McpAutoConfiguration.java new file mode 100644 index 000000000..7802a8c4a --- /dev/null +++ b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/config/McpAutoConfiguration.java @@ -0,0 +1,29 @@ +package org.dromara.common.mcp.config; + +import io.modelcontextprotocol.client.McpSyncClient; +import org.dromara.common.mcp.core.McpClientTemplate; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; + +import java.util.List; + +/** + * MCP 公共模块自动配置。 + * + * @author Lion Li + */ +@AutoConfiguration +public class McpAutoConfiguration { + + /** + * MCP Client 通用操作模板。 + */ + @Bean + @ConditionalOnBean(McpSyncClient.class) + @ConditionalOnMissingBean + public McpClientTemplate mcpClientTemplate(List mcpSyncClients) { + return new McpClientTemplate(mcpSyncClients); + } +} diff --git a/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpClientTemplate.java b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpClientTemplate.java new file mode 100644 index 000000000..962665cdd --- /dev/null +++ b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpClientTemplate.java @@ -0,0 +1,125 @@ +package org.dromara.common.mcp.core; + +import io.modelcontextprotocol.client.McpSyncClient; +import io.modelcontextprotocol.spec.McpSchema; +import lombok.RequiredArgsConstructor; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * MCP Client 通用操作模板。 + *

+ * Spring AI 已经负责 MCP Client 的创建、初始化与连接管理,本模板只做项目内常用调用封装, + * 避免业务模块直接遍历 `McpSyncClient` 或重复处理返回结构。 + * + * @author Lion Li + */ +@RequiredArgsConstructor +public class McpClientTemplate { + + private final List mcpSyncClients; + + /** + * 查询所有已连接 MCP Server 的工具列表。 + * + * @return Server 名称与工具名称列表 + */ + public Map> listTools() { + Map> result = new LinkedHashMap<>(); + for (McpSyncClient client : mcpSyncClients) { + List tools = client.listTools().tools().stream() + .map(McpSchema.Tool::name) + .toList(); + result.put(getServerName(client), tools); + } + return result; + } + + /** + * 调用所有 MCP Server 上的同名工具。 + * + * @param toolName 工具名称 + * @param arguments 工具参数 + * @return 各 Server 的工具调用结果 + */ + public Map callTool(String toolName, Map arguments) { + Map result = new LinkedHashMap<>(); + for (McpSyncClient client : mcpSyncClients) { + McpSchema.CallToolResult callResult = client.callTool(new McpSchema.CallToolRequest(toolName, arguments)); + result.put(getServerName(client), McpToolCallResult.of(getServerName(client), callResult)); + } + return result; + } + + /** + * 调用指定 MCP Server 上的工具。 + * + * @param serverName Server 名称 + * @param toolName 工具名称 + * @param arguments 工具参数 + * @return 工具调用结果 + */ + public Optional callTool(String serverName, String toolName, Map arguments) { + return findClient(serverName) + .map(client -> McpToolCallResult.of(serverName, client.callTool(new McpSchema.CallToolRequest(toolName, arguments)))); + } + + /** + * 读取所有 MCP Server 上的同名资源。 + * + * @param uri 资源地址 + * @return 各 Server 的资源内容 + */ + public Map readResource(String uri) { + Map result = new LinkedHashMap<>(); + for (McpSyncClient client : mcpSyncClients) { + McpSchema.ReadResourceResult readResult = client.readResource(new McpSchema.ReadResourceRequest(uri)); + result.put(getServerName(client), McpResourceReadResult.of(getServerName(client), readResult)); + } + return result; + } + + /** + * 读取指定 MCP Server 上的资源。 + * + * @param serverName Server 名称 + * @param uri 资源地址 + * @return 资源内容 + */ + public Optional readResource(String serverName, String uri) { + return findClient(serverName) + .map(client -> McpResourceReadResult.of(serverName, client.readResource(new McpSchema.ReadResourceRequest(uri)))); + } + + /** + * 按 Server 名称查找 MCP Client。 + * + * @param serverName Server 名称 + * @return MCP Client + */ + public Optional findClient(String serverName) { + return mcpSyncClients.stream() + .filter(client -> serverName.equals(getServerName(client))) + .findFirst(); + } + + /** + * 查询已连接的 MCP Client。 + * + * @return MCP Client 列表 + */ + public List getClients() { + return mcpSyncClients; + } + + private String getServerName(McpSyncClient client) { + McpSchema.Implementation serverInfo = client.getServerInfo(); + if (serverInfo == null || serverInfo.name() == null) { + return "unknown"; + } + return serverInfo.name(); + } +} diff --git a/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpResourceReadResult.java b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpResourceReadResult.java new file mode 100644 index 000000000..afdaab154 --- /dev/null +++ b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpResourceReadResult.java @@ -0,0 +1,20 @@ +package org.dromara.common.mcp.core; + +import io.modelcontextprotocol.spec.McpSchema; + +import java.util.List; + +/** + * MCP 资源读取结果。 + * + * @author Lion Li + */ +public record McpResourceReadResult( + String serverName, + List contents +) { + + public static McpResourceReadResult of(String serverName, McpSchema.ReadResourceResult result) { + return new McpResourceReadResult(serverName, result.contents()); + } +} diff --git a/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpToolCallResult.java b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpToolCallResult.java new file mode 100644 index 000000000..80bd2d4cb --- /dev/null +++ b/ruoyi-common/ruoyi-common-mcp/src/main/java/org/dromara/common/mcp/core/McpToolCallResult.java @@ -0,0 +1,27 @@ +package org.dromara.common.mcp.core; + +import io.modelcontextprotocol.spec.McpSchema; + +import java.util.List; + +/** + * MCP 工具调用结果。 + * + * @author Lion Li + */ +public record McpToolCallResult( + String serverName, + boolean error, + List content, + Object structuredContent +) { + + public static McpToolCallResult of(String serverName, McpSchema.CallToolResult result) { + return new McpToolCallResult( + serverName, + Boolean.TRUE.equals(result.isError()), + result.content(), + result.structuredContent() + ); + } +} diff --git a/ruoyi-common/ruoyi-common-mcp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-mcp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..cd71c1bbd --- /dev/null +++ b/ruoyi-common/ruoyi-common-mcp/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.dromara.common.mcp.config.McpAutoConfiguration diff --git a/ruoyi-modules/ruoyi-demo/pom.xml b/ruoyi-modules/ruoyi-demo/pom.xml index da6105015..847c7d6bf 100644 --- a/ruoyi-modules/ruoyi-demo/pom.xml +++ b/ruoyi-modules/ruoyi-demo/pom.xml @@ -98,6 +98,11 @@ ruoyi-common-mqtt + + org.dromara + ruoyi-common-mcp + + diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java new file mode 100644 index 000000000..163fe7eb9 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java @@ -0,0 +1,64 @@ +package org.dromara.demo.controller; + +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.mcp.core.McpResourceReadResult; +import org.dromara.demo.mcp.McpDemoClientService; +import org.dromara.demo.mcp.McpDemoClientService.McpDemoHandleResult; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; + +/** + * MCP Client 演示案例。 + *

+ * 这些接口不是 MCP 协议接口,只是为了在后台系统中手动触发 MCP Client 调用。 + * 真实 MCP Server 入口由 Spring AI 挂载到 `/mcp`。 + *

+ * 调用示例: + *

    + *
  • `GET /demo/mcp/tools`:查看已连接 MCP Server 的工具列表
  • + *
  • `GET /demo/mcp/receive?toolName=demo_get_data&id=1`:调用外部工具并模拟业务处理
  • + *
  • `GET /demo/mcp/resource?uri=demo://summary`:读取外部 MCP 资源
  • + *
+ * 当 `spring.ai.mcp.client.enabled=false` 时,本 Controller 不会注册。 + * + * @author Lion Li + */ +@RequiredArgsConstructor +@RestController +@RequestMapping("/demo/mcp") +@ConditionalOnBean(McpDemoClientService.class) +public class McpDemoController { + + private final McpDemoClientService mcpDemoClientService; + + /** + * 查询外部 MCP Server 工具列表。 + */ + @GetMapping("/tools") + public R>> tools() { + return R.ok(mcpDemoClientService.listRemoteTools()); + } + + /** + * 调用外部 MCP 工具并模拟业务处理。 + */ + @GetMapping("/receive") + public R receive(@RequestParam(defaultValue = "demo_get_data") String toolName, + @RequestParam(defaultValue = "1") String id) { + return R.ok(mcpDemoClientService.receiveAndHandle(toolName, Map.of("id", id))); + } + + /** + * 读取外部 MCP 资源。 + */ + @GetMapping("/resource") + public R> resource(@RequestParam(defaultValue = "demo://summary") String uri) { + return R.ok(mcpDemoClientService.readRemoteResource(uri)); + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java new file mode 100644 index 000000000..da4e3701b --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java @@ -0,0 +1,88 @@ +package org.dromara.demo.mcp; + +import org.dromara.common.mcp.core.McpClientTemplate; +import org.dromara.common.mcp.core.McpResourceReadResult; +import org.dromara.common.mcp.core.McpToolCallResult; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * MCP Client 演示服务,从外部 MCP Server 接收数据后交给业务层处理。 + *

+ * 本服务会注入 Spring AI 自动创建的多个 `McpSyncClient`。当 + * `spring.ai.mcp.client.enabled=true` 且配置了多个 connection 时, + * 这里可以统一遍历这些 MCP Server。 + *

+ * 如果要用当前项目自己连自己测试,建议启动两个 `ruoyi-admin` 实例: + * 一个作为 server,一个作为 client,client 指向 server 的地址。 + * + * @author Lion Li + */ +@Service +@RequiredArgsConstructor +@ConditionalOnBean(McpClientTemplate.class) +public class McpDemoClientService { + + private final McpClientTemplate mcpClientTemplate; + + /** + * 查询已接入的 MCP Server 与工具列表。 + * + * @return Server 名称与工具名称列表 + */ + public Map> listRemoteTools() { + return mcpClientTemplate.listTools(); + } + + /** + * 调用外部 MCP 工具接收数据。 + * + * @param toolName 工具名称 + * @param arguments 工具参数 + * @return 工具返回内容 + */ + public Map callRemoteTool(String toolName, Map arguments) { + return mcpClientTemplate.callTool(toolName, arguments); + } + + /** + * 读取外部 MCP 资源。 + * + * @param uri 资源地址 + * @return 资源内容 + */ + public Map readRemoteResource(String uri) { + return mcpClientTemplate.readResource(uri); + } + + /** + * 模拟业务处理入口。真实业务可在这里做校验、转换、去重、入库和审计。 + *

+ * MCP 返回数据不建议直接入库,应先转换成业务 BO/DTO,再进入业务 Service。 + * + * @param toolName 工具名称 + * @param arguments 工具参数 + * @return 处理结果 + */ + public McpDemoHandleResult receiveAndHandle(String toolName, Map arguments) { + return new McpDemoHandleResult("MCP", true, callRemoteTool(toolName, arguments)); + } + + /** + * MCP 数据处理结果。 + * + * @param sourceType 数据来源类型 + * @param handled 是否已处理 + * @param data MCP 原始返回数据 + */ + public record McpDemoHandleResult( + String sourceType, + boolean handled, + Map data + ) { + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoServerTool.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoServerTool.java new file mode 100644 index 000000000..2988936f9 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoServerTool.java @@ -0,0 +1,85 @@ +package org.dromara.demo.mcp; + +import org.springframework.ai.mcp.annotation.McpResource; +import org.springframework.ai.mcp.annotation.McpTool; +import org.springframework.ai.mcp.annotation.McpToolParam; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * MCP Server 演示工具,对外提供业务数据。 + *

+ * 启动 `ruoyi-admin` 后,本类中的 `@McpTool` 与 `@McpResource` 会被 Spring AI + * MCP 注解扫描器注册到当前应用的 MCP Server,默认通过同端口 `/mcp` 暴露。 + *

+ * 可被外部 MCP Client 调用的示例能力: + *

    + *
  • `demo_get_data`:根据 id 查询一条演示数据
  • + *
  • `demo_list_data`:按 limit 查询演示数据列表
  • + *
  • `demo://summary`:读取演示模块摘要资源
  • + *
+ * 真实业务中不要在 MCP 工具里直接访问 Mapper,应继续调用业务 Service, + * 并保留权限、租户、脱敏、审计和幂等规则。 + * + * @author Lion Li + */ +@Component +public class McpDemoServerTool { + + /** + * 查询一条演示数据。 + * + * @param id 业务主键 + * @return 演示数据 + */ + @McpTool(name = "demo_get_data", description = "根据业务主键查询演示数据") + public McpDemoData getData(@McpToolParam(description = "业务主键") String id) { + return new McpDemoData(id, "演示数据-" + id, "ruoyi-demo", 100, LocalDateTime.now()); + } + + /** + * 查询演示数据列表。 + * + * @param limit 返回条数 + * @return 演示数据列表 + */ + @McpTool(name = "demo_list_data", description = "查询演示数据列表") + public List listData(@McpToolParam(description = "返回条数") Integer limit) { + int size = limit == null || limit < 1 ? 3 : Math.min(limit, 20); + return java.util.stream.IntStream.rangeClosed(1, size) + .mapToObj(index -> new McpDemoData(String.valueOf(index), "演示数据-" + index, "ruoyi-demo", index * 10, LocalDateTime.now())) + .toList(); + } + + /** + * 读取演示资源。 + * + * @return 演示资源内容 + */ + @McpResource( + name = "demo_summary", + uri = "demo://summary", + description = "演示模块摘要资源", + mimeType = "text/plain" + ) + public String summary() { + return "ruoyi-demo MCP resource summary"; + } + + /** + * MCP 演示数据。 + * + * @author Lion Li + */ + public record McpDemoData( + String id, + String name, + String source, + Integer amount, + LocalDateTime syncTime + ) { + } + +} From e2f9a7ce9ade0167232357c95725c29bde4469ac 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: Mon, 18 May 2026 18:10:01 +0800 Subject: [PATCH 30/35] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20mcp?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=A1=88=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../demo/controller/McpDemoController.java | 25 +++++--- .../demo/mcp/McpDemoClientHandlers.java | 58 +++++++++++++++++++ .../demo/mcp/McpDemoClientService.java | 21 +++++-- 3 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientHandlers.java diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java index 163fe7eb9..e47f50a01 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/McpDemoController.java @@ -5,12 +5,12 @@ import org.dromara.common.core.domain.R; import org.dromara.common.mcp.core.McpResourceReadResult; import org.dromara.demo.mcp.McpDemoClientService; import org.dromara.demo.mcp.McpDemoClientService.McpDemoHandleResult; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.util.List; import java.util.Map; /** @@ -25,14 +25,13 @@ import java.util.Map; *
  • `GET /demo/mcp/receive?toolName=demo_get_data&id=1`:调用外部工具并模拟业务处理
  • *
  • `GET /demo/mcp/resource?uri=demo://summary`:读取外部 MCP 资源
  • * - * 当 `spring.ai.mcp.client.enabled=false` 时,本 Controller 不会注册。 + * 当 `spring.ai.mcp.client.enabled=false` 时,接口会返回提示,不影响应用启动。 * * @author Lion Li */ @RequiredArgsConstructor @RestController @RequestMapping("/demo/mcp") -@ConditionalOnBean(McpDemoClientService.class) public class McpDemoController { private final McpDemoClientService mcpDemoClientService; @@ -41,8 +40,12 @@ public class McpDemoController { * 查询外部 MCP Server 工具列表。 */ @GetMapping("/tools") - public R>> tools() { - return R.ok(mcpDemoClientService.listRemoteTools()); + public R>> tools() { + try { + return R.ok(mcpDemoClientService.listRemoteTools()); + } catch (IllegalStateException e) { + return R.fail(e.getMessage()); + } } /** @@ -51,7 +54,11 @@ public class McpDemoController { @GetMapping("/receive") public R receive(@RequestParam(defaultValue = "demo_get_data") String toolName, @RequestParam(defaultValue = "1") String id) { - return R.ok(mcpDemoClientService.receiveAndHandle(toolName, Map.of("id", id))); + try { + return R.ok(mcpDemoClientService.receiveAndHandle(toolName, Map.of("id", id))); + } catch (IllegalStateException e) { + return R.fail(e.getMessage()); + } } /** @@ -59,6 +66,10 @@ public class McpDemoController { */ @GetMapping("/resource") public R> resource(@RequestParam(defaultValue = "demo://summary") String uri) { - return R.ok(mcpDemoClientService.readRemoteResource(uri)); + try { + return R.ok(mcpDemoClientService.readRemoteResource(uri)); + } catch (IllegalStateException e) { + return R.fail(e.getMessage()); + } } } diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientHandlers.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientHandlers.java new file mode 100644 index 000000000..fd6d6e6b4 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientHandlers.java @@ -0,0 +1,58 @@ +package org.dromara.demo.mcp; + +import io.modelcontextprotocol.spec.McpSchema; +import org.springframework.ai.mcp.annotation.McpElicitation; +import org.springframework.ai.mcp.annotation.McpSampling; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * MCP Client 能力回调演示。 + *

    + * `@McpSampling` 与 `@McpElicitation` 不是对外提供给 MCP Client 调用的工具, + * 而是 MCP Server 在处理请求时,反向请求 MCP Client 进行模型采样或补充信息时的回调处理器。 + *

    + * 这里提供最小示例,主要用于展示注解写法,并让 Spring AI MCP 注解扫描器能扫描到 + * sampling/elicitation 方法,避免启动时输出 "No sampling methods found" 与 + * "No elicitation methods found" 提示。 + *

    + * Spring AI 2.0.0-M6 要求 `clients` 不能为空。这里使用 `ruoyi-demo` 作为示例客户端名称, + * 实际使用时应与 `spring.ai.mcp.client.*.connections` 中的连接名保持一致。 + * + * @author Lion Li + */ +@Component +public class McpDemoClientHandlers { + + /** + * MCP sampling 回调示例。 + * + * @param request 采样请求 + * @return 采样结果 + */ + @McpSampling(clients = "ruoyi-demo") + public McpSchema.CreateMessageResult sampling(McpSchema.CreateMessageRequest request) { + String text = request.systemPrompt() == null ? "demo sampling response" : request.systemPrompt(); + return new McpSchema.CreateMessageResult( + McpSchema.Role.ASSISTANT, + new McpSchema.TextContent(text), + "ruoyi-demo-sampling", + McpSchema.CreateMessageResult.StopReason.END_TURN + ); + } + + /** + * MCP elicitation 回调示例。 + * + * @param request 补充信息请求 + * @return 补充信息结果 + */ + @McpElicitation(clients = "ruoyi-demo") + public McpSchema.ElicitResult elicitation(McpSchema.ElicitRequest request) { + return new McpSchema.ElicitResult( + McpSchema.ElicitResult.Action.ACCEPT, + Map.of("message", request.message()) + ); + } +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java index da4e3701b..f8983ab7c 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/mcp/McpDemoClientService.java @@ -4,11 +4,12 @@ import org.dromara.common.mcp.core.McpClientTemplate; import org.dromara.common.mcp.core.McpResourceReadResult; import org.dromara.common.mcp.core.McpToolCallResult; import lombok.RequiredArgsConstructor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; +import java.util.function.Function; /** * MCP Client 演示服务,从外部 MCP Server 接收数据后交给业务层处理。 @@ -16,6 +17,7 @@ import java.util.Map; * 本服务会注入 Spring AI 自动创建的多个 `McpSyncClient`。当 * `spring.ai.mcp.client.enabled=true` 且配置了多个 connection 时, * 这里可以统一遍历这些 MCP Server。 + * 未启用 MCP Client 时,本服务仍会注册,避免演示 Controller 影响应用启动。 *

    * 如果要用当前项目自己连自己测试,建议启动两个 `ruoyi-admin` 实例: * 一个作为 server,一个作为 client,client 指向 server 的地址。 @@ -24,10 +26,9 @@ import java.util.Map; */ @Service @RequiredArgsConstructor -@ConditionalOnBean(McpClientTemplate.class) public class McpDemoClientService { - private final McpClientTemplate mcpClientTemplate; + private final ObjectProvider mcpClientTemplateProvider; /** * 查询已接入的 MCP Server 与工具列表。 @@ -35,7 +36,7 @@ public class McpDemoClientService { * @return Server 名称与工具名称列表 */ public Map> listRemoteTools() { - return mcpClientTemplate.listTools(); + return execute(McpClientTemplate::listTools); } /** @@ -46,7 +47,7 @@ public class McpDemoClientService { * @return 工具返回内容 */ public Map callRemoteTool(String toolName, Map arguments) { - return mcpClientTemplate.callTool(toolName, arguments); + return execute(template -> template.callTool(toolName, arguments)); } /** @@ -56,7 +57,7 @@ public class McpDemoClientService { * @return 资源内容 */ public Map readRemoteResource(String uri) { - return mcpClientTemplate.readResource(uri); + return execute(template -> template.readResource(uri)); } /** @@ -72,6 +73,14 @@ public class McpDemoClientService { return new McpDemoHandleResult("MCP", true, callRemoteTool(toolName, arguments)); } + private T execute(Function action) { + McpClientTemplate template = mcpClientTemplateProvider.getIfAvailable(); + if (template == null) { + throw new IllegalStateException("MCP Client 未启用,请配置 spring.ai.mcp.client.enabled=true 并设置 connections"); + } + return action.apply(template); + } + /** * MCP 数据处理结果。 * From cc74b454d8dbac06d6b53df1fd28c1017ddef4e4 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: Mon, 18 May 2026 18:31:54 +0800 Subject: [PATCH 31/35] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9E=20=E8=BF=81?= =?UTF-8?q?=E7=A7=BBcloud=E7=89=88=E6=9C=AC=20ruoyi-common-elasticsearch?= =?UTF-8?q?=20=E6=A8=A1=E5=9D=97=20=E5=8F=AF=E6=8E=A5=E5=85=A5es=E5=BC=80?= =?UTF-8?q?=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 20 +++++ .../src/main/resources/application.yml | 46 ++++++++++ ruoyi-common/pom.xml | 1 + ruoyi-common/ruoyi-common-bom/pom.xml | 7 ++ .../ruoyi-common-elasticsearch/pom.xml | 24 +++++ .../ActuatorEnvironmentPostProcessor.java | 26 ++++++ .../config/EasyEsConfiguration.java | 17 ++++ .../main/resources/META-INF/spring.factories | 2 + ...ot.autoconfigure.AutoConfiguration.imports | 1 + ruoyi-modules/ruoyi-demo/pom.xml | 5 ++ .../demo/controller/EsCrudController.java | 89 +++++++++++++++++++ .../org/dromara/demo/domain/Document.java | 26 ++++++ .../dromara/demo/esmapper/DocumentMapper.java | 7 ++ 13 files changed, 271 insertions(+) create mode 100644 ruoyi-common/ruoyi-common-elasticsearch/pom.xml create mode 100644 ruoyi-common/ruoyi-common-elasticsearch/src/main/java/org/dromara/common/elasticsearch/config/ActuatorEnvironmentPostProcessor.java create mode 100644 ruoyi-common/ruoyi-common-elasticsearch/src/main/java/org/dromara/common/elasticsearch/config/EasyEsConfiguration.java create mode 100644 ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring.factories create mode 100644 ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports create mode 100644 ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/EsCrudController.java create mode 100644 ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/Document.java create mode 100644 ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/esmapper/DocumentMapper.java diff --git a/pom.xml b/pom.xml index 9b72103fc..f3abbf70e 100644 --- a/pom.xml +++ b/pom.xml @@ -52,6 +52,8 @@ 1.8.7 2.5.12 + 3.0.2 + 7.17.28 2.0.0-M6 @@ -325,6 +327,24 @@ ${mica-mqtt.version} + + org.dromara.easy-es + easy-es-boot-starter + ${easy-es.version} + + + + co.elastic.clients + elasticsearch-java + ${elasticsearch-client.version} + + + + org.elasticsearch.client + elasticsearch-rest-client + ${elasticsearch-client.version} + + me.zhyd.oauth diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml index beab6585f..8c9f26c19 100644 --- a/ruoyi-admin/src/main/resources/application.yml +++ b/ruoyi-admin/src/main/resources/application.yml @@ -282,6 +282,52 @@ mqtt.client: truststore-path: truststore-pass: +--- # elasticsearch 功能配置 +# 文档地址: https://www.easy-es.cn/ +# 更改包名需要去 EasyEsConfiguration 修改包扫描(后续版本支持配置文件读取) +easy-es: + # 是否开启EE自动配置 + enable: false + # 兼容模式 + compatible: true + # es连接地址+端口 格式必须为ip:port,如果是集群则可用逗号隔开 + address: localhost:9200 + # 默认为http + schema: http + # 注意ES建议使用账号认证 不使用会报警告日志 + # 如果无账号密码则可不配置此行 + # username: + # 如果无账号密码则可不配置此行 + # password: + # 心跳策略时间 单位:ms + keep-alive-millis: 18000 + # 连接超时时间 单位:ms + connectTimeout: 5000 + # 通信超时时间 单位:ms + socketTimeout: 5000 + # 连接请求超时时间 单位:ms + connectionRequestTimeout: 5000 + # 最大连接数 单位:个 + maxConnTotal: 100 + # 最大连接路由数 单位:个 + maxConnPerRoute: 100 + global-config: + # 开启控制台打印通过本框架生成的DSL语句,默认为开启,测试稳定后的生产环境建议关闭,以提升少量性能 + print-dsl: true + # 异步处理索引是否阻塞主线程 默认阻塞 数据量过大时调整为非阻塞异步进行 项目启动更快 + asyncProcessIndexBlocking: true + db-config: + # 是否开启下划线转驼峰 默认为false + map-underscore-to-camel-case: true + # id生成策略 customize为自定义,id值由用户生成,比如取MySQL中的数据id,如缺省此项配置,则id默认策略为es自动生成 + id-type: customize + # 字段更新策略 默认为not_null + field-strategy: not_null + # 默认开启,查询若指定了size超过1w条时也会自动开启,开启后查询所有匹配数据,若不开启,会导致无法获取数据总条数,其它功能不受影响. + enable-track-total-hits: true + # 数据刷新策略,默认为不刷新 + refresh-policy: immediate + --- # MCP 服务端配置 spring.ai.mcp: server: diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml index fb14db339..17e22d37c 100644 --- a/ruoyi-common/pom.xml +++ b/ruoyi-common/pom.xml @@ -30,6 +30,7 @@ ruoyi-common-satoken ruoyi-common-security ruoyi-common-sms + ruoyi-common-elasticsearch ruoyi-common-web ruoyi-common-translation ruoyi-common-sensitive diff --git a/ruoyi-common/ruoyi-common-bom/pom.xml b/ruoyi-common/ruoyi-common-bom/pom.xml index 809474e19..cba9a6f03 100644 --- a/ruoyi-common/ruoyi-common-bom/pom.xml +++ b/ruoyi-common/ruoyi-common-bom/pom.xml @@ -103,6 +103,13 @@ ${revision} + + + org.dromara + ruoyi-common-elasticsearch + ${revision} + + org.dromara diff --git a/ruoyi-common/ruoyi-common-elasticsearch/pom.xml b/ruoyi-common/ruoyi-common-elasticsearch/pom.xml new file mode 100644 index 000000000..a7332817d --- /dev/null +++ b/ruoyi-common/ruoyi-common-elasticsearch/pom.xml @@ -0,0 +1,24 @@ + + + + org.dromara + ruoyi-common + ${revision} + + 4.0.0 + + ruoyi-common-elasticsearch + + + ruoyi-common-elasticsearch ES搜索引擎服务 + + + + + org.dromara.easy-es + easy-es-boot-starter + + + diff --git a/ruoyi-common/ruoyi-common-elasticsearch/src/main/java/org/dromara/common/elasticsearch/config/ActuatorEnvironmentPostProcessor.java b/ruoyi-common/ruoyi-common-elasticsearch/src/main/java/org/dromara/common/elasticsearch/config/ActuatorEnvironmentPostProcessor.java new file mode 100644 index 000000000..e33a67edc --- /dev/null +++ b/ruoyi-common/ruoyi-common-elasticsearch/src/main/java/org/dromara/common/elasticsearch/config/ActuatorEnvironmentPostProcessor.java @@ -0,0 +1,26 @@ +package org.dromara.common.elasticsearch.config; + +import org.springframework.boot.EnvironmentPostProcessor; +import org.springframework.boot.SpringApplication; +import org.springframework.core.Ordered; +import org.springframework.core.env.ConfigurableEnvironment; + +/** + * 健康检查配置注入 + * + * @author Lion Li + */ +public class ActuatorEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { + + @Override + public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { + String enable = environment.getProperty("easy-es.enable", "false"); + System.setProperty("management.health.elasticsearch.enabled", enable); + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + +} diff --git a/ruoyi-common/ruoyi-common-elasticsearch/src/main/java/org/dromara/common/elasticsearch/config/EasyEsConfiguration.java b/ruoyi-common/ruoyi-common-elasticsearch/src/main/java/org/dromara/common/elasticsearch/config/EasyEsConfiguration.java new file mode 100644 index 000000000..ceb9c2bfe --- /dev/null +++ b/ruoyi-common/ruoyi-common-elasticsearch/src/main/java/org/dromara/common/elasticsearch/config/EasyEsConfiguration.java @@ -0,0 +1,17 @@ +package org.dromara.common.elasticsearch.config; + +import org.dromara.easyes.spring.annotation.EsMapperScan; +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; + +/** + * easy-es 配置 + * + * @author Lion Li + */ +@AutoConfiguration +@ConditionalOnProperty(value = "easy-es.enable", havingValue = "true") +@EsMapperScan("org.dromara.**.esmapper") +public class EasyEsConfiguration { + +} diff --git a/ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring.factories b/ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring.factories new file mode 100644 index 000000000..1a18290aa --- /dev/null +++ b/ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.env.EnvironmentPostProcessor=\ + org.dromara.common.elasticsearch.config.ActuatorEnvironmentPostProcessor diff --git a/ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..b1a5918ec --- /dev/null +++ b/ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +org.dromara.common.elasticsearch.config.EasyEsConfiguration diff --git a/ruoyi-modules/ruoyi-demo/pom.xml b/ruoyi-modules/ruoyi-demo/pom.xml index 847c7d6bf..f3800ccda 100644 --- a/ruoyi-modules/ruoyi-demo/pom.xml +++ b/ruoyi-modules/ruoyi-demo/pom.xml @@ -88,6 +88,11 @@ ruoyi-common-encrypt + + org.dromara + ruoyi-common-elasticsearch + + org.dromara ruoyi-common-push diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/EsCrudController.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/EsCrudController.java new file mode 100644 index 000000000..d3a154801 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/controller/EsCrudController.java @@ -0,0 +1,89 @@ +package org.dromara.demo.controller; + +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.demo.domain.Document; +import org.dromara.demo.esmapper.DocumentMapper; +import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 搜索引擎 crud 演示案例 + * + * @author Lion Li + */ +@ConditionalOnProperty(value = "easy-es.enable", havingValue = "true") +@RequiredArgsConstructor +@RestController +@RequestMapping("/es") +public class EsCrudController { + + private final DocumentMapper documentMapper; + + /** + * 查询(指定) + * + * @param title 标题 + */ + @GetMapping("/select") + public Document select(String title) { + LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper<>(); + wrapper.eq(Document::getTitle, title); + return documentMapper.selectOne(wrapper); + } + + /** + * 搜索(模糊) + * + * @param key 搜索关键字 + */ + @GetMapping("/search") + public List search(String key) { + LambdaEsQueryWrapper wrapper = new LambdaEsQueryWrapper<>(); + wrapper.like(Document::getTitle, key); + return documentMapper.selectList(wrapper); + } + + /** + * 插入 + */ + @PostMapping("/insert") + public Integer insert(@RequestBody Document document) { + return documentMapper.insert(document); + } + + /** + * 更新 + */ + @PutMapping("/update") + public R update(@RequestBody Document document) { + // 测试更新 更新有两种情况 分别演示如下: + // case1: 已知id, 根据id更新 (为了演示方便,此id是从上一步查询中复制过来的,实际业务可以自行查询) + documentMapper.updateById(document); + + // case2: id未知, 根据条件更新 +// LambdaEsUpdateWrapper wrapper = new LambdaEsUpdateWrapper<>(); +// wrapper.like(Document::getTitle, document.getTitle()); +// Document document2 = new Document(); +// document2.setTitle(document.getTitle()); +// document2.setContent(document.getContent()); +// documentMapper.update(document2, wrapper); + + return R.ok(); + } + + /** + * 删除 + * + * @param id 主键 + */ + @DeleteMapping("/delete/{id}") + public R delete(@PathVariable String id) { + // 测试删除数据 删除有两种情况:根据id删或根据条件删 + return R.ok(documentMapper.deleteById(id)); + } + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/Document.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/Document.java new file mode 100644 index 000000000..f9f06dda6 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/domain/Document.java @@ -0,0 +1,26 @@ +package org.dromara.demo.domain; + +import lombok.Data; + +/** + * 文档实体 + */ +@Data +public class Document { + + /** + * es中的唯一id + */ + private String id; + + /** + * 文档标题 + */ + private String title; + + /** + * 文档内容 + */ + private String content; + +} diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/esmapper/DocumentMapper.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/esmapper/DocumentMapper.java new file mode 100644 index 000000000..63c982366 --- /dev/null +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/esmapper/DocumentMapper.java @@ -0,0 +1,7 @@ +package org.dromara.demo.esmapper; + +import org.dromara.demo.domain.Document; +import org.dromara.easyes.core.kernel.BaseEsMapper; + +public interface DocumentMapper extends BaseEsMapper { +} From a51a7f558cd61286506e33a54a27ae2013fc75c7 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: Mon, 18 May 2026 18:40:02 +0800 Subject: [PATCH 32/35] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=20EnvironmentPos?= =?UTF-8?q?tProcessor=20=E5=8C=85=E5=90=8D=E8=A2=AB=E6=9B=B4=E6=94=B9?= =?UTF-8?q?=E5=AF=BC=E8=87=B4=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/META-INF/spring.factories | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring.factories b/ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring.factories index 1a18290aa..7f1b15e80 100644 --- a/ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring.factories +++ b/ruoyi-common/ruoyi-common-elasticsearch/src/main/resources/META-INF/spring.factories @@ -1,2 +1,2 @@ -org.springframework.boot.env.EnvironmentPostProcessor=\ +org.springframework.boot.EnvironmentPostProcessor=\ org.dromara.common.elasticsearch.config.ActuatorEnvironmentPostProcessor From 491aea4e11822888ec4f93ec3ec6112cedbe3684 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: Mon, 18 May 2026 18:47:26 +0800 Subject: [PATCH 33/35] update springboot 4.0.3 => 4.0.6 update springboot-admin 4.0.2 => 4.0.4 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index f3abbf70e..f5c07b83e 100644 --- a/pom.xml +++ b/pom.xml @@ -14,7 +14,7 @@ 5.5.3 - 4.0.3 + 4.0.6 UTF-8 UTF-8 21 @@ -28,7 +28,7 @@ 1.5.6 3.9.1 5.8.44 - 4.0.2 + 4.0.4 4.4.0 2.2.7 4.5.0 From a95790b4650a4c683ba6b8019bd7dc4091e6c490 Mon Sep 17 00:00:00 2001 From: AprilWind <2100166581@qq.com> Date: Wed, 20 May 2026 13:33:14 +0800 Subject: [PATCH 34/35] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=A0=BC=E5=BC=8F=E5=92=8C=E5=8F=AF=E8=AF=BB=E6=80=A7?= =?UTF-8?q?=EF=BC=8C=E7=AE=80=E5=8C=96=E6=9D=A1=E4=BB=B6=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../system/service/impl/SysConfigServiceImpl.java | 4 ++-- .../system/service/impl/SysMenuServiceImpl.java | 6 +++--- .../system/service/impl/SysMessageServiceImpl.java | 8 ++++---- .../service/impl/SysOssConfigServiceImpl.java | 5 +---- .../service/impl/SysPermissionServiceImpl.java | 14 +++++++------- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java index 56862ff6e..cac34e329 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysConfigServiceImpl.java @@ -88,7 +88,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { */ @Override public boolean selectRegisterEnabled() { - return Convert.toBool(this.selectConfigByKey("sys.account.registerUser")); + return Convert.toBool(this.getConfigValue("sys.account.registerUser")); } /** @@ -146,7 +146,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService { @CachePut(cacheNames = CacheNames.SYS_CONFIG, key = "#bo.configKey") @Override public String updateConfig(SysConfigBo bo) { - int row = 0; + int row; SysConfig config = MapstructUtils.convert(bo, SysConfig.class); if (config.getConfigId() != null) { SysConfig temp = configMapper.selectById(config.getConfigId()); 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 a053f48ec..a5cd55c19 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 @@ -354,9 +354,9 @@ public class SysMenuServiceImpl implements ISysMenuService { String routeName = StringUtils.isEmpty(menu.getRouteName()) ? path : menu.getRouteName(); 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(); + .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-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java index 9c8f2f19e..6813e271e 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysMessageServiceImpl.java @@ -209,11 +209,11 @@ public class SysMessageServiceImpl implements ISysMessageService, MessageService // 仅查询30天内消息 .ge(SysMessage::getCreateTime, LocalDateTime.now().minusDays(BOX_DAYS)) // 全局消息 或 当前用户在接收人范围内 - .and(wrapper -> { + .and(wrapper -> wrapper.eq(SysMessage::getSendUserIds, GLOBAL_USER_IDS) - .or() - .findInSet(userId, SysMessage::getSendUserIds); - }) + .or() + .findInSet(userId, SysMessage::getSendUserIds) + ) .orderByDesc(SysMessage::getCreateTime, SysMessage::getMessageId) // 分页查询(只查第一页,最多100条) .list(new Page<>(1, BOX_LIMIT, false)); diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java index e38fce246..f5e74e3b8 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysOssConfigServiceImpl.java @@ -201,10 +201,7 @@ public class SysOssConfigServiceImpl implements ISysOssConfigService { .select(SysOssConfig::getOssConfigId, SysOssConfig::getConfigKey) .eq(SysOssConfig::getConfigKey, sysOssConfig.getConfigKey()) .one(); - if (ObjectUtil.isNotNull(info) && info.getOssConfigId() != ossConfigId) { - return false; - } - return true; + return ObjectUtil.isNull(info) || ObjectUtil.equals(info.getOssConfigId(), ossConfigId); } /** diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java index c24258ec7..75cd2bd50 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysPermissionServiceImpl.java @@ -29,7 +29,7 @@ public class SysPermissionServiceImpl implements ISysPermissionService, Permissi /** * 获取角色数据权限 * - * @param userId 用户id + * @param userId 用户id * @return 角色权限信息 */ @Override @@ -47,7 +47,7 @@ public class SysPermissionServiceImpl implements ISysPermissionService, Permissi /** * 获取菜单数据权限 * - * @param userId 用户id + * @param userId 用户id * @return 菜单权限信息 */ @Override @@ -76,11 +76,11 @@ public class SysPermissionServiceImpl implements ISysPermissionService, Permissi List roleIds = StreamUtils.toList(roles, RoleDTO::getRoleId); Map> permsRoleIds = menuService.selectMenuPermsByRoleIds(roleIds); Map> rolePermsMap = new LinkedHashMap<>(); - permsRoleIds.forEach((roleId, perms) -> { - perms.forEach(perm -> { - rolePermsMap.computeIfAbsent(perm, key -> new ArrayList<>()).add(roleId); - }); - }); + permsRoleIds.forEach((roleId, perms) -> + perms.forEach(perm -> + rolePermsMap.computeIfAbsent(perm, key -> new ArrayList<>()).add(roleId) + ) + ); return rolePermsMap; } } From 67cf459da7dc977b6554ba1a1de16ab1a48ff768 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: Thu, 21 May 2026 09:49:17 +0800 Subject: [PATCH 35/35] =?UTF-8?q?update=20=E4=BC=98=E5=8C=96=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E5=B7=A5=E5=85=B7=E7=B1=BB=E7=AE=80=E5=8C=96=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/mapper/LambdaCrudChainWrapper.java | 2 +- .../service/impl/ExportExcelServiceImpl.java | 9 ++++----- .../dromara/job/snailjob/AlipayBillTask.java | 8 ++++---- .../dromara/job/snailjob/SummaryBillTask.java | 6 +++--- .../job/snailjob/TestMapJobAnnotation.java | 7 +++---- .../job/snailjob/TestMapReduceAnnotation1.java | 13 ++++++------- .../dromara/job/snailjob/WechatBillTask.java | 8 ++++---- .../listener/WorkflowGlobalListener.java | 11 ++++++----- .../service/impl/FlwNodeExtServiceImpl.java | 2 +- .../impl/FlwTaskAssigneeServiceImpl.java | 9 +++++---- .../service/impl/FlwTaskServiceImpl.java | 17 +++++++++-------- 11 files changed, 46 insertions(+), 46 deletions(-) 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 4e8cff3ad..10cde6708 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 @@ -189,7 +189,7 @@ public class LambdaCrudChainWrapper extends AbstractLambdaWrapper setIfText(SFunction column, String value) { - return set(org.dromara.common.core.utils.StringUtils.isNotBlank(value), column, value); + return set(StringUtils.isNotBlank(value), column, value); } /** diff --git a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java index 45739023f..97a0710ab 100644 --- a/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java +++ b/ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java @@ -1,13 +1,13 @@ package org.dromara.demo.service.impl; import cn.hutool.core.util.RandomUtil; -import cn.hutool.core.util.StrUtil; import org.apache.fesod.sheet.write.metadata.WriteSheet; import jakarta.servlet.http.HttpServletResponse; import lombok.Data; import lombok.RequiredArgsConstructor; import org.dromara.common.core.constant.SystemConstants; import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.excel.core.DropDownOptions; import org.dromara.common.excel.utils.ExcelBuilder; import org.dromara.common.excel.utils.ExcelWriterWrapper; @@ -18,7 +18,6 @@ import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; /** * 导出下拉框Excel示例 @@ -109,12 +108,12 @@ public class ExportExcelServiceImpl implements IExportExcelService { private String buildOptions(List cityDataList, Integer id) { Map> groupByIdMap = - cityDataList.stream().collect(Collectors.groupingBy(DemoCityData::getId)); + StreamUtils.groupByKey(cityDataList, DemoCityData::getId); if (groupByIdMap.containsKey(id)) { DemoCityData demoCityData = groupByIdMap.get(id).get(0); return DropDownOptions.createOptionValue(demoCityData.getName(), demoCityData.getId()); } else { - return StrUtil.EMPTY; + return StringUtils.EMPTY; } } @@ -201,7 +200,7 @@ public class ExportExcelServiceImpl implements IExportExcelService { */ private void selectParentData(List parentList, List sonList) { Map> parentGroupByIdMap = - parentList.stream().collect(Collectors.groupingBy(DemoCityData::getId)); + StreamUtils.groupByKey(parentList, DemoCityData::getId); sonList.forEach(everySon -> { if (parentGroupByIdMap.containsKey(everySon.getPid())) { diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java index ee2904add..75ddfb002 100644 --- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/AlipayBillTask.java @@ -1,11 +1,11 @@ package org.dromara.job.snailjob; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; import com.aizuda.snailjob.client.job.core.dto.JobArgs; import com.aizuda.snailjob.common.log.SnailJobLog; import com.aizuda.snailjob.model.dto.ExecuteResult; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.job.entity.BillDTO; import org.springframework.stereotype.Component; @@ -25,8 +25,8 @@ public class AlipayBillTask { public ExecuteResult jobExecute(JobArgs jobArgs) throws InterruptedException { // 设置清算日期 String settlementDate = (String) jobArgs.getWfContext().get("settlementDate"); - if (StrUtil.equals(settlementDate, "sysdate")) { - settlementDate = DateUtil.today(); + if (StringUtils.equals(settlementDate, "sysdate")) { + settlementDate = DateUtils.getDate(); } BillDTO billDTO = new BillDTO(23456789L, "alipay", settlementDate, new BigDecimal("2345.67")); // 把billDTO对象放入上下文进行传递 diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java index 544339b06..405a4d419 100644 --- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/SummaryBillTask.java @@ -1,10 +1,10 @@ package org.dromara.job.snailjob; -import cn.hutool.core.util.StrUtil; import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; import com.aizuda.snailjob.client.job.core.dto.JobArgs; import com.aizuda.snailjob.common.log.SnailJobLog; import com.aizuda.snailjob.model.dto.ExecuteResult; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.job.entity.BillDTO; import org.springframework.stereotype.Component; @@ -25,14 +25,14 @@ public class SummaryBillTask { // 获得微信账单 BigDecimal wechatAmount = BigDecimal.valueOf(0); String wechat = (String) jobArgs.getWfContext("wechat"); - if (StrUtil.isNotBlank(wechat)) { + if (StringUtils.isNotBlank(wechat)) { BillDTO wechatBillDTO = JsonUtils.parseObject(wechat, BillDTO.class); wechatAmount = wechatBillDTO.billAmount(); } // 获得支付宝账单 BigDecimal alipayAmount = BigDecimal.valueOf(0); String alipay = (String) jobArgs.getWfContext("alipay"); - if (StrUtil.isNotBlank(alipay)) { + if (StringUtils.isNotBlank(alipay)) { BillDTO alipayBillDTO = JsonUtils.parseObject(alipay, BillDTO.class); alipayAmount = alipayBillDTO.billAmount(); } diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java index 8507092f6..b00b35d0e 100644 --- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapJobAnnotation.java @@ -8,11 +8,11 @@ import com.aizuda.snailjob.client.job.core.annotation.MapExecutor; import com.aizuda.snailjob.client.job.core.dto.MapArgs; import com.aizuda.snailjob.common.log.SnailJobLog; import com.aizuda.snailjob.model.dto.ExecuteResult; +import org.dromara.common.core.utils.StreamUtils; import org.springframework.stereotype.Component; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; import java.util.stream.IntStream; /** @@ -30,9 +30,8 @@ public class TestMapJobAnnotation { public ExecuteResult doJobMapExecute(MapArgs mapArgs, MapHandler mapHandler) { // 生成1~200数值并分片 int partitionSize = 50; - List> partition = IntStream.rangeClosed(1, 200) - .boxed() - .collect(Collectors.groupingBy(i -> (i - 1) / partitionSize)) + List sourceList = IntStream.rangeClosed(1, 200).boxed().toList(); + List> partition = StreamUtils.groupByKey(sourceList, i -> (i - 1) / partitionSize) .values() .stream() .toList(); diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java index 17aa9e461..5a5ee4e47 100644 --- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/TestMapReduceAnnotation1.java @@ -10,11 +10,11 @@ import com.aizuda.snailjob.client.job.core.dto.MapArgs; import com.aizuda.snailjob.client.job.core.dto.ReduceArgs; import com.aizuda.snailjob.common.log.SnailJobLog; import com.aizuda.snailjob.model.dto.ExecuteResult; +import org.dromara.common.core.utils.StreamUtils; import org.springframework.stereotype.Component; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.stream.Collectors; import java.util.stream.IntStream; /** @@ -31,12 +31,11 @@ public class TestMapReduceAnnotation1 { @MapExecutor public ExecuteResult rootMapExecute(MapArgs mapArgs, MapHandler mapHandler) { int partitionSize = 50; - List> partition = IntStream.rangeClosed(1, 200) - .boxed() - .collect(Collectors.groupingBy(i -> (i - 1) / partitionSize)) - .values() - .stream() - .toList(); + List sourceList = IntStream.rangeClosed(1, 200).boxed().toList(); + List> partition = StreamUtils.groupByKey(sourceList, i -> (i - 1) / partitionSize) + .values() + .stream() + .toList(); SnailJobLog.REMOTE.info("端口:{}完成分配任务", SpringUtil.getProperty("server.port")); return mapHandler.doMap(partition, "doCalc"); } diff --git a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java index adeb638e0..9b13c52da 100644 --- a/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java +++ b/ruoyi-modules/ruoyi-job/src/main/java/org/dromara/job/snailjob/WechatBillTask.java @@ -1,11 +1,11 @@ package org.dromara.job.snailjob; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; import com.aizuda.snailjob.client.job.core.annotation.JobExecutor; import com.aizuda.snailjob.client.job.core.dto.JobArgs; import com.aizuda.snailjob.common.log.SnailJobLog; import com.aizuda.snailjob.model.dto.ExecuteResult; +import org.dromara.common.core.utils.DateUtils; +import org.dromara.common.core.utils.StringUtils; import org.dromara.common.json.utils.JsonUtils; import org.dromara.job.entity.BillDTO; import org.springframework.stereotype.Component; @@ -26,8 +26,8 @@ public class WechatBillTask { // 从上下文中获得清算日期并设置,如果上下文中清算日期 // 是sysdate设置为当前日期;否则取管理页面设置的值 String settlementDate = (String) jobArgs.getWfContext().get("settlementDate"); - if (StrUtil.equals(settlementDate, "sysdate")) { - settlementDate = DateUtil.today(); + if (StringUtils.equals(settlementDate, "sysdate")) { + settlementDate = DateUtils.getDate(); } BillDTO billDTO = new BillDTO(123456789L, "wechat", settlementDate, new BigDecimal("1234.56")); // 把billDTO对象放入上下文进行传递 diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java index 66f93bb49..914a6c00d 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/listener/WorkflowGlobalListener.java @@ -5,7 +5,6 @@ import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.TypeReference; import cn.hutool.core.map.MapUtil; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.enums.BusinessStatusEnum; @@ -48,6 +47,8 @@ import java.util.*; @RequiredArgsConstructor public class WorkflowGlobalListener implements GlobalListener { + private static final String NODE_KEY_SEPARATOR = ":"; + private final IFlwTaskService flwTaskService; private final IFlwInstanceService flwInstanceService; private final FlowProcessEventHandler flowProcessEventHandler; @@ -138,7 +139,7 @@ public class WorkflowGlobalListener implements GlobalListener { * @param taskStatus 任务状态 */ private void processTaskPermission(Map variable, Task flowTask, String taskStatus) { - String nodeKey = taskStatus + StrUtil.COLON + flowTask.getNodeCode(); + String nodeKey = taskStatus + NODE_KEY_SEPARATOR + flowTask.getNodeCode(); // 检查是否存在状态相关的变量 if (!variable.containsKey(nodeKey)) { @@ -157,9 +158,9 @@ public class WorkflowGlobalListener implements GlobalListener { } // 分割用户ID并设置权限列表 - String[] userIdArray = userIds.split(StringUtils.SEPARATOR); - if (userIdArray.length > 0) { - flowTask.setPermissionList(List.of(userIdArray)); + List userIdList = StringUtils.str2List(userIds, StringUtils.SEPARATOR, true, true); + if (CollUtil.isNotEmpty(userIdList)) { + flowTask.setPermissionList(userIdList); // 移除已处理的状态变量 variable.remove(nodeKey); FlowEngine.insService().removeVariables(flowTask.getInstanceId(), nodeKey); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java index 5f84b1b57..5a9dab190 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwNodeExtServiceImpl.java @@ -259,7 +259,7 @@ public class FlwNodeExtServiceImpl implements NodeExtService, IFlwNodeExtService } else if (VariablesEnum.class.getSimpleName().equals(code)) { // 解析自定义参数 // 将 key=value 字符串拆分为 Map - Map variables = Arrays.stream(StringUtils.split(value, StringUtils.SEPARATOR)) + Map variables = StringUtils.str2List(value, StringUtils.SEPARATOR, true, true).stream() .map(s -> StringUtils.split(s, "=")) .filter(arr -> arr.length == 2) .collect(Collectors.toMap(arr -> arr[0], arr -> arr[1])); diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java index 58a3af63b..fb1de2903 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskAssigneeServiceImpl.java @@ -5,7 +5,6 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Pair; import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.utils.DateUtils; @@ -45,6 +44,8 @@ import java.util.stream.Collectors; public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, HandlerSelectService { private static final String DEFAULT_GROUP_NAME = "默认分组"; + private static final String STORAGE_ID_SEPARATOR = ":"; + private final TaskAssigneeService taskAssigneeService; private final UserService userService; private final DeptService deptService; @@ -208,7 +209,7 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand return List.of(); } Map> typeIdMap = new EnumMap<>(TaskAssigneeEnum.class); - for (String storageId : storageIds.split(StringUtils.SEPARATOR)) { + for (String storageId : StringUtils.str2List(storageIds, StringUtils.SEPARATOR, true, true)) { Pair parsed = this.parseStorageId(storageId); if (parsed != null) { typeIdMap.computeIfAbsent(parsed.getKey(), k -> new ArrayList<>()).add(parsed.getValue()); @@ -311,11 +312,11 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand return Pair.of(TaskAssigneeEnum.SPEL, storageId); } try { - String[] parts = storageId.split(StrUtil.COLON, 2); + String[] parts = storageId.split(STORAGE_ID_SEPARATOR, 2); if (parts.length < 2) { return Pair.of(TaskAssigneeEnum.USER, parts[0]); } else { - TaskAssigneeEnum type = TaskAssigneeEnum.fromCode(parts[0] + StrUtil.COLON); + TaskAssigneeEnum type = TaskAssigneeEnum.fromCode(parts[0] + STORAGE_ID_SEPARATOR); return Pair.of(type, parts[1]); } } catch (Exception e) { diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java index 42773bcd7..fec9f3058 100644 --- a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/FlwTaskServiceImpl.java @@ -5,7 +5,6 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.lang.Dict; import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; import com.baomidou.lock.annotation.Lock4j; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import lombok.RequiredArgsConstructor; @@ -77,6 +76,8 @@ import static org.dromara.workflow.common.constant.FlowConstant.*; @Service public class FlwTaskServiceImpl implements IFlwTaskService { + private static final String NODE_KEY_SEPARATOR = ":"; + private final TaskService taskService; private final InsService insService; private final DefService defService; @@ -311,17 +312,17 @@ public class FlwTaskServiceImpl implements IFlwTaskService { if (StringUtils.isNotBlank(userIds)) { Set hashSet = new HashSet<>(); //弹窗传入的选人 - List popUserIds = Arrays.asList(entry.getValue().toString().split(StringUtils.SEPARATOR)); + List popUserIds = StringUtils.str2List(entry.getValue().toString(), StringUtils.SEPARATOR, true, true); //已有的选人 - List variableUserIds = Arrays.asList(userIds.split(StringUtils.SEPARATOR)); + List variableUserIds = StringUtils.str2List(userIds, StringUtils.SEPARATOR, true, true); hashSet.addAll(popUserIds); hashSet.addAll(variableUserIds); - map.put(TaskStatusEnum.PASS.getStatus() + StrUtil.COLON + entry.getKey(), StringUtils.joinComma(hashSet)); - map.put(TaskStatusEnum.BACK.getStatus() + StrUtil.COLON + entry.getKey(), StringUtils.joinComma(hashSet)); + map.put(TaskStatusEnum.PASS.getStatus() + NODE_KEY_SEPARATOR + entry.getKey(), StringUtils.joinComma(hashSet)); + map.put(TaskStatusEnum.BACK.getStatus() + NODE_KEY_SEPARATOR + entry.getKey(), StringUtils.joinComma(hashSet)); } } else { - map.put(TaskStatusEnum.PASS.getStatus() + StrUtil.COLON + entry.getKey(), entry.getValue()); - map.put(TaskStatusEnum.BACK.getStatus() + StrUtil.COLON + entry.getKey(), entry.getValue()); + map.put(TaskStatusEnum.PASS.getStatus() + NODE_KEY_SEPARATOR + entry.getKey(), entry.getValue()); + map.put(TaskStatusEnum.BACK.getStatus() + NODE_KEY_SEPARATOR + entry.getKey(), entry.getValue()); } } return map; @@ -838,7 +839,7 @@ public class FlwTaskServiceImpl implements IFlwTaskService { // 操作执行成功后再发送消息 if (result && CollUtil.isNotEmpty(bo.getMessageType())) { List userIdList = new ArrayList<>(); - if (StrUtil.isNotBlank(bo.getUserId())) { + if (StringUtils.isNotBlank(bo.getUserId())) { userIdList.add(Convert.toLong(bo.getUserId())); } if (CollUtil.isNotEmpty(bo.getUserIds())) {