diff --git a/pom.xml b/pom.xml
index 48a6f0589d..e4770100e7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
https://github.com/YunaiV/ruoyi-vue-pro
- 2.6.1-SNAPSHOT
+ 2025.08-SNAPSHOT
17
${java.version}
diff --git a/yudao-dependencies/pom.xml b/yudao-dependencies/pom.xml
index 27ad06fe5d..668249863a 100644
--- a/yudao-dependencies/pom.xml
+++ b/yudao-dependencies/pom.xml
@@ -14,7 +14,7 @@
https://github.com/YunaiV/ruoyi-vue-pro
- 2.6.1-SNAPSHOT
+ 2025.08-SNAPSHOT
1.6.0
3.4.5
@@ -24,9 +24,9 @@
1.2.24
3.5.19
- 3.5.10.1
+ 3.5.12
+ 1.5.4
4.3.1
- 1.4.13
3.0.6
3.41.0
8.1.3.140
@@ -54,7 +54,7 @@
1.6.3
5.8.35
6.0.0-M19
- 4.0.3
+ 1.2.0
2.4.1
1.2.83
33.4.8-jre
@@ -69,8 +69,6 @@
0.9.0
4.5.13
- 2.17.0
- 1.27.1
2.30.14
1.16.7
1.4.0
@@ -480,20 +478,11 @@
- com.alibaba
- easyexcel
- ${easyexcel.version}
-
-
- commons-io
- commons-io
- ${commons-io.version}
-
-
- org.apache.commons
- commons-compress
- ${commons-compress.version}
+ cn.idev.excel
+ fastexcel
+ ${fastexcel.version}
+
org.apache.tika
tika-core
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java
index ac74103154..afb9cd3063 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/CommonResult.java
@@ -25,16 +25,16 @@ public class CommonResult implements Serializable {
* @see ErrorCode#getCode()
*/
private Integer code;
- /**
- * 返回数据
- */
- private T data;
/**
* 错误提示,用户可阅读
*
* @see ErrorCode#getMsg() ()
*/
private String msg;
+ /**
+ * 返回数据
+ */
+ private T data;
/**
* 将传入的 result 对象,转换成另外一个泛型结果的对象
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageResult.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageResult.java
index ff9087a81f..47c59d1d9e 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageResult.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/pojo/PageResult.java
@@ -11,12 +11,12 @@ import java.util.List;
@Data
public final class PageResult implements Serializable {
- @Schema(description = "数据", requiredMode = Schema.RequiredMode.REQUIRED)
- private List list;
-
@Schema(description = "总量", requiredMode = Schema.RequiredMode.REQUIRED)
private Long total;
+ @Schema(description = "数据", requiredMode = Schema.RequiredMode.REQUIRED)
+ private List list;
+
public PageResult() {
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java
index 12a6e17246..4d9168ebdb 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/cache/CacheUtils.java
@@ -14,6 +14,13 @@ import java.util.concurrent.Executors;
*/
public class CacheUtils {
+ /**
+ * 异步刷新的 LoadingCache 最大缓存数量
+ *
+ * @see 本地缓存 CacheUtils 工具类建议
+ */
+ private static final Integer CACHE_MAX_SIZE = 10000;
+
/**
* 构建异步刷新的 LoadingCache 对象
*
@@ -29,6 +36,7 @@ public class CacheUtils {
*/
public static LoadingCache buildAsyncReloadingCache(Duration duration, CacheLoader loader) {
return CacheBuilder.newBuilder()
+ .maximumSize(CACHE_MAX_SIZE)
// 只阻塞当前数据加载线程,其他线程返回旧值
.refreshAfterWrite(duration)
// 通过 asyncReloading 实现全异步加载,包括 refreshAfterWrite 被阻塞的加载线程
@@ -43,7 +51,11 @@ public class CacheUtils {
* @return LoadingCache 对象
*/
public static LoadingCache buildCache(Duration duration, CacheLoader loader) {
- return CacheBuilder.newBuilder().refreshAfterWrite(duration).build(loader);
+ return CacheBuilder.newBuilder()
+ .maximumSize(CACHE_MAX_SIZE)
+ // 只阻塞当前数据加载线程,其他线程返回旧值
+ .refreshAfterWrite(duration)
+ .build(loader);
}
}
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
index b51a838c69..d6051e85fe 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/DateUtils.java
@@ -74,30 +74,30 @@ public class DateUtils {
* 创建指定时间
*
* @param year 年
- * @param mouth 月
+ * @param month 月
* @param day 日
* @return 指定时间
*/
- public static Date buildTime(int year, int mouth, int day) {
- return buildTime(year, mouth, day, 0, 0, 0);
+ public static Date buildTime(int year, int month, int day) {
+ return buildTime(year, month, day, 0, 0, 0);
}
/**
* 创建指定时间
*
* @param year 年
- * @param mouth 月
+ * @param month 月
* @param day 日
* @param hour 小时
* @param minute 分钟
* @param second 秒
* @return 指定时间
*/
- public static Date buildTime(int year, int mouth, int day,
+ public static Date buildTime(int year, int month, int day,
int hour, int minute, int second) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, year);
- calendar.set(Calendar.MONTH, mouth - 1);
+ calendar.set(Calendar.MONTH, month - 1);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
diff --git a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
index 8d18479c81..26b3961685 100644
--- a/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
+++ b/yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
@@ -69,17 +69,17 @@ public class LocalDateTimeUtils {
* 创建指定时间
*
* @param year 年
- * @param mouth 月
+ * @param month 月
* @param day 日
* @return 指定时间
*/
- public static LocalDateTime buildTime(int year, int mouth, int day) {
- return LocalDateTime.of(year, mouth, day, 0, 0, 0);
+ public static LocalDateTime buildTime(int year, int month, int day) {
+ return LocalDateTime.of(year, month, day, 0, 0, 0);
}
- public static LocalDateTime[] buildBetweenTime(int year1, int mouth1, int day1,
- int year2, int mouth2, int day2) {
- return new LocalDateTime[]{buildTime(year1, mouth1, day1), buildTime(year2, mouth2, day2)};
+ public static LocalDateTime[] buildBetweenTime(int year1, int month1, int day1,
+ int year2, int month2, int day2) {
+ return new LocalDateTime[]{buildTime(year1, month1, day1), buildTime(year2, month2, day2)};
}
/**
diff --git a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDeptDataPermissionAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDeptDataPermissionAutoConfiguration.java
index ab5b3bc7ec..46b6ca7c59 100644
--- a/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDeptDataPermissionAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/config/YudaoDeptDataPermissionAutoConfiguration.java
@@ -18,7 +18,7 @@ import java.util.List;
*/
@AutoConfiguration
@ConditionalOnClass(LoginUser.class)
-@ConditionalOnBean(value = {PermissionCommonApi.class, DeptDataPermissionRuleCustomizer.class})
+@ConditionalOnBean(value = {DeptDataPermissionRuleCustomizer.class})
public class YudaoDeptDataPermissionAutoConfiguration {
@Bean
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/pom.xml b/yudao-framework/yudao-spring-boot-starter-excel/pom.xml
index 0413986a64..b1218183cb 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/pom.xml
+++ b/yudao-framework/yudao-spring-boot-starter-excel/pom.xml
@@ -42,8 +42,14 @@
- com.alibaba
- easyexcel
+ cn.idev.excel
+ fastexcel
+
+
+
+ jakarta.validation
+ jakarta.validation-api
+ provided
@@ -51,11 +57,6 @@
guava
-
- org.apache.commons
- commons-compress
-
-
cn.iocoder.boot
yudao-spring-boot-starter-biz-ip
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java
index 9fc67bfe7f..2a2350f02b 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/core/DictFrameworkUtils.java
@@ -76,4 +76,9 @@ public class DictFrameworkUtils {
return dictData!= null ? dictData.getValue(): null;
}
+ @SneakyThrows
+ public static List getDictDataValueList(String dictType) {
+ List dictDatas = GET_DICT_DATA_CACHE.get(dictType);
+ return convertList(dictDatas, DictDataRespDTO::getValue);
+ }
}
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDict.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDict.java
new file mode 100644
index 0000000000..de7498775c
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDict.java
@@ -0,0 +1,38 @@
+package cn.iocoder.yudao.framework.dict.validation;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({
+ ElementType.METHOD,
+ ElementType.FIELD,
+ ElementType.ANNOTATION_TYPE,
+ ElementType.CONSTRUCTOR,
+ ElementType.PARAMETER,
+ ElementType.TYPE_USE
+})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Constraint(
+ validatedBy = {InDictValidator.class, InDictCollectionValidator.class}
+)
+public @interface InDict {
+
+ /**
+ * 数据字典 type
+ */
+ String type();
+
+ String message() default "必须在指定范围 {value}";
+
+ Class>[] groups() default {};
+
+ Class extends Payload>[] payload() default {};
+
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDictCollectionValidator.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDictCollectionValidator.java
new file mode 100644
index 0000000000..a7184b066a
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDictCollectionValidator.java
@@ -0,0 +1,43 @@
+package cn.iocoder.yudao.framework.dict.validation;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+import java.util.Collection;
+import java.util.List;
+
+public class InDictCollectionValidator implements ConstraintValidator> {
+
+ private String dictType;
+
+ @Override
+ public void initialize(InDict annotation) {
+ this.dictType = annotation.type();
+ }
+
+ @Override
+ public boolean isValid(Collection> list, ConstraintValidatorContext context) {
+ // 为空时,默认不校验,即认为通过
+ if (CollUtil.isEmpty(list)) {
+ return true;
+ }
+ // 校验全部通过
+ List dbValues = DictFrameworkUtils.getDictDataValueList(dictType);
+ boolean match = list.stream().allMatch(v -> dbValues.stream()
+ .anyMatch(dbValue -> dbValue.equalsIgnoreCase(v.toString())));
+ if (match) {
+ return true;
+ }
+
+ // 校验不通过,自定义提示语句
+ context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
+ context.buildConstraintViolationWithTemplate(
+ context.getDefaultConstraintMessageTemplate().replaceAll("\\{value}", dbValues.toString())
+ ).addConstraintViolation(); // 重新添加错误提示语句
+ return false;
+ }
+
+}
+
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDictValidator.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDictValidator.java
new file mode 100644
index 0000000000..b67f017750
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/dict/validation/InDictValidator.java
@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.framework.dict.validation;
+
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+import java.util.List;
+
+public class InDictValidator implements ConstraintValidator {
+
+ private String dictType;
+
+ @Override
+ public void initialize(InDict annotation) {
+ this.dictType = annotation.type();
+ }
+
+ @Override
+ public boolean isValid(Object value, ConstraintValidatorContext context) {
+ // 为空时,默认不校验,即认为通过
+ if (value == null) {
+ return true;
+ }
+ // 校验通过
+ final List values = DictFrameworkUtils.getDictDataValueList(dictType);
+ boolean match = values.stream().anyMatch(v -> StrUtil.equalsIgnoreCase(v, value.toString()));
+ if (match) {
+ return true;
+ }
+
+ // 校验不通过,自定义提示语句
+ context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
+ context.buildConstraintViolationWithTemplate(
+ context.getDefaultConstraintMessageTemplate().replaceAll("\\{value}", values.toString())
+ ).addConstraintViolation(); // 重新添加错误提示语句
+ return false;
+ }
+
+}
+
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java
index 9778b17aea..b5ca863173 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/AreaConvert.java
@@ -3,11 +3,11 @@ package cn.iocoder.yudao.framework.excel.core.convert;
import cn.hutool.core.convert.Convert;
import cn.iocoder.yudao.framework.ip.core.Area;
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
-import com.alibaba.excel.converters.Converter;
-import com.alibaba.excel.enums.CellDataTypeEnum;
-import com.alibaba.excel.metadata.GlobalConfiguration;
-import com.alibaba.excel.metadata.data.ReadCellData;
-import com.alibaba.excel.metadata.property.ExcelContentProperty;
+import cn.idev.excel.converters.Converter;
+import cn.idev.excel.enums.CellDataTypeEnum;
+import cn.idev.excel.metadata.GlobalConfiguration;
+import cn.idev.excel.metadata.data.ReadCellData;
+import cn.idev.excel.metadata.property.ExcelContentProperty;
import lombok.extern.slf4j.Slf4j;
/**
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java
index e393195ed1..b9e0dcb735 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/DictConvert.java
@@ -3,12 +3,12 @@ package cn.iocoder.yudao.framework.excel.core.convert;
import cn.hutool.core.convert.Convert;
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
-import com.alibaba.excel.converters.Converter;
-import com.alibaba.excel.enums.CellDataTypeEnum;
-import com.alibaba.excel.metadata.GlobalConfiguration;
-import com.alibaba.excel.metadata.data.ReadCellData;
-import com.alibaba.excel.metadata.data.WriteCellData;
-import com.alibaba.excel.metadata.property.ExcelContentProperty;
+import cn.idev.excel.converters.Converter;
+import cn.idev.excel.enums.CellDataTypeEnum;
+import cn.idev.excel.metadata.GlobalConfiguration;
+import cn.idev.excel.metadata.data.ReadCellData;
+import cn.idev.excel.metadata.data.WriteCellData;
+import cn.idev.excel.metadata.property.ExcelContentProperty;
import lombok.extern.slf4j.Slf4j;
/**
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/JsonConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/JsonConvert.java
index 0d4794e5fa..6958c32e0c 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/JsonConvert.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/JsonConvert.java
@@ -1,11 +1,11 @@
package cn.iocoder.yudao.framework.excel.core.convert;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import com.alibaba.excel.converters.Converter;
-import com.alibaba.excel.enums.CellDataTypeEnum;
-import com.alibaba.excel.metadata.GlobalConfiguration;
-import com.alibaba.excel.metadata.data.WriteCellData;
-import com.alibaba.excel.metadata.property.ExcelContentProperty;
+import cn.idev.excel.converters.Converter;
+import cn.idev.excel.enums.CellDataTypeEnum;
+import cn.idev.excel.metadata.GlobalConfiguration;
+import cn.idev.excel.metadata.data.WriteCellData;
+import cn.idev.excel.metadata.property.ExcelContentProperty;
/**
* Excel Json 转换器
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java
index ee66fe7dec..9ed0bd581f 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/convert/MoneyConvert.java
@@ -1,10 +1,10 @@
package cn.iocoder.yudao.framework.excel.core.convert;
-import com.alibaba.excel.converters.Converter;
-import com.alibaba.excel.enums.CellDataTypeEnum;
-import com.alibaba.excel.metadata.GlobalConfiguration;
-import com.alibaba.excel.metadata.data.WriteCellData;
-import com.alibaba.excel.metadata.property.ExcelContentProperty;
+import cn.idev.excel.converters.Converter;
+import cn.idev.excel.enums.CellDataTypeEnum;
+import cn.idev.excel.metadata.GlobalConfiguration;
+import cn.idev.excel.metadata.data.WriteCellData;
+import cn.idev.excel.metadata.property.ExcelContentProperty;
import java.math.BigDecimal;
import java.math.RoundingMode;
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java
new file mode 100644
index 0000000000..49a5b31572
--- /dev/null
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/ColumnWidthMatchStyleStrategy.java
@@ -0,0 +1,78 @@
+package cn.iocoder.yudao.framework.excel.core.handler;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.idev.excel.enums.CellDataTypeEnum;
+import cn.idev.excel.metadata.Head;
+import cn.idev.excel.metadata.data.WriteCellData;
+import cn.idev.excel.util.MapUtils;
+import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
+import cn.idev.excel.write.style.column.AbstractColumnWidthStyleStrategy;
+import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
+import org.apache.poi.ss.usermodel.Cell;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Excel 自适应列宽处理器
+ *
+ * 相比 {@link LongestMatchColumnWidthStyleStrategy} 来说,额外处理了 DATE 类型!
+ *
+ * @see 添加自适应列宽处理器,并替换默认列宽策略
+ * @author hmb
+ */
+public class ColumnWidthMatchStyleStrategy extends AbstractColumnWidthStyleStrategy {
+
+ private static final int MAX_COLUMN_WIDTH = 255;
+
+ private final Map> cache = MapUtils.newHashMapWithExpectedSize(8);
+
+ @Override
+ protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List> cellDataList, Cell cell,
+ Head head, Integer relativeRowIndex, Boolean isHead) {
+ boolean needSetWidth = isHead || CollUtil.isNotEmpty(cellDataList);
+ if (!needSetWidth) {
+ return;
+ }
+ Map maxColumnWidthMap = cache.computeIfAbsent(writeSheetHolder.getSheetNo(),
+ key -> new HashMap<>(16));
+ Integer columnWidth = dataLength(cellDataList, cell, isHead);
+ if (columnWidth < 0) {
+ return;
+ }
+ if (columnWidth > MAX_COLUMN_WIDTH) {
+ columnWidth = MAX_COLUMN_WIDTH;
+ }
+ Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
+ if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
+ maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
+ writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
+ }
+ }
+
+ @SuppressWarnings("EnhancedSwitchMigration")
+ private Integer dataLength(List> cellDataList, Cell cell, Boolean isHead) {
+ if (isHead) {
+ return cell.getStringCellValue().getBytes().length;
+ }
+ WriteCellData> cellData = cellDataList.get(0);
+ CellDataTypeEnum type = cellData.getType();
+ if (type == null) {
+ return -1;
+ }
+ switch (type) {
+ case STRING:
+ return cellData.getStringValue().getBytes().length;
+ case BOOLEAN:
+ return cellData.getBooleanValue().toString().getBytes().length;
+ case NUMBER:
+ return cellData.getNumberValue().toString().getBytes().length;
+ case DATE:
+ return cellData.getDateValue().toString().getBytes().length;
+ default:
+ return -1;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java
index 8e3e28eb44..c55e1210a2 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/handler/SelectSheetWriteHandler.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.framework.excel.core.handler;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
-import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.poi.excel.ExcelUtil;
@@ -11,12 +10,12 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.dict.core.DictFrameworkUtils;
import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;
import cn.iocoder.yudao.framework.excel.core.function.ExcelColumnSelectFunction;
-import com.alibaba.excel.annotation.ExcelIgnore;
-import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
-import com.alibaba.excel.annotation.ExcelProperty;
-import com.alibaba.excel.write.handler.SheetWriteHandler;
-import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
-import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
+import cn.idev.excel.annotation.ExcelIgnore;
+import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
+import cn.idev.excel.annotation.ExcelProperty;
+import cn.idev.excel.write.handler.SheetWriteHandler;
+import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
+import cn.idev.excel.write.metadata.holder.WriteWorkbookHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFDataValidation;
import org.apache.poi.ss.usermodel.*;
@@ -87,7 +86,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
/**
* 判断字段是否是静态的、最终的、 transient 的
- * 原因:EasyExcel 默认是忽略 static final 或 transient 的字段,所以需要判断
+ * 原因:FastExcel 默认是忽略 static final 或 transient 的字段,所以需要判断
*
* @param field 字段
* @return 是否是静态的、最终的、transient 的
@@ -185,4 +184,4 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
writeSheetHolder.getSheet().addValidationData(validation);
}
-}
\ No newline at end of file
+}
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
index eb037d9e17..f05d3e51e5 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/core/util/ExcelUtils.java
@@ -1,10 +1,10 @@
package cn.iocoder.yudao.framework.excel.core.util;
+import cn.idev.excel.FastExcelFactory;
+import cn.idev.excel.converters.longconverter.LongStringConverter;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
+import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy;
import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler;
-import com.alibaba.excel.EasyExcel;
-import com.alibaba.excel.converters.longconverter.LongStringConverter;
-import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
@@ -32,9 +32,9 @@ public class ExcelUtils {
public static void write(HttpServletResponse response, String filename, String sheetName,
Class head, List data) throws IOException {
// 输出 Excel
- EasyExcel.write(response.getOutputStream(), head)
+ FastExcelFactory.write(response.getOutputStream(), head)
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
- .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
+ .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
.registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框
.registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
.sheet(sheetName).doWrite(data);
@@ -44,7 +44,7 @@ public class ExcelUtils {
}
public static List read(MultipartFile file, Class head) throws IOException {
- return EasyExcel.read(file.getInputStream(), head, null)
+ return FastExcelFactory.read(file.getInputStream(), head, null)
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
.doReadAllSync();
}
diff --git a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/package-info.java b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/package-info.java
index 53bc5c01bf..72c3ac42e1 100644
--- a/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/package-info.java
+++ b/yudao-framework/yudao-spring-boot-starter-excel/src/main/java/cn/iocoder/yudao/framework/excel/package-info.java
@@ -1,4 +1,4 @@
/**
- * 基于 EasyExcel 实现 Excel 相关的操作
+ * 基于 FastExcel 实现 Excel 相关的操作
*/
package cn.iocoder.yudao.framework.excel;
diff --git a/yudao-framework/yudao-spring-boot-starter-monitor/pom.xml b/yudao-framework/yudao-spring-boot-starter-monitor/pom.xml
index ebd1210c03..6b6970fbef 100644
--- a/yudao-framework/yudao-spring-boot-starter-monitor/pom.xml
+++ b/yudao-framework/yudao-spring-boot-starter-monitor/pom.xml
@@ -44,29 +44,35 @@
io.opentracing
opentracing-util
+ true
org.apache.skywalking
apm-toolkit-trace
+ true
org.apache.skywalking
apm-toolkit-logback-1.x
+ true
org.apache.skywalking
apm-toolkit-opentracing
+ true
io.micrometer
micrometer-registry-prometheus
+ true
de.codecentric
- spring-boot-admin-starter-client
+ spring-boot-admin-starter-client
+ true
diff --git a/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java
index c7d9e2c0db..7876742620 100644
--- a/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-monitor/src/main/java/cn/iocoder/yudao/framework/tracer/config/YudaoTracerAutoConfiguration.java
@@ -3,6 +3,9 @@ package cn.iocoder.yudao.framework.tracer.config;
import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum;
import cn.iocoder.yudao.framework.tracer.core.aop.BizTraceAspect;
import cn.iocoder.yudao.framework.tracer.core.filter.TraceFilter;
+import io.opentracing.Tracer;
+import io.opentracing.util.GlobalTracer;
+import org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -16,30 +19,32 @@ import org.springframework.context.annotation.Bean;
* @author mashu
*/
@AutoConfiguration
-@ConditionalOnClass({BizTraceAspect.class})
+@ConditionalOnClass(name = {
+ "org.apache.skywalking.apm.toolkit.opentracing.SkywalkingTracer",
+ "io.opentracing.Tracer"
+})
@EnableConfigurationProperties(TracerProperties.class)
@ConditionalOnProperty(prefix = "yudao.tracer", value = "enable", matchIfMissing = true)
public class YudaoTracerAutoConfiguration {
- // TODO @芋艿:重要。目前 opentracing 版本存在冲突,要么保证 skywalking,要么保证阿里云短信 sdk
-// @Bean
-// public TracerProperties bizTracerProperties() {
-// return new TracerProperties();
-// }
-//
-// @Bean
-// public BizTraceAspect bizTracingAop() {
-// return new BizTraceAspect(tracer());
-// }
-//
-// @Bean
-// public Tracer tracer() {
-// // 创建 SkywalkingTracer 对象
-// SkywalkingTracer tracer = new SkywalkingTracer();
-// // 设置为 GlobalTracer 的追踪器
-// GlobalTracer.register(tracer);
-// return tracer;
-// }
+ @Bean
+ public TracerProperties bizTracerProperties() {
+ return new TracerProperties();
+ }
+
+ @Bean
+ public BizTraceAspect bizTracingAop() {
+ return new BizTraceAspect(tracer());
+ }
+
+ @Bean
+ public Tracer tracer() {
+ // 创建 SkywalkingTracer 对象
+ SkywalkingTracer tracer = new SkywalkingTracer();
+ // 设置为 GlobalTracer 的追踪器
+ GlobalTracer.registerIfAbsent(tracer);
+ return tracer;
+ }
/**
* 创建 TraceFilter 过滤器,响应 header 设置 traceId
diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java
index 48e901d624..8b5a0fcfc8 100644
--- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java
+++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java
@@ -118,7 +118,6 @@ public class MPJLambdaWrapperX extends MPJLambdaWrapper {
@Override
public MPJLambdaWrapperX orderByDesc(SFunction column) {
- //noinspection unchecked
super.orderByDesc(true, column);
return this;
}
@@ -208,7 +207,7 @@ public class MPJLambdaWrapperX extends MPJLambdaWrapper {
}
@Override
- public MPJLambdaWrapperX selectCount(SFunction column, String alias) {
+ public MPJLambdaWrapperX selectCount(SFunction column, String alias) {
super.selectCount(column, alias);
return this;
}
@@ -226,7 +225,7 @@ public class MPJLambdaWrapperX extends MPJLambdaWrapper {
}
@Override
- public MPJLambdaWrapperX selectSum(SFunction column, String alias) {
+ public MPJLambdaWrapperX selectSum(SFunction column, String alias) {
super.selectSum(column, alias);
return this;
}
@@ -244,7 +243,7 @@ public class MPJLambdaWrapperX extends MPJLambdaWrapper {
}
@Override
- public MPJLambdaWrapperX selectMax(SFunction column, String alias) {
+ public MPJLambdaWrapperX selectMax(SFunction column, String alias) {
super.selectMax(column, alias);
return this;
}
@@ -262,7 +261,7 @@ public class MPJLambdaWrapperX extends MPJLambdaWrapper {
}
@Override
- public MPJLambdaWrapperX selectMin(SFunction column, String alias) {
+ public MPJLambdaWrapperX selectMin(SFunction column, String alias) {
super.selectMin(column, alias);
return this;
}
@@ -280,7 +279,7 @@ public class MPJLambdaWrapperX extends MPJLambdaWrapper {
}
@Override
- public MPJLambdaWrapperX selectAvg(SFunction column, String alias) {
+ public MPJLambdaWrapperX selectAvg(SFunction column, String alias) {
super.selectAvg(column, alias);
return this;
}
@@ -298,7 +297,7 @@ public class MPJLambdaWrapperX extends MPJLambdaWrapper {
}
@Override
- public MPJLambdaWrapperX selectLen(SFunction column, String alias) {
+ public MPJLambdaWrapperX selectLen(SFunction column, String alias) {
super.selectLen(column, alias);
return this;
}
diff --git a/yudao-framework/yudao-spring-boot-starter-web/pom.xml b/yudao-framework/yudao-spring-boot-starter-web/pom.xml
index d04db57c47..92ebc918f0 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/pom.xml
+++ b/yudao-framework/yudao-spring-boot-starter-web/pom.xml
@@ -53,7 +53,13 @@
provided
-
+
+
+ com.google.guava
+ guava
+ provided
+
+
org.jsoup
jsoup
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/CacheRequestBodyFilter.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/CacheRequestBodyFilter.java
index 9071998f91..49958fc096 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/CacheRequestBodyFilter.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/filter/CacheRequestBodyFilter.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.framework.web.core.filter;
+import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import org.springframework.web.filter.OncePerRequestFilter;
@@ -16,6 +17,14 @@ import java.io.IOException;
*/
public class CacheRequestBodyFilter extends OncePerRequestFilter {
+ /**
+ * 需要排除的 URI
+ *
+ * 1. 排除 Spring Boot Admin 相关请求,避免客户端连接中断导致的异常。
+ * 例如说:795 ISSUE
+ */
+ private static final String[] IGNORE_URIS = {"/admin/", "/actuator/"};
+
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
@@ -24,7 +33,13 @@ public class CacheRequestBodyFilter extends OncePerRequestFilter {
@Override
protected boolean shouldNotFilter(HttpServletRequest request) {
- // 只处理 json 请求内容
+ // 1. 校验是否为排除的 URL
+ String requestURI = request.getRequestURI();
+ if (StrUtil.startWithAny(requestURI, IGNORE_URIS)) {
+ return true;
+ }
+
+ // 2. 只处理 json 请求内容
return !ServletUtils.isJsonRequest(request);
}
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
index 627a5ea784..22234ba3bf 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
@@ -5,7 +5,6 @@ import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
-import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.iocoder.yudao.framework.common.biz.infra.logger.ApiErrorLogCommonApi;
import cn.iocoder.yudao.framework.common.biz.infra.logger.dto.ApiErrorLogCreateReqDTO;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
@@ -17,6 +16,7 @@ import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
+import com.google.common.util.concurrent.UncheckedExecutionException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
@@ -111,6 +111,9 @@ public class GlobalExceptionHandler {
if (ex instanceof AccessDeniedException) {
return accessDeniedExceptionHandler(request, (AccessDeniedException) ex);
}
+ if (ex instanceof UncheckedExecutionException && ex.getCause() != ex) {
+ return allExceptionHandler(request, ex.getCause());
+ }
return defaultExceptionHandler(request, ex);
}
@@ -266,6 +269,16 @@ public class GlobalExceptionHandler {
return CommonResult.error(FORBIDDEN);
}
+ /**
+ * 处理 Guava UncheckedExecutionException
+ *
+ * 例如说,缓存加载报错,可见 https://t.zsxq.com/UszdH
+ */
+ @ExceptionHandler(value = UncheckedExecutionException.class)
+ public CommonResult> uncheckedExecutionExceptionHandler(HttpServletRequest req, UncheckedExecutionException ex) {
+ return allExceptionHandler(req, ex.getCause());
+ }
+
/**
* 处理业务异常 ServiceException
*
@@ -344,12 +357,12 @@ public class GlobalExceptionHandler {
errorLog.setApplicationName(applicationName);
errorLog.setRequestUrl(request.getRequestURI());
Map requestParams = MapUtil.builder()
- .put("query", JakartaServletUtil.getParamMap(request))
- .put("body", JakartaServletUtil.getBody(request)).build();
+ .put("query", ServletUtils.getParamMap(request))
+ .put("body", ServletUtils.getBody(request)).build();
errorLog.setRequestParams(JsonUtils.toJsonString(requestParams));
errorLog.setRequestMethod(request.getMethod());
errorLog.setUserAgent(ServletUtils.getUserAgent(request));
- errorLog.setUserIp(JakartaServletUtil.getClientIP(request));
+ errorLog.setUserIp(ServletUtils.getClientIP(request));
errorLog.setExceptionTime(LocalDateTime.now());
}
diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java
index 99b6a448f3..16f87dae1f 100644
--- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java
+++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/xss/config/YudaoXssAutoConfiguration.java
@@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.xss.core.clean.JsoupXssCleaner;
import cn.iocoder.yudao.framework.xss.core.clean.XssCleaner;
import cn.iocoder.yudao.framework.xss.core.filter.XssFilter;
import cn.iocoder.yudao.framework.xss.core.json.XssStringJsonDeserializer;
-import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
@@ -42,13 +41,13 @@ public class YudaoXssAutoConfiguration implements WebMvcConfigurer {
*/
@Bean
@ConditionalOnMissingBean(name = "xssJacksonCustomizer")
- @ConditionalOnBean(ObjectMapper.class)
@ConditionalOnProperty(value = "yudao.xss.enable", havingValue = "true")
public Jackson2ObjectMapperBuilderCustomizer xssJacksonCustomizer(XssProperties properties,
PathMatcher pathMatcher,
XssCleaner xssCleaner) {
// 在反序列化时进行 xss 过滤,可以替换使用 XssStringJsonSerializer,在序列化时进行处理
- return builder -> builder.deserializerByType(String.class, new XssStringJsonDeserializer(properties, pathMatcher, xssCleaner));
+ return builder ->
+ builder.deserializerByType(String.class, new XssStringJsonDeserializer(properties, pathMatcher, xssCleaner));
}
/**
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
index 5142cde443..ddd426d283 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/chat/AiChatConversationController.java
@@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatCo
import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO;
import cn.iocoder.yudao.module.ai.service.chat.AiChatConversationService;
import cn.iocoder.yudao.module.ai.service.chat.AiChatMessageService;
+import com.fhs.core.trans.anno.TransMethodResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -54,6 +55,7 @@ public class AiChatConversationController {
@GetMapping("/my-list")
@Operation(summary = "获得【我的】聊天对话列表")
+ @TransMethodResult
public CommonResult> getChatConversationMyList() {
List list = chatConversationService.getChatConversationListByUserId(getLoginUserId());
return success(BeanUtils.toBean(list, AiChatConversationRespVO.class));
@@ -62,6 +64,7 @@ public class AiChatConversationController {
@GetMapping("/get-my")
@Operation(summary = "获得【我的】聊天对话")
@Parameter(name = "id", required = true, description = "对话编号", example = "1024")
+ @TransMethodResult
public CommonResult getChatConversationMy(@RequestParam("id") Long id) {
AiChatConversationDO conversation = chatConversationService.getChatConversation(id);
if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
@@ -90,6 +93,7 @@ public class AiChatConversationController {
@GetMapping("/page")
@Operation(summary = "获得对话分页", description = "用于【对话管理】菜单")
@PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')")
+ @TransMethodResult
public CommonResult> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
PageResult pageResult = chatConversationService.getChatConversationPage(pageReqVO);
if (CollUtil.isEmpty(pageResult.getList())) {
diff --git a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java
index 5714c5fedd..804e211527 100644
--- a/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java
+++ b/yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java
@@ -10,6 +10,7 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleS
import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatRole.AiChatRoleSaveReqVO;
import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO;
import cn.iocoder.yudao.module.ai.service.model.AiChatRoleService;
+import com.fhs.core.trans.anno.TransMethodResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -35,6 +36,7 @@ public class AiChatRoleController {
@GetMapping("/my-page")
@Operation(summary = "获得【我的】聊天角色分页")
+ @TransMethodResult
public CommonResult> getChatRoleMyPage(@Valid AiChatRolePageReqVO pageReqVO) {
PageResult pageResult = chatRoleService.getChatRoleMyPage(pageReqVO, getLoginUserId());
return success(BeanUtils.toBean(pageResult, AiChatRoleRespVO.class));
@@ -43,6 +45,7 @@ public class AiChatRoleController {
@GetMapping("/get-my")
@Operation(summary = "获得【我的】聊天角色")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
+ @TransMethodResult
public CommonResult getChatRoleMy(@RequestParam("id") Long id) {
AiChatRoleDO chatRole = chatRoleService.getChatRole(id);
if (ObjUtil.notEqual(chatRole.getUserId(), getLoginUserId())) {
@@ -108,6 +111,7 @@ public class AiChatRoleController {
@Operation(summary = "获得聊天角色")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('ai:chat-role:query')")
+ @TransMethodResult
public CommonResult getChatRole(@RequestParam("id") Long id) {
AiChatRoleDO chatRole = chatRoleService.getChatRole(id);
return success(BeanUtils.toBean(chatRole, AiChatRoleRespVO.class));
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java
index d877f60a88..7ce45b3350 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java
@@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.expression;
-import com.alibaba.excel.annotation.ExcelProperty;
+import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -27,4 +27,4 @@ public class BpmProcessExpressionRespVO {
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
-}
\ No newline at end of file
+}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
index dabedb5463..aeac0876f1 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/ErrorCodeConstants.java
@@ -40,7 +40,7 @@ public interface ErrorCodeConstants {
ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在");
ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程");
ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消");
- ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败");
+ ErrorCode PROCESS_INSTANCE_HTTP_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 请求调用失败");
ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置");
ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消");
@@ -58,7 +58,6 @@ public interface ErrorCodeConstants {
ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务");
ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人");
ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在");
- ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!");
ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!");
ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!");
ErrorCode TASK_WITHDRAW_FAIL_PROCESS_NOT_RUNNING = new ErrorCode(1_009_005_017, "撤回失败,流程实例未运行!");
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
index 57f4d393f3..87e6a605ff 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java
@@ -7,44 +7,35 @@ import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.BpmTaskCand
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils;
import lombok.Setter;
-import org.flowable.bpmn.model.Activity;
-import org.flowable.bpmn.model.CallActivity;
-import org.flowable.bpmn.model.FlowElement;
-import org.flowable.bpmn.model.UserTask;
+import org.flowable.bpmn.model.*;
import org.flowable.engine.delegate.DelegateExecution;
import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior;
-import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior;
+import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior;
+import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import java.util.List;
import java.util.Set;
/**
- * 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配
- * 第一步,基于分配规则,计算出分配任务的【多个】候选人们。
- * 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它
+ * 自定义的【串行】的【多个】流程任务的 assignee 负责人的分配
*
- * @author kemengkai
- * @since 2022-04-21 16:57
+ * 本质上,实现和 {@link BpmParallelMultiInstanceBehavior} 一样,只是继承的类不一样
+ *
+ * @author 芋道源码
*/
@Setter
-public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior {
+public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior {
private BpmTaskCandidateInvoker taskCandidateInvoker;
- public BpmParallelMultiInstanceBehavior(Activity activity,
- AbstractBpmnActivityBehavior innerActivityBehavior) {
+ public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) {
super(activity, innerActivityBehavior);
}
/**
- * 重写该方法,主要实现两个功能:
- * 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的
- * 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人
+ * 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似
*
- * 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量
- *
- * @param execution 执行任务
- * @return 数量
+ * 差异的点:是在【第二步】的时候,需要返回 LinkedHashSet 集合!因为它需要有序!
*/
@Override
protected int resolveNrOfInstances(DelegateExecution execution) {
@@ -58,8 +49,9 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
// 第二步,获取任务的所有处理人
+ // 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人
@SuppressWarnings("unchecked")
- Set assigneeUserIds = (Set) execution.getVariable(super.collectionVariable, Set.class);
+ Set assigneeUserIds = (Set) execution.getVariableLocal(super.collectionVariable, Set.class);
if (assigneeUserIds == null) {
assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution);
if (CollUtil.isEmpty(assigneeUserIds)) {
@@ -88,4 +80,19 @@ public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehav
return super.resolveNrOfInstances(execution);
}
+ @Override
+ protected void executeOriginalBehavior(DelegateExecution execution, ExecutionEntity multiInstanceRootExecution, int loopCounter) {
+ // 参见 https://t.zsxq.com/53Meo 情况
+ if (execution.getCurrentFlowElement() instanceof CallActivity
+ || execution.getCurrentFlowElement() instanceof SubProcess) {
+ super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter);
+ return;
+ }
+ // 参见 https://gitee.com/zhijiantianya/yudao-cloud/issues/IC239F
+ super.collectionExpression = null;
+ super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId());
+ super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId());
+ super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter);
+ }
+
}
diff --git a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java
index 014b5e3f0d..358ee9f4dc 100644
--- a/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java
+++ b/yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java
@@ -6,15 +6,15 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
+import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
+import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum;
import cn.iocoder.yudao.module.bpm.service.task.BpmProcessInstanceService;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.runtime.ProcessInstance;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.ResponseEntity;
+import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestClientException;
@@ -26,7 +26,7 @@ import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID;
-import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR;
+import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_CALL_ERROR;
/**
* 工作流发起 HTTP 请求工具类
@@ -42,7 +42,6 @@ public class BpmHttpRequestUtils {
List bodyParams,
Boolean handleResponse,
List> response) {
- RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);
BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class);
// 1.1 设置请求头
@@ -51,6 +50,7 @@ public class BpmHttpRequestUtils {
MultiValueMap body = buildHttpBody(processInstance, bodyParams);
// 2. 发起请求
+ RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);
ResponseEntity responseEntity = sendHttpRequest(url, headers, body, restTemplate);
// 3. 处理返回
@@ -78,27 +78,55 @@ public class BpmHttpRequestUtils {
}
}
+ public static void executeBpmHttpRequest(BpmProcessInstanceStatusEvent event,
+ String url) {
+ // 1.1 设置请求头
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ if (TenantContextHolder.getTenantId() != null) {
+ headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId()));
+ } else {
+ BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class);
+ ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getId());
+ if (processInstance != null) {
+ headers.add(HEADER_TENANT_ID, String.valueOf(TenantContextHolder.getTenantId()));
+ }
+ }
+ // 1.2 设置请求体
+// MultiValueMap body = new LinkedMultiValueMap<>();
+// body.add("id", event.getId());
+// body.add("processDefinitionKey", event.getProcessDefinitionKey());
+// body.add("status", event.getStatus().toString());
+// if (StrUtil.isNotEmpty(event.getBusinessKey())) {
+// body.add("businessKey", event.getBusinessKey());
+// }
+
+ // 2. 发起请求
+ RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class);
+ sendHttpRequest(url, headers, event, restTemplate);
+ }
+
public static ResponseEntity sendHttpRequest(String url,
MultiValueMap headers,
- MultiValueMap body,
+ Object body,
RestTemplate restTemplate) {
- HttpEntity> requestEntity = new HttpEntity<>(body, headers);
+ HttpEntity