Compare commits

...

17 Commits

Author SHA1 Message Date
疯狂的狮子Li
b337e0ef98 update 优化 翻译模块 增加逻辑注释和相关警告日志 2026-04-03 16:44:17 +08:00
疯狂的狮子Li
b2e07254f9 Revert "update 优化操作日志"
This reverts commit bcc11dcc12.
2026-04-03 07:07:03 +00:00
AprilWind
bcc11dcc12 update 优化操作日志 2026-04-03 14:49:46 +08:00
疯狂的狮子Li
ef3fa714bb update 优化 LocalDateTime 任意时间格式传参序列化 2026-04-03 13:44:29 +08:00
疯狂的狮子Li
d9115291bc update 优化 调整代码生成器时间类型 2026-04-03 12:22:01 +08:00
疯狂的狮子Li
0403aac1a4 update 优化 调整代码生成器时间类型 2026-04-03 12:11:02 +08:00
疯狂的狮子Li
22ed3454ba fix 修复 下拉数据字符大于255报错问题 2026-04-03 11:28:39 +08:00
疯狂的狮子Li
b9489d7406 [重大更新] 应广大用户要求 将Date换成LocalDateTime 2026-04-03 10:59:04 +08:00
疯狂的狮子Li
e20e31b5f8 update 优化 工作流类别翻译器支持批量翻译 2026-04-02 11:35:43 +08:00
疯狂的狮子Li
f8ebeaa01a update 优化 工作流类别翻译器支持批量翻译 2026-04-02 11:26:36 +08:00
疯狂的狮子Li
00a5d5c59f update 优化 已办任务列表去除抄送任务 2026-04-02 11:16:17 +08:00
疯狂的狮子Li
bb166c0742 update 优化 删除一些无用方法 2026-04-02 11:01:27 +08:00
疯狂的狮子Li
568547ada5 update 优化一些性能问题 2026-04-01 10:13:42 +08:00
疯狂的狮子Li
d11990bfd8 update 优化 项目中的一些存在null的问题 与一些性能问题 小优化 2026-03-31 18:54:43 +08:00
疯狂的狮子Li
26464c0051 update 优化 翻译处理器 避免多次序列化 实现类增加缓存避免重复解析 2026-03-31 18:00:01 +08:00
疯狂的狮子Li
eb850fb8cf update 优化 代码生成器方法 2026-03-31 17:58:18 +08:00
疯狂的狮子Li
37e1ed0bf3 fix 修复 DeptExcelConverter 存在的内存泄漏问题 与优化部分代码 2026-03-31 17:54:41 +08:00
86 changed files with 607 additions and 495 deletions

View File

@@ -32,6 +32,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.function.Supplier;
@@ -178,7 +179,7 @@ public class SysLoginService {
SysUser sysUser = new SysUser();
sysUser.setUserId(userId);
sysUser.setLoginIp(ip);
sysUser.setLoginDate(DateUtils.getNowDate());
sysUser.setLoginDate(LocalDateTime.now());
sysUser.setUpdateBy(userId);
DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser));
}

View File

@@ -76,8 +76,6 @@ spring:
format:
date-time: yyyy-MM-dd HH:mm:ss
jackson:
# 日期格式化
date-format: yyyy-MM-dd HH:mm:ss
serialization:
# 格式化输出
indent_output: false

View File

@@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -57,7 +57,7 @@ public class TaskAssigneeDTO implements Serializable {
Function<T, String> handlerCode,
Function<T, String> handlerName,
Function<T, String> groupName,
Function<T, Date> createTimeMapper) {
Function<T, LocalDateTime> createTimeMapper) {
return sourceCollection.stream()
.map(item -> new TaskHandler(
storageId.apply(item),
@@ -99,7 +99,7 @@ public class TaskAssigneeDTO implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
}
}

View File

@@ -5,7 +5,7 @@ import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -68,6 +68,6 @@ public class UserDTO implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
}

View File

@@ -10,6 +10,7 @@ import java.lang.management.ManagementFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.concurrent.TimeUnit;
@@ -122,6 +123,16 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, date);
}
/**
* 将 LocalDateTime 格式化为 YYYY-MM-DD HH:MM:SS 格式的字符串
*
* @param dateTime 要格式化的 LocalDateTime 对象
* @return 格式化后的日期时间字符串
*/
public static String formatDateTime(final LocalDateTime dateTime) {
return dateTime.format(DateTimeFormatter.ofPattern(FormatsType.YYYY_MM_DD_HH_MM_SS.getTimeFormat()));
}
/**
* 将指定日期按照指定格式进行格式化
*
@@ -242,7 +253,36 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
if (sec > 0) {
result.append(String.format("%d秒", sec));
}
return result.length() > 0 ? result.toString().trim() : "0秒";
return !result.isEmpty() ? result.toString().trim() : "0秒";
}
/**
* 计算两个 LocalDateTime 时间点的差值天、小时、分钟、秒当值为0时不显示该单位
*
* @param endDate 结束时间
* @param nowDate 当前时间
* @return 时间差字符串,格式为 "x天 x小时 x分钟 x秒",若为 0 则不显示
*/
public static String getTimeDifference(LocalDateTime endDate, LocalDateTime nowDate) {
long diffInMillis = java.time.Duration.between(nowDate, endDate).toMillis();
long day = TimeUnit.MILLISECONDS.toDays(diffInMillis);
long hour = TimeUnit.MILLISECONDS.toHours(diffInMillis) % 24;
long min = TimeUnit.MILLISECONDS.toMinutes(diffInMillis) % 60;
long sec = TimeUnit.MILLISECONDS.toSeconds(diffInMillis) % 60;
StringBuilder result = new StringBuilder();
if (day > 0) {
result.append(String.format("%d天 ", day));
}
if (hour > 0) {
result.append(String.format("%d小时 ", hour));
}
if (min > 0) {
result.append(String.format("%d分钟 ", min));
}
if (sec > 0) {
result.append(String.format("%d秒", sec));
}
return !result.isEmpty() ? result.toString().trim() : "0秒";
}
/**

View File

@@ -1,6 +1,7 @@
package org.dromara.common.core.utils;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.extra.servlet.JakartaServletUtil;
import cn.hutool.http.HttpStatus;
import jakarta.servlet.ServletRequest;
@@ -266,6 +267,21 @@ public class ServletUtils extends JakartaServletUtil {
return getClientIP(getRequest());
}
/**
* 获取客户端 IP 地址
*
* @return 客户端 IP 地址
*/
public static String getClientIP(HttpServletRequest request, String... otherHeaderNames) {
String[] headers = {"X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP", "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"};
if (ArrayUtil.isNotEmpty(otherHeaderNames)) {
headers = ArrayUtil.addAll(otherHeaderNames, headers);
}
String ip = getClientIPByHeader(request, headers);
return StringUtils.strip(ip, "[]");
}
/**
* 对内容进行 URL 编码
*

View File

@@ -23,6 +23,8 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
public static final String SLASH = "/";
private static final AntPathMatcher ANT_PATH_MATCHER = new AntPathMatcher();
@Deprecated
private StringUtils() {
}
@@ -233,8 +235,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
* @param url 需要匹配的url
*/
public static boolean isMatch(String pattern, String url) {
AntPathMatcher matcher = new AntPathMatcher();
return matcher.match(pattern, url);
return ANT_PATH_MATCHER.match(pattern, url);
}
/**
@@ -547,4 +548,11 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
return Strings.CS.containsAny(cs, searchCharSequences);
}
/**
* 将一个字符串替换为较大字符串内的另一个字符串,一次
*/
public static String replaceOnce(final String text, final String searchString, final String replacement) {
return Strings.CS.replaceOnce(text, searchString, replacement);
}
}

View File

@@ -1,5 +1,6 @@
package org.dromara.common.core.utils.sql;
import cn.hutool.core.exceptions.UtilException;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.utils.StringUtils;
@@ -15,7 +16,7 @@ public class SqlUtil {
/**
* 定义常用的 sql关键字
*/
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
public static final String SQL_REGEX = "\u000B|%0A|and |extractvalue|updatexml|sleep|information_schema|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
/**
* 仅支持字母、数字、下划线、空格、逗号、小数点(支持多个字段排序)
@@ -46,10 +47,11 @@ public class SqlUtil {
if (StringUtils.isEmpty(value)) {
return;
}
String normalizedValue = value.replaceAll("\\p{Z}|\\s", "");
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords) {
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
throw new IllegalArgumentException("参数存在SQL注入风险");
if (StringUtils.indexOf(normalizedValue, sqlKeyword) > -1) {
throw new UtilException("请求参数包含敏感关键词'" + sqlKeyword + "',可能存在安全风险");
}
}
}

View File

@@ -38,6 +38,9 @@ public class ExcelBigNumberConvert implements Converter<Long> {
@Override
public WriteCellData<Object> convertToExcelData(Long object, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
if (ObjectUtil.isNull(object)) {
return new WriteCellData<>("");
}
if (ObjectUtil.isNotNull(object)) {
String str = Convert.toStr(object);
if (str.length() > 15) {

View File

@@ -16,6 +16,7 @@ import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 枚举格式化转换处理
@@ -25,6 +26,9 @@ import java.util.Map;
@Slf4j
public class ExcelEnumConvert implements Converter<Object> {
private static final Map<Field, Map<Object, String>> ENUM_MAP_CACHE = new ConcurrentHashMap<>();
private static final Map<Field, Map<Object, Object>> ENUM_REVERSE_MAP_CACHE = new ConcurrentHashMap<>();
@Override
public Class<Object> supportJavaTypeKey() {
return Object.class;
@@ -50,10 +54,15 @@ public class ExcelEnumConvert implements Converter<Object> {
return null;
}
Map<Object, String> enumCodeToTextMap = beforeConvert(contentProperty);
// 从Java输出至Excel是code转text
// 因此从Excel转Java应该将text与code对调
Map<Object, Object> enumTextToCodeMap = new HashMap<>();
enumCodeToTextMap.forEach((key, value) -> enumTextToCodeMap.put(value, key));
// 从Java输出至Excel是code转text从Excel转Java应将text与code对调
Map<Object, Object> enumTextToCodeMap = ENUM_REVERSE_MAP_CACHE.computeIfAbsent(
contentProperty.getField(),
f -> {
Map<Object, Object> reverseMap = new HashMap<>();
enumCodeToTextMap.forEach((key, value) -> reverseMap.put(value, key));
return reverseMap;
}
);
// 应该从text -> code中查找
Object codeValue = enumTextToCodeMap.get(textValue);
return Convert.convert(contentProperty.getField().getType(), codeValue);
@@ -70,15 +79,17 @@ public class ExcelEnumConvert implements Converter<Object> {
}
private Map<Object, String> beforeConvert(ExcelContentProperty contentProperty) {
ExcelEnumFormat anno = getAnnotation(contentProperty.getField());
Map<Object, String> enumValueMap = new HashMap<>();
Enum<?>[] enumConstants = anno.enumClass().getEnumConstants();
for (Enum<?> enumConstant : enumConstants) {
Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField());
String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField());
enumValueMap.put(codeValue, textValue);
}
return enumValueMap;
return ENUM_MAP_CACHE.computeIfAbsent(contentProperty.getField(), field -> {
ExcelEnumFormat anno = getAnnotation(field);
Map<Object, String> enumValueMap = new HashMap<>();
Enum<?>[] enumConstants = anno.enumClass().getEnumConstants();
for (Enum<?> enumConstant : enumConstants) {
Object codeValue = ReflectUtils.invokeGetter(enumConstant, anno.codeField());
String textValue = ReflectUtils.invokeGetter(enumConstant, anno.textField());
enumValueMap.put(codeValue, textValue);
}
return enumValueMap;
});
}
private ExcelEnumFormat getAnnotation(Field field) {

View File

@@ -62,7 +62,7 @@ public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements
errMsg = StrUtil.format("第{}行-第{}列-表头{}: 解析异常<br/>",
rowIndex + 1, columnIndex + 1, headMap.get(columnIndex));
if (log.isDebugEnabled()) {
log.error(errMsg);
log.warn(errMsg);
}
}
if (exception instanceof ConstraintViolationException constraintViolationException) {
@@ -70,9 +70,13 @@ public class DefaultExcelListener<T> extends AnalysisEventListener<T> implements
String constraintViolationsMsg = StreamUtils.join(constraintViolations, ConstraintViolation::getMessage, ", ");
errMsg = StrUtil.format("第{}行数据校验异常: {}", context.readRowHolder().getRowIndex() + 1, constraintViolationsMsg);
if (log.isDebugEnabled()) {
log.error(errMsg);
log.warn(errMsg);
}
}
if (errMsg == null) {
errMsg = StrUtil.format("第{}行数据异常: {}", context.readRowHolder().getRowIndex() + 1, exception.getMessage());
log.warn(errMsg, exception);
}
excelResult.getErrorList().add(errMsg);
throw new ExcelAnalysisException(errMsg);
}

View File

@@ -66,7 +66,7 @@ public class DefaultExcelResult<T> implements ExcelResult<T> {
if (errorCount == 0) {
return StrUtil.format("恭喜您,全部读取成功!共{}条", successCount);
} else {
return "";
return StrUtil.format("共{}条,成功导入{}条,错误{}条", successCount + errorCount, successCount, errorCount);
}
}
}

View File

@@ -130,8 +130,9 @@ public class ExcelDownHandler implements SheetWriteHandler {
}
if (ObjectUtil.isNotEmpty(options)) {
// 仅当下拉可选项不为空时执行
if (options.size() > 20) {
// 这里限制如果可选项大于20则使用额外表形式
int totalCharacter = options.stream().mapToInt(String::length).sum() + options.size();
if (options.size() > 20 || totalCharacter > 255) {
// 这里限制如果可选项大于20 或 总字符数超过255则使用额外表形式
dropDownWithSheet(helper, workbook, sheet, index, options);
} else {
// 否则使用固定值形式
@@ -182,9 +183,10 @@ public class ExcelDownHandler implements SheetWriteHandler {
Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName));
// 将下拉表隐藏
workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true);
// 选项数据
// 选项数据(使用副本,避免修改调用方的原始数据)
List<String> firstOptions = options.getOptions();
Map<String, List<String>> secoundOptionsMap = options.getNextOptions();
Map<String, List<String>> secoundOptionsMap = new HashMap<>();
options.getNextOptions().forEach((k, v) -> secoundOptionsMap.put(k, new ArrayList<>(v)));
// 采用按行填充数据的方式,避免出现数据无法写入的问题
// Attempting to write a row in the range that is already written to disk
@@ -378,7 +380,6 @@ public class ExcelDownHandler implements SheetWriteHandler {
//选定提示
dataValidation.createPromptBox("填写说明:", "填写内容只能为下拉中数据,其他数据将导致导入失败");
dataValidation.setShowPromptBox(true);
sheet.addValidationData(dataValidation);
} else {
dataValidation.setSuppressDropDownArrow(false);
}

View File

@@ -99,6 +99,9 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler {
}
ExcelRequired excelRequired = field.getAnnotation(ExcelRequired.class);
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
if (excelProperty == null || excelProperty.value().length == 0) {
continue;
}
requiredMap.put(excelProperty.value()[0], excelRequired.fontColor().getIndex());
}
return requiredMap;
@@ -116,6 +119,9 @@ public class DataWriteHandler implements SheetWriteHandler, CellWriteHandler {
}
ExcelNotation excelNotation = field.getAnnotation(ExcelNotation.class);
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
if (excelProperty == null || excelProperty.value().length == 0) {
continue;
}
notationMap.put(excelProperty.value()[0], excelNotation.value());
}
return notationMap;

View File

@@ -89,7 +89,7 @@ public class ExcelUtil {
ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, false, os, null);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
throw new RuntimeException("导出Excel异常", e);
}
}
@@ -108,7 +108,7 @@ public class ExcelUtil {
ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, false, os, options);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
throw new RuntimeException("导出Excel异常", e);
}
}
@@ -127,7 +127,7 @@ public class ExcelUtil {
ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, merge, os, null);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
throw new RuntimeException("导出Excel异常", e);
}
}
@@ -147,7 +147,7 @@ public class ExcelUtil {
ServletOutputStream os = response.getOutputStream();
exportExcel(list, sheetName, clazz, merge, os, options);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
throw new RuntimeException("导出Excel异常", e);
}
}
@@ -261,7 +261,7 @@ public class ExcelUtil {
ServletOutputStream os = response.getOutputStream();
exportTemplate(data, templatePath, os);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
throw new RuntimeException("导出Excel异常", e);
}
}
@@ -283,13 +283,16 @@ public class ExcelUtil {
.registerConverter(new ExcelBigNumberConvert())
.registerWriteHandler(new DataWriteHandler(data.getFirst().getClass()))
.build();
WriteSheet writeSheet = FesodSheet.writerSheet().build();
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
// 单表多数据导出 模板格式为 {.属性}
for (T d : data) {
excelWriter.fill(d, fillConfig, writeSheet);
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();
}
excelWriter.finish();
}
/**
@@ -311,7 +314,7 @@ public class ExcelUtil {
ServletOutputStream os = response.getOutputStream();
exportTemplateMultiList(data, templatePath, os);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
throw new RuntimeException("导出Excel异常", e);
}
}
@@ -334,7 +337,7 @@ public class ExcelUtil {
ServletOutputStream os = response.getOutputStream();
exportTemplateMultiSheet(data, templatePath, os);
} catch (IOException e) {
throw new RuntimeException("导出Excel异常");
throw new RuntimeException("导出Excel异常", e);
}
}
@@ -355,18 +358,21 @@ public class ExcelUtil {
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
WriteSheet writeSheet = FesodSheet.writerSheet().build();
for (Map.Entry<String, Object> 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);
try {
WriteSheet writeSheet = FesodSheet.writerSheet().build();
for (Map.Entry<String, Object> 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();
}
excelWriter.finish();
}
/**
@@ -386,20 +392,23 @@ public class ExcelUtil {
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.build();
for (int i = 0; i < data.size(); i++) {
WriteSheet writeSheet = FesodSheet.writerSheet(i).build();
for (Map.Entry<String, Object> 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);
try {
for (int i = 0; i < data.size(); i++) {
WriteSheet writeSheet = FesodSheet.writerSheet(i).build();
for (Map.Entry<String, Object> 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();
}
excelWriter.finish();
}
/**

View File

@@ -3,11 +3,11 @@ package org.dromara.common.json.config;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.json.handler.BigNumberSerializer;
import org.dromara.common.json.handler.CustomDateDeserializer;
import org.dromara.common.json.handler.CustomLocalDateTimeDeserializer;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
import org.springframework.boot.jackson.autoconfigure.JsonMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import tools.jackson.databind.ext.javatime.deser.LocalDateTimeDeserializer;
import tools.jackson.databind.ext.javatime.ser.LocalDateTimeSerializer;
import tools.jackson.databind.module.SimpleModule;
import tools.jackson.databind.ser.std.ToStringSerializer;
@@ -43,7 +43,7 @@ public class JacksonConfig {
module.addSerializer(BigDecimal.class, ToStringSerializer.instance);
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(formatter));
module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(formatter));
module.addDeserializer(LocalDateTime.class, new CustomLocalDateTimeDeserializer());
module.addDeserializer(Date.class, new CustomDateDeserializer());
return module;
}

View File

@@ -3,8 +3,8 @@ package org.dromara.common.json.enhance;
import lombok.Getter;
import tools.jackson.databind.json.JsonMapper;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 单次响应增强上下文。
@@ -14,7 +14,7 @@ public class JsonEnhancementContext {
private final JsonMapper jsonMapper;
private final Map<String, Object> attributes = new ConcurrentHashMap<>();
private final Map<String, Object> attributes = new LinkedHashMap<>();
public JsonEnhancementContext(JsonMapper jsonMapper) {
this.jsonMapper = jsonMapper;

View File

@@ -2,15 +2,42 @@ package org.dromara.common.json.enhance;
/**
* 响应字段处理器。
*
* <p>生命周期按顺序分为三个阶段,由 {@link JsonValueEnhancer} 统一驱动:
* <ol>
* <li><b>collect</b>(收集阶段):递归扫描响应对象树时,对每个字段调用一次。
* 用于采集需要处理的字段 key存入 {@link JsonEnhancementContext} 供下一阶段批量处理。</li>
* <li><b>prepare</b>预处理阶段collect 全部完成后调用一次。
* 用于执行批量 IO如批量查询数据库将结果写入 {@link JsonEnhancementContext}。
* 此阶段应消除 N+1 查询问题。</li>
* <li><b>process</b>(处理阶段):渲染响应 JSON 树时,对每个字段调用一次。
* 从 {@link JsonEnhancementContext} 取出 prepare 阶段写入的结果,返回替换后的字段值。
* 返回原 {@code value} 表示不修改;返回 {@code null} 表示将字段值置为 null。</li>
* </ol>
*
* <p>实现类通过 {@link JsonEnhancementContext#setAttribute} / {@link JsonEnhancementContext#getAttribute}
* 在三个阶段之间共享数据,建议以实现类全限定名作为 attribute key 前缀以避免冲突。
*/
public interface JsonFieldProcessor {
/**
* 收集阶段:扫描字段,将需要处理的 key 存入 context。
* 每个字段调用一次,整个对象树扫描完成后才会进入 prepare 阶段。
*/
default void collect(JsonFieldContext fieldContext, JsonEnhancementContext context) {
}
/**
* 预处理阶段:基于 collect 阶段收集到的数据执行批量处理(如批量查询),结果写入 context。
* 每次响应只调用一次。
*/
default void prepare(JsonEnhancementContext context) {
}
/**
* 处理阶段:根据 prepare 阶段写入的结果,对字段值进行替换并返回。
* 返回原 {@code value} 表示不修改该字段;返回 {@code null} 表示将字段值置为 null。
*/
default Object process(JsonFieldContext fieldContext, Object value, JsonEnhancementContext context) {
return value;
}

View File

@@ -129,7 +129,7 @@ public class JsonValueEnhancer {
return jsonMapper.valueToTree(value);
}
try {
ObjectNode objectNode = asObjectNode(jsonMapper.valueToTree(value));
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);
@@ -158,13 +158,6 @@ public class JsonValueEnhancer {
return enhanceTree(value);
}
private ObjectNode asObjectNode(JsonNode node) {
if (node instanceof ObjectNode objectNode) {
return objectNode;
}
return jsonMapper.createObjectNode();
}
private List<PropertyMetadata> getProperties(Class<?> type) {
return propertyCache.computeIfAbsent(type, this::resolveProperties);
}

View File

@@ -31,7 +31,7 @@ public class BigNumberSerializer extends NumberSerializer {
@Override
public void serialize(Number value, JsonGenerator gen, SerializationContext provider) {
// 超出范围 序列化为字符串
if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
if (value.longValue() >= MIN_SAFE_INTEGER && value.longValue() <= MAX_SAFE_INTEGER) {
super.serialize(value, gen, provider);
} else {
gen.writeString(value.toString());

View File

@@ -25,7 +25,7 @@ public class CustomDateDeserializer extends ValueDeserializer<Date> {
*/
@Override
public Date deserialize(JsonParser p, DeserializationContext ctxt) {
DateTime parse = DateUtil.parse(p.getText());
DateTime parse = DateUtil.parse(p.getString());
if (ObjectUtils.isNull(parse)) {
return null;
}

View File

@@ -0,0 +1,81 @@
package org.dromara.common.json.handler;
import tools.jackson.core.JsonParser;
import tools.jackson.databind.DeserializationContext;
import tools.jackson.databind.ValueDeserializer;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.List;
/**
* 自定义 LocalDateTime 类型反序列化处理器(支持多种格式,无第三方依赖)
*
* @author AprilWind
*/
public class CustomLocalDateTimeDeserializer extends ValueDeserializer<LocalDateTime> {
/** 支持时间的格式列表(直接解析为 LocalDateTime */
private static final List<DateTimeFormatter> DATETIME_FORMATTERS = List.of(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"),
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"),
DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"),
DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm"),
DateTimeFormatter.ofPattern("yyyyMMddHHmmss"),
DateTimeFormatter.ofPattern("yyyyMMdd HHmmss"),
DateTimeFormatter.ISO_LOCAL_DATE_TIME
);
/** 仅日期的格式列表(解析为 LocalDate再补零时转 LocalDateTime */
private static final List<DateTimeFormatter> DATE_ONLY_FORMATTERS = List.of(
DateTimeFormatter.ISO_LOCAL_DATE,
DateTimeFormatter.ofPattern("yyyy/MM/dd"),
DateTimeFormatter.BASIC_ISO_DATE
);
/**
* 反序列化逻辑:将字符串转换为 LocalDateTime 对象
*
* @param p JSON 解析器,用于获取字符串值
* @param ctxt 上下文环境(可用于获取更多配置)
* @return 转换后的 LocalDateTime 对象,若为空字符串返回 null
*/
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext ctxt) {
String text = p.getString();
if (text == null || text.isBlank()) {
return null;
}
text = text.trim();
// 纯数字:按时间戳处理(毫秒)
if (text.chars().allMatch(Character::isDigit)) {
return Instant.ofEpochMilli(Long.parseLong(text))
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
}
// 尝试带时间的格式
for (DateTimeFormatter formatter : DATETIME_FORMATTERS) {
try {
return LocalDateTime.parse(text, formatter);
} catch (DateTimeParseException ignored) {
}
}
// 尝试仅日期的格式,补零时
for (DateTimeFormatter formatter : DATE_ONLY_FORMATTERS) {
try {
return LocalDate.parse(text, formatter).atStartOfDay();
} catch (DateTimeParseException ignored) {
}
}
return null;
}
}

View File

@@ -4,7 +4,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 操作日志事件
@@ -101,7 +101,7 @@ public class OperLogEvent implements Serializable {
/**
* 操作时间
*/
private Date operTime;
private LocalDateTime operTime;
/**
* 消耗时间

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* Entity基类
@@ -35,7 +35,7 @@ public class BaseEntity implements Serializable {
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private Date createTime;
private LocalDateTime createTime;
/**
* 更新者
@@ -47,6 +47,6 @@ public class BaseEntity implements Serializable {
* 更新时间
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
private LocalDateTime updateTime;
}

View File

@@ -33,7 +33,7 @@ public enum DataScopeType {
/**
* 自定数据权限
*/
CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #user.roleId )} ) ", " 1 = 0 "),
CUSTOM("2", " #{#deptName} IN ( #{@sdss.getRoleCustom( #roleId )} ) ", " 1 = 0 "),
/**
* 部门数据权限

View File

@@ -11,7 +11,7 @@ import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.satoken.utils.LoginHelper;
import java.util.Date;
import java.time.LocalDateTime;
/**
* MP注入处理器
@@ -37,7 +37,7 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler {
try {
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
// 获取当前时间作为创建时间和更新时间,如果创建时间不为空,则使用创建时间,否则使用当前时间
Date current = ObjectUtils.notNull(baseEntity.getCreateTime(), new Date());
LocalDateTime current = ObjectUtils.notNull(baseEntity.getCreateTime(), LocalDateTime.now());
baseEntity.setCreateTime(current);
baseEntity.setUpdateTime(current);
@@ -58,12 +58,12 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler {
}
}
} else {
Date date = new Date();
this.strictInsertFill(metaObject, "createTime", Date.class, date);
this.strictInsertFill(metaObject, "updateTime", Date.class, date);
LocalDateTime date = LocalDateTime.now();
this.strictInsertFill(metaObject, "createTime", LocalDateTime.class, date);
this.strictInsertFill(metaObject, "updateTime", LocalDateTime.class, date);
}
} catch (Exception e) {
throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_INTERNAL_ERROR);
}
}
@@ -77,21 +77,18 @@ public class InjectionMetaObjectHandler implements MetaObjectHandler {
try {
if (ObjectUtil.isNotNull(metaObject) && metaObject.getOriginalObject() instanceof BaseEntity baseEntity) {
// 获取当前时间作为更新时间,无论原始对象中的更新时间是否为空都填充
Date current = new Date();
LocalDateTime current = LocalDateTime.now();
baseEntity.setUpdateTime(current);
// 获取当前登录用户的ID并填充更新人信息
Long userId = LoginHelper.getUserId();
if (ObjectUtil.isNotNull(userId)) {
baseEntity.setUpdateBy(userId);
} else {
baseEntity.setUpdateBy(DEFAULT_USER_ID);
}
LoginUser loginUser = getLoginUser();
Long userId = ObjectUtil.isNotNull(loginUser) ? loginUser.getUserId() : DEFAULT_USER_ID;
baseEntity.setUpdateBy(userId);
} else {
this.strictUpdateFill(metaObject, "updateTime", Date.class, new Date());
this.strictUpdateFill(metaObject, "updateTime", LocalDateTime.class, LocalDateTime.now());
}
} catch (Exception e) {
throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED);
throw new ServiceException("自动注入异常 => " + e.getMessage(), HttpStatus.HTTP_INTERNAL_ERROR);
}
}

View File

@@ -133,7 +133,7 @@ public class PlusDataPermissionHandler {
}
for (RoleDTO role : scopeRoles) {
user.setRoleId(role.getRoleId());
context.setVariable("roleId", role.getRoleId());
// 获取角色权限泛型
DataScopeType type = DataScopeType.findCode(role.getDataScope());
if (ObjectUtil.isNull(type)) {

View File

@@ -2,6 +2,7 @@ package org.dromara.common.mybatis.helper;
import cn.hutool.core.convert.Convert;
import com.baomidou.dynamic.datasource.DynamicRoutingDataSource;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.common.core.exception.ServiceException;
@@ -14,6 +15,8 @@ import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 数据库助手
@@ -24,6 +27,7 @@ import java.util.List;
public class DataBaseHelper {
private static final DynamicRoutingDataSource DS = SpringUtils.getBean(DynamicRoutingDataSource.class);
private static final Map<String, DataBaseType> DB_TYPE_CACHE = new ConcurrentHashMap<>();
/**
* 获取当前数据源对应的数据库类型
@@ -37,13 +41,17 @@ public class DataBaseHelper {
*/
public static DataBaseType getDataBaseType() {
DataSource dataSource = DS.determineDataSource();
try (Connection conn = dataSource.getConnection()) {
DatabaseMetaData metaData = conn.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
return DataBaseType.find(databaseProductName);
} catch (SQLException e) {
throw new RuntimeException("获取数据库类型失败", e);
}
String dsKey = DynamicDataSourceContextHolder.peek();
final String key = dsKey != null ? dsKey : "primary";
return DB_TYPE_CACHE.computeIfAbsent(key, k -> {
try (Connection conn = dataSource.getConnection()) {
DatabaseMetaData metaData = conn.getMetaData();
String databaseProductName = metaData.getDatabaseProductName();
return DataBaseType.find(databaseProductName);
} catch (SQLException e) {
throw new RuntimeException("获取数据库类型失败", e);
}
});
}
/**

View File

@@ -12,9 +12,10 @@ import org.dromara.common.mybatis.core.domain.DataPermissionAccess;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.mybatis.annotation.DataPermission;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.function.Supplier;
/**
@@ -30,7 +31,7 @@ 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<Stack<Integer>> REENTRANT_IGNORE = ThreadLocal.withInitial(Stack::new);
private static final ThreadLocal<Deque<Integer>> REENTRANT_IGNORE = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<DataPermission> PERMISSION_CACHE = new ThreadLocal<>();
@@ -116,7 +117,7 @@ public class DataPermissionHelper {
if (attribute instanceof Map map) {
return map;
}
throw new NullPointerException("data permission context type exception");
throw new IllegalStateException("data permission context type exception");
}
/**
@@ -144,7 +145,7 @@ public class DataPermissionHelper {
} else {
ignoreStrategy.setDataPermission(true);
}
Stack<Integer> reentrantStack = REENTRANT_IGNORE.get();
Deque<Integer> reentrantStack = REENTRANT_IGNORE.get();
reentrantStack.push(reentrantStack.size() + 1);
}
@@ -159,7 +160,7 @@ public class DataPermissionHelper {
&& !Boolean.TRUE.equals(ignoreStrategy.getIllegalSql())
&& !Boolean.TRUE.equals(ignoreStrategy.getTenantLine())
&& CollectionUtil.isEmpty(ignoreStrategy.getOthers());
Stack<Integer> reentrantStack = REENTRANT_IGNORE.get();
Deque<Integer> reentrantStack = REENTRANT_IGNORE.get();
boolean empty = reentrantStack.isEmpty() || reentrantStack.pop() == 1;
if (noOtherIgnoreStrategy && empty) {
InterceptorIgnoreHelper.clearIgnoreStrategy();

View File

@@ -2,13 +2,12 @@ package org.dromara.common.sensitive.handler;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.json.enhance.JsonEnhancementContext;
import org.dromara.common.json.enhance.JsonFieldContext;
import org.dromara.common.json.enhance.JsonFieldProcessor;
import org.dromara.common.sensitive.annotation.Sensitive;
import org.dromara.common.sensitive.core.SensitiveService;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
/**
@@ -18,22 +17,19 @@ import org.springframework.core.annotation.Order;
@Order(100)
public class SensitiveJsonFieldProcessor implements JsonFieldProcessor {
@Autowired(required = false)
private SensitiveService sensitiveService;
@Override
public Object process(JsonFieldContext fieldContext, Object value, JsonEnhancementContext context) {
Sensitive sensitive = fieldContext.getAnnotation(Sensitive.class);
if (sensitive == null || !(value instanceof String text)) {
return value;
}
try {
SensitiveService sensitiveService = SpringUtils.getBean(SensitiveService.class);
if (ObjectUtil.isNotNull(sensitiveService) && sensitiveService.isSensitive(sensitive.roleKey(), sensitive.perms())) {
return sensitive.strategy().desensitizer().apply(text);
}
return text;
} catch (BeansException e) {
log.error("脱敏实现不存在, 采用默认处理 => {}", e.getMessage());
return text;
if (ObjectUtil.isNotNull(sensitiveService) && sensitiveService.isSensitive(sensitive.roleKey(), sensitive.perms())) {
return sensitive.strategy().desensitizer().apply(text);
}
return text;
}
}

View File

@@ -4,6 +4,8 @@ import cn.hutool.core.convert.Convert;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.translation.annotation.TranslationType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Collection;
@@ -21,6 +23,8 @@ import java.util.function.Function;
*/
public interface TranslationInterface<T> {
Logger log = LoggerFactory.getLogger(TranslationInterface.class);
/**
* 按翻译键执行转换。
*
@@ -38,6 +42,9 @@ public interface TranslationInterface<T> {
* @return 翻译结果映射
*/
default Map<Object, T> translationBatch(Set<Object> keys, String other) {
TranslationType annotation = this.getClass().getAnnotation(TranslationType.class);
String type = annotation != null ? annotation.type() : this.getClass().getSimpleName();
log.warn("翻译类型 [{}] 未覆盖 translationBatch 方法,已退化为逐条查询,建议实现批量查询以提升性能", type);
Map<Object, T> result = new LinkedHashMap<>(keys.size());
for (Object key : keys) {
result.put(key, translation(key, other));

View File

@@ -1,7 +1,6 @@
package org.dromara.common.translation.core.handler;
import cn.hutool.core.util.ObjectUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.reflect.ReflectUtils;
@@ -13,11 +12,11 @@ import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.core.TranslationInterface;
import org.springframework.core.annotation.Order;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
/**
@@ -25,14 +24,24 @@ import java.util.Set;
*/
@Slf4j
@Order(0)
@RequiredArgsConstructor
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 List<TranslationInterface<?>> translations;
private final Map<String, TranslationInterface<?>> translationMap;
public TranslationJsonFieldProcessor(List<TranslationInterface<?>> translations) {
Map<String, TranslationInterface<?>> map = new LinkedHashMap<>(translations.size());
for (TranslationInterface<?> t : translations) {
TranslationType annotation = t.getClass().getAnnotation(TranslationType.class);
if (annotation != null) {
map.put(annotation.type(), t);
}
}
this.translationMap = Collections.unmodifiableMap(map);
}
@Override
public void collect(JsonFieldContext fieldContext, JsonEnhancementContext context) {
@@ -79,7 +88,7 @@ public class TranslationJsonFieldProcessor implements JsonFieldProcessor {
}
Object sourceValue = resolveSourceValue(fieldContext, translation);
if (sourceValue == null) {
return null;
return value;
}
TranslationBatchKey batchKey = new TranslationBatchKey(translation.type(), translation.other());
Map<TranslationBatchKey, Map<Object, Object>> results = context.getAttribute(ATTR_RESULTS);
@@ -118,13 +127,7 @@ public class TranslationJsonFieldProcessor implements JsonFieldProcessor {
}
private TranslationInterface<?> getTranslation(String type) {
for (TranslationInterface<?> translation : translations) {
TranslationType translationType = translation.getClass().getAnnotation(TranslationType.class);
if (translationType != null && Objects.equals(type, translationType.type())) {
return translation;
}
}
return null;
return translationMap.get(type);
}
private record TranslationBatchKey(String type, String other) {

View File

@@ -13,7 +13,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -77,7 +77,7 @@ public class TestDemoVo implements Serializable {
@ExcelRequired
@DateTimeFormat("yyyy-MM-dd HH:mm:ss")
@ExcelProperty(value = "创建时间")
private Date createTime;
private LocalDateTime createTime;
/**
* 创建人
@@ -96,7 +96,7 @@ public class TestDemoVo implements Serializable {
* 更新时间
*/
@ExcelProperty(value = "更新时间")
private Date updateTime;
private LocalDateTime updateTime;
/**
* 更新人

View File

@@ -8,7 +8,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -58,7 +58,7 @@ public class TestTreeVo implements Serializable {
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
private LocalDateTime createTime;
}

View File

@@ -171,7 +171,7 @@ public interface GenConstants {
/**
* 时间类型
*/
String TYPE_DATE = "Date";
String TYPE_DATE = "LocalDateTime";
/**
* 模糊查询

View File

@@ -1,6 +1,7 @@
package org.dromara.gen.service;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.ObjectUtil;
@@ -169,8 +170,9 @@ public class GenTableServiceImpl implements IGenTableService {
gen.setTableName(x.getName());
gen.setTableComment(x.getComment());
// postgresql的表元数据没有创建时间这个东西(好奇葩) 只能new Date代替
gen.setCreateTime(ObjectUtil.defaultIfNull(x.getCreateTime(), new Date()));
gen.setUpdateTime(x.getUpdateTime());
Date createDate = ObjectUtil.defaultIfNull(x.getCreateTime(), new Date());
gen.setCreateTime(LocalDateTimeUtil.of(createDate));
gen.setUpdateTime(x.getUpdateTime() != null ? LocalDateTimeUtil.of(x.getUpdateTime()) : null);
return gen;
}).sorted(Comparator.comparing(GenTable::getCreateTime).reversed())
.toList();
@@ -212,8 +214,8 @@ public class GenTableServiceImpl implements IGenTableService {
gen.setDataName(dataName);
gen.setTableName(x.getName());
gen.setTableComment(x.getComment());
gen.setCreateTime(x.getCreateTime());
gen.setUpdateTime(x.getUpdateTime());
gen.setCreateTime(LocalDateTimeUtil.of(x.getCreateTime()));
gen.setUpdateTime(LocalDateTimeUtil.of(x.getUpdateTime()));
return gen;
}).toList();
}
@@ -241,9 +243,7 @@ public class GenTableServiceImpl implements IGenTableService {
genTable.setOptions(options);
int row = baseMapper.updateById(genTable);
if (row > 0) {
for (GenTableColumn cenTableColumn : genTable.getColumns()) {
genTableColumnMapper.updateById(cenTableColumn);
}
genTableColumnMapper.updateBatchById(genTable.getColumns());
}
}
@@ -289,6 +289,7 @@ public class GenTableServiceImpl implements IGenTableService {
}
}
} catch (Exception e) {
log.error("导入失败", e);
throw new ServiceException("导入失败:" + e.getMessage());
}
}
@@ -332,23 +333,9 @@ public class GenTableServiceImpl implements IGenTableService {
@Override
public Map<String, String> previewCode(Long tableId) {
Map<String, String> dataMap = new LinkedHashMap<>();
// 查询表信息
GenTable table = getGenTable(tableId);
List<Long> menuIds = new ArrayList<>();
for (int i = 0; i < 6; i++) {
menuIds.add(IdGeneratorUtil.nextLongId());
}
table.setMenuIds(menuIds);
// 设置主键列信息
setPkColumn(table);
Dict context = TemplateEngineUtils.buildContext(table);
// 获取模板列表
List<PathNamedTemplate> templates = TemplateEngineUtils.getTemplateList(table.getTplCategory());
for (PathNamedTemplate template : templates) {
// 渲染模板
String render = template.render(context);
dataMap.put(template.getPathName(), render);
RenderContext rc = buildRenderContext(tableId);
for (PathNamedTemplate template : rc.templates()) {
dataMap.put(template.getPathName(), template.render(rc.context()));
}
return dataMap;
}
@@ -474,33 +461,42 @@ public class GenTableServiceImpl implements IGenTableService {
* @param zip 代码压缩输出流
*/
private void generatorCode(Long tableId, ZipOutputStream zip) {
// 查询表信息
RenderContext rc = buildRenderContext(tableId);
GenTable table = rc.table();
for (PathNamedTemplate template : rc.templates()) {
String pathName = template.getPathName();
try {
String render = template.render(rc.context());
zip.putNextEntry(new ZipEntry(TemplateEngineUtils.getFileName(pathName, table)));
IoUtil.write(zip, StandardCharsets.UTF_8, false, render);
zip.flush();
zip.closeEntry();
} catch (IOException e) {
log.error("渲染模板失败,表名:{}", table.getTableName(), e);
}
}
}
/**
* 构建代码渲染上下文含表信息、菜单ID、主键列、模板列表
*
* @param tableId 业务表主键
* @return 渲染上下文
*/
private RenderContext buildRenderContext(Long tableId) {
GenTable table = getGenTable(tableId);
List<Long> menuIds = new ArrayList<>();
for (int i = 0; i < 6; i++) {
menuIds.add(IdGeneratorUtil.nextLongId());
}
table.setMenuIds(menuIds);
// 设置主键列信息
setPkColumn(table);
Dict context = TemplateEngineUtils.buildContext(table);
// 获取模板列表
List<PathNamedTemplate> templates = TemplateEngineUtils.getTemplateList(table.getTplCategory());
for (PathNamedTemplate template : templates) {
String pathName = template.getPathName();
// 渲染模板
try {
String render = template.render(context);
// 添加到zip
zip.putNextEntry(new ZipEntry(TemplateEngineUtils.getFileName(pathName, table)));
IoUtil.write(zip, StandardCharsets.UTF_8, false, render);
zip.flush();
zip.closeEntry();
} catch (IOException e) {
log.error("渲染模板失败,表名:" + table.getTableName(), e);
}
}
return new RenderContext(table, context, templates);
}
private record RenderContext(GenTable table, Dict context, List<PathNamedTemplate> templates) {
}
/**

View File

@@ -180,7 +180,7 @@ public class GenUtils {
String text = replacementm;
for (String searchString : searchList) {
if (replacementm.startsWith(searchString)) {
text = replacementm.replaceFirst(searchString, StringUtils.EMPTY);
text = StringUtils.removeStart(replacementm, searchString);
break;
}
}
@@ -220,6 +220,10 @@ public class GenUtils {
public static Integer getColumnLength(String columnType) {
if (StringUtils.indexOf(columnType, "(") > 0) {
String length = StringUtils.substringBetween(columnType, "(", ")");
// 处理 decimal(10,2) 这类带精度的类型,只取长度部分
if (length.contains(",")) {
length = StringUtils.substringBefore(length, ",");
}
return Integer.valueOf(length);
} else {
return 0;

View File

@@ -111,7 +111,7 @@ public class TemplateEngineUtils {
// 向树形模板上下文写入树字段相关变量
if (GenConstants.TPL_TREE.equals(tplCategory)) {
setTreeContext(context, genTable);
setTreeContext(context, genTable, paramsObj);
}
return context;
@@ -120,12 +120,11 @@ public class TemplateEngineUtils {
/**
* 向树形模板上下文写入树字段相关变量。
*
* @param context 模板上下文
* @param genTable 代码生成业务表对象
* @param context 模板上下文
* @param genTable 代码生成业务表对象
* @param paramsObj 已解析的 options 参数(避免重复解析)
*/
public static void setTreeContext(Dict context, GenTable genTable) {
String options = genTable.getOptions();
Dict paramsObj = JsonUtils.parseMap(options);
public static void setTreeContext(Dict context, GenTable genTable, Dict paramsObj) {
String treeCode = getTreeCode(paramsObj);
String treeParentCode = getTreeParentCode(paramsObj);
String treeName = getTreeName(paramsObj);
@@ -133,7 +132,17 @@ public class TemplateEngineUtils {
context.put("treeCode", treeCode);
context.put("treeParentCode", treeParentCode);
context.put("treeName", treeName);
context.put("expandColumn", getExpandColumn(genTable));
String expandTreeName = paramsObj.getStr(GenConstants.TREE_NAME);
int expandColumn = 0;
for (GenTableColumn column : genTable.getColumns()) {
if (column.isList()) {
expandColumn++;
if (column.getColumnName().equals(expandTreeName)) {
break;
}
}
}
context.put("expandColumn", expandColumn);
if (paramsObj.containsKey(GenConstants.TREE_PARENT_CODE)) {
context.put("tree_parent_code", paramsObj.get(GenConstants.TREE_PARENT_CODE));
}
@@ -209,14 +218,11 @@ public class TemplateEngineUtils {
// genFilePathFormat
if (template.contains("domain.java.vm")) {
fileName = StringUtils.format("{}/domain/{}.java", javaPath, className);
}
if (template.contains("vo.java.vm")) {
} else if (template.contains("vo.java.vm")) {
fileName = StringUtils.format("{}/domain/vo/{}Vo.java", javaPath, className);
}
if (template.contains("bo.java.vm")) {
} else if (template.contains("bo.java.vm")) {
fileName = StringUtils.format("{}/domain/bo/{}Bo.java", javaPath, className);
}
if (template.contains("mapper.java.vm")) {
} else if (template.contains("mapper.java.vm")) {
fileName = StringUtils.format("{}/mapper/{}Mapper.java", javaPath, className);
} else if (template.contains("service.java.vm")) {
fileName = StringUtils.format("{}/service/I{}Service.java", javaPath, className);
@@ -262,7 +268,7 @@ public class TemplateEngineUtils {
HashSet<String> importList = new HashSet<>();
for (GenTableColumn column : columns) {
if (!column.isSuperColumn() && GenConstants.TYPE_DATE.equals(column.getJavaType())) {
importList.add("java.util.Date");
importList.add("java.time.LocalDateTime");
importList.add("com.fasterxml.jackson.annotation.JsonFormat");
} else if (!column.isSuperColumn() && GenConstants.TYPE_BIGDECIMAL.equals(column.getJavaType())) {
importList.add("java.math.BigDecimal");
@@ -370,27 +376,4 @@ public class TemplateEngineUtils {
}
return StringUtils.EMPTY;
}
/**
* 获取需要在哪一列上面显示展开按钮
*
* @param genTable 业务表对象
* @return 展开按钮列序号
*/
public static int getExpandColumn(GenTable genTable) {
String options = genTable.getOptions();
Dict paramsObj = JsonUtils.parseMap(options);
String treeName = paramsObj.getStr(GenConstants.TREE_NAME);
int num = 0;
for (GenTableColumn column : genTable.getColumns()) {
if (column.isList()) {
num++;
String columnName = column.getColumnName();
if (columnName.equals(treeName)) {
break;
}
}
}
return num;
}
}

View File

@@ -13,7 +13,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 系统访问记录表 sys_login_info
@@ -75,6 +75,6 @@ public class SysLoginInfo implements Serializable {
/**
* 访问时间
*/
private Date loginTime;
private LocalDateTime loginTime;
}

View File

@@ -6,7 +6,7 @@ import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 操作日志记录表 oper_log
@@ -100,7 +100,7 @@ public class SysOperLog implements Serializable {
/**
* 操作时间
*/
private Date operTime;
private LocalDateTime operTime;
/**
* 消耗时间

View File

@@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 用户对象 sys_user
@@ -96,7 +96,7 @@ public class SysUser extends BaseEntity {
/**
* 最后登录时间
*/
private Date loginDate;
private LocalDateTime loginDate;
/**
* 备注

View File

@@ -6,7 +6,7 @@ import org.dromara.system.domain.SysLoginInfo;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@@ -76,7 +76,7 @@ public class SysLoginInfoBo implements Serializable {
/**
* 访问时间
*/
private Date loginTime;
private LocalDateTime loginTime;
/**
* 请求参数

View File

@@ -8,7 +8,7 @@ import org.dromara.system.domain.SysOperLog;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@@ -112,7 +112,7 @@ public class SysOperLogBo implements Serializable {
/**
* 操作时间
*/
private Date operTime;
private LocalDateTime operTime;
/**
* 消耗时间

View File

@@ -6,7 +6,7 @@ import org.dromara.common.translation.constant.TransConstant;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -74,7 +74,7 @@ public class ProfileUserVo implements Serializable {
/**
* 最后登录时间
*/
private Date loginDate;
private LocalDateTime loginDate;
/**
* 部门名

View File

@@ -10,7 +10,7 @@ import org.dromara.system.domain.SysConfig;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -67,6 +67,6 @@ public class SysConfigVo implements Serializable {
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
private LocalDateTime createTime;
}

View File

@@ -11,7 +11,7 @@ import org.dromara.system.domain.SysDept;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -99,7 +99,7 @@ public class SysDeptVo implements Serializable {
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
private LocalDateTime createTime;
/**
* 子部门

View File

@@ -10,7 +10,7 @@ import org.dromara.system.domain.SysDictData;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -83,6 +83,6 @@ public class SysDictDataVo implements Serializable {
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
private LocalDateTime createTime;
}

View File

@@ -8,7 +8,7 @@ import org.dromara.system.domain.SysDictType;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -52,6 +52,6 @@ public class SysDictTypeVo implements Serializable {
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
private LocalDateTime createTime;
}

View File

@@ -10,7 +10,7 @@ import org.dromara.system.domain.SysLoginInfo;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
@@ -95,7 +95,7 @@ public class SysLoginInfoVo implements Serializable {
* 访问时间
*/
@ExcelProperty(value = "访问时间")
private Date loginTime;
private LocalDateTime loginTime;
}

View File

@@ -7,7 +7,7 @@ import org.dromara.system.domain.SysMenu;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
@@ -106,7 +106,7 @@ public class SysMenuVo implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
/**
* 子菜单

View File

@@ -6,7 +6,7 @@ import org.dromara.system.domain.SysMessage;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 消息记录视图对象 sys_message
@@ -68,5 +68,5 @@ public class SysMessageVo implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
}

View File

@@ -8,7 +8,7 @@ import org.dromara.system.domain.SysNotice;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
@@ -68,6 +68,6 @@ public class SysNoticeVo implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
}

View File

@@ -10,7 +10,7 @@ import org.dromara.system.domain.SysOperLog;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -129,7 +129,7 @@ public class SysOperLogVo implements Serializable {
* 操作时间
*/
@ExcelProperty(value = "操作时间")
private Date operTime;
private LocalDateTime operTime;
/**
* 消耗时间

View File

@@ -8,7 +8,7 @@ import org.dromara.system.domain.SysOss;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* OSS对象存储视图对象 sys_oss
@@ -55,7 +55,7 @@ public class SysOssVo implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
/**
* 上传人

View File

@@ -12,7 +12,7 @@ import org.dromara.system.domain.SysPost;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 岗位信息视图对象 sys_post
@@ -80,7 +80,7 @@ public class SysPostVo implements Serializable {
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
private LocalDateTime createTime;
/**
* 部门名

View File

@@ -11,7 +11,7 @@ import org.dromara.system.domain.SysRole;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 角色信息视图对象 sys_role
@@ -86,7 +86,7 @@ public class SysRoleVo implements Serializable {
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
private LocalDateTime createTime;
/**
* 用户是否存在此角色标识 默认不存在

View File

@@ -6,7 +6,7 @@ import org.dromara.system.domain.SysSocial;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -135,5 +135,5 @@ public class SysSocialVo implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
}

View File

@@ -9,7 +9,7 @@ import org.dromara.system.listener.DeptExcelConverter;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 用户对象导出VO
@@ -83,13 +83,7 @@ public class SysUserExportVo implements Serializable {
* 最后登录时间
*/
@ExcelProperty(value = "最后登录时间")
private Date loginDate;
/**
* 部门名称
*/
@ExcelProperty(value = "部门名称")
private String deptName;
private LocalDateTime loginDate;
/**
* 负责人

View File

@@ -12,7 +12,7 @@ import org.dromara.system.domain.SysUser;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
@@ -96,7 +96,7 @@ public class SysUserVo implements Serializable {
/**
* 最后登录时间
*/
private Date loginDate;
private LocalDateTime loginDate;
/**
* 备注
@@ -106,7 +106,7 @@ public class SysUserVo implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
/**
* 部门名

View File

@@ -1,7 +1,8 @@
package org.dromara.system.listener;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import lombok.RequiredArgsConstructor;
import org.apache.fesod.sheet.converters.Converter;
import org.apache.fesod.sheet.enums.CellDataTypeEnum;
@@ -14,10 +15,10 @@ import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.TreeBuildUtils;
import org.dromara.system.domain.bo.SysDeptBo;
import org.dromara.system.service.ISysDeptService;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* Excel 部门转换处理
@@ -25,47 +26,41 @@ import java.util.Map;
* @author AprilWind
*/
@RequiredArgsConstructor
@Component
public class DeptExcelConverter implements Converter<Long> {
/**
* 线程内缓存ID → 名称
*/
private static final ThreadLocal<Map<Long, String>> TL_ID_TO_NAME = new ThreadLocal<>();
private static final String CACHE_KEY = "dept:excel";
/**
* 线程内缓存:名称 → ID
* Caffeine 缓存key=CACHE_KEYvalue=[idToName, nameToId]5分钟过期
*/
private static final ThreadLocal<Map<String, Long>> TL_NAME_TO_ID = new ThreadLocal<>();
private static final Cache<String, DeptMaps> DEPT_CACHE = Caffeine.newBuilder()
.expireAfterWrite(30, TimeUnit.SECONDS)
.build();
private DeptMaps getDeptMaps() {
ISysDeptService deptService = SpringUtils.getBean(ISysDeptService.class);
return DEPT_CACHE.get(CACHE_KEY, k -> {
Map<String, Tree<Long>> deptPathToTreeMap = buildDeptPathMap(deptService);
Map<Long, String> idToName = new HashMap<>();
Map<String, Long> nameToId = new HashMap<>();
deptPathToTreeMap.forEach((name, treeNode) -> {
Long deptId = treeNode.getId();
idToName.put(deptId, name);
nameToId.put(name, deptId);
});
return new DeptMaps(idToName, nameToId);
});
}
/**
* 初始化当前线程的部门缓存(一次导出/导入只执行1次
* 构建部门路径 → 树节点映射,供 Converter 和 Options 共用
*/
private void initThreadCache() {
Map<Long, String> idMap = TL_ID_TO_NAME.get();
if (CollUtil.isNotEmpty(idMap)) {
// 当前线程已加载,直接返回
return;
}
Map<String, Tree<Long>> deptPathToTreeMap = TreeBuildUtils.buildTreeNodeMap(
SpringUtils.getBean(ISysDeptService.class).selectDeptTreeList(new SysDeptBo()),
static Map<String, Tree<Long>> buildDeptPathMap(ISysDeptService deptService) {
return TreeBuildUtils.buildTreeNodeMap(
deptService.selectDeptTreeList(new SysDeptBo()),
"/",
Tree::getName
);
// 构建互转映射
Map<Long, String> idToName = new HashMap<>();
Map<String, Long> nameToId = new HashMap<>();
deptPathToTreeMap.forEach((name, treeNode) -> {
Long deptId = treeNode.getId();
idToName.put(deptId, name);
nameToId.put(name, deptId);
});
// 设置到当前线程
TL_ID_TO_NAME.set(idToName);
TL_NAME_TO_ID.set(nameToId);
}
/**
@@ -93,12 +88,7 @@ public class DeptExcelConverter implements Converter<Long> {
if (StringUtils.isBlank(deptName)) {
return null;
}
// 初始化线程缓存(只执行一次)
initThreadCache();
// 从线程缓存中直接获取,不查库
return TL_NAME_TO_ID.get().get(deptName);
return getDeptMaps().nameToId().get(deptName);
}
/**
@@ -109,13 +99,11 @@ public class DeptExcelConverter implements Converter<Long> {
if (value == null) {
return new WriteCellData<>("");
}
// 初始化线程缓存(只执行一次)
initThreadCache();
// 从线程缓存中直接获取,不查库
String deptName = TL_ID_TO_NAME.get().getOrDefault(value, "");
String deptName = getDeptMaps().idToName().getOrDefault(value, "");
return new WriteCellData<>(deptName);
}
private record DeptMaps(Map<Long, String> idToName, Map<String, Long> nameToId) {
}
}

View File

@@ -1,15 +1,10 @@
package org.dromara.system.listener;
import cn.hutool.core.lang.tree.Tree;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.TreeBuildUtils;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.excel.core.ExcelOptionsProvider;
import org.dromara.system.domain.bo.SysDeptBo;
import org.dromara.system.service.ISysDeptService;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
@@ -18,11 +13,8 @@ import java.util.Set;
* @author AprilWind
*/
@RequiredArgsConstructor
@Component
public class DeptExcelOptions implements ExcelOptionsProvider {
private final ISysDeptService deptService;
/**
* 获取下拉选项数据
*
@@ -30,9 +22,8 @@ public class DeptExcelOptions implements ExcelOptionsProvider {
*/
@Override
public Set<String> getOptions() {
List<Tree<Long>> trees = deptService.selectDeptTreeList(new SysDeptBo());
Map<String, Tree<Long>> treeMap = TreeBuildUtils.buildTreeNodeMap(trees, "/", Tree::getName);
return treeMap.keySet();
ISysDeptService deptService = SpringUtils.getBean(ISysDeptService.class);
return DeptExcelConverter.buildDeptPathMap(deptService).keySet();
}
}

View File

@@ -73,7 +73,6 @@ public interface SysUserMapper extends BaseMapperPlus<SysUser, SysUserVo>, MPJBa
default List<SysUserExportVo> selectUserExportList(SysUserBo user, List<Long> deptIds) {
MPJLambdaWrapper<SysUser> wrapper = JoinWrappers.lambda("u", SysUser.class)
.selectAll(SysUser.class)
.selectAs(SysDept::getDeptName, SysUserExportVo::getDeptName)
.selectAs("u1", SysUser::getUserName, SysUserExportVo::getLeaderName)
.leftJoin(SysDept.class, "d", SysDept::getDeptId, SysUser::getDeptId)
.leftJoin(SysUser.class, "u1", SysUser::getUserId, SysDept::getLeader)

View File

@@ -152,7 +152,7 @@ public class SysConfigServiceImpl implements ISysConfigService, ConfigService {
SysConfig config = MapstructUtils.convert(bo, SysConfig.class);
if (config.getConfigId() != null) {
SysConfig temp = baseMapper.selectById(config.getConfigId());
if (!StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) {
if (ObjectUtil.isNotNull(temp) && !StringUtils.equals(temp.getConfigKey(), config.getConfigKey())) {
CacheUtils.evict(CacheNames.SYS_CONFIG, temp.getConfigKey());
}
row = baseMapper.updateById(config);

View File

@@ -216,7 +216,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
@Override
public Long selectDeptLeaderById(Long deptId) {
SysDeptVo vo = SpringUtils.getAopProxy(this).selectDeptById(deptId);
return vo.getLeader();
return ObjectUtil.isNull(vo) ? null : vo.getLeader();
}
/**
@@ -312,7 +312,10 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
@Override
public int insertDept(SysDeptBo bo) {
SysDept info = baseMapper.selectById(bo.getParentId());
// 如果父节点不为正常状态,则不允许新增子节点
// 如果父节点不存在或不为正常状态,则不允许新增子节点
if (ObjectUtil.isNull(info)) {
throw new ServiceException("父部门不存在");
}
if (!SystemConstants.NORMAL.equals(info.getStatus())) {
throw new ServiceException("部门停用,不允许新增");
}
@@ -390,7 +393,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
for (SysDept child : children) {
SysDept dept = new SysDept();
dept.setDeptId(child.getDeptId());
dept.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
dept.setAncestors(StringUtils.replaceOnce(child.getAncestors(), oldAncestors, newAncestors));
list.add(dept);
}
if (CollUtil.isNotEmpty(list)) {
@@ -408,7 +411,7 @@ public class SysDeptServiceImpl implements ISysDeptService, DeptService {
*/
@Caching(evict = {
@CacheEvict(cacheNames = CacheNames.SYS_DEPT, key = "#deptId"),
@CacheEvict(cacheNames = CacheNames.SYS_DEPT_AND_CHILD, key = "#deptId")
@CacheEvict(cacheNames = CacheNames.SYS_DEPT_AND_CHILD, allEntries = true)
})
@Override
public int deleteDeptById(Long deptId) {

View File

@@ -108,7 +108,7 @@ public class SysDictTypeServiceImpl implements ISysDictTypeService, DictService
@Override
public List<SysDictDataVo> selectDictDataByType(String dictType) {
List<SysDictDataVo> dictDatas = dictDataMapper.selectDictDataByType(dictType);
return CollUtil.isNotEmpty(dictDatas) ? dictDatas : null;
return CollUtil.isNotEmpty(dictDatas) ? dictDatas : Collections.emptyList();
}
/**

View File

@@ -28,8 +28,8 @@ import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -56,8 +56,8 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService {
@EventListener
public void recordLoginInfo(LoginInfoEvent loginInfoEvent) {
HttpServletRequest request = loginInfoEvent.getRequest();
final UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
final String ip = ServletUtils.getClientIP(request);
UserAgent userAgent = UserAgentUtil.parse(request.getHeader("User-Agent"));
String ip = ServletUtils.getClientIP(request);
// 客户端信息
String clientId = request.getHeader(LoginHelper.CLIENT_KEY);
SysClientVo client = null;
@@ -144,7 +144,7 @@ public class SysLoginInfoServiceImpl implements ISysLoginInfoService {
@Override
public void insertLoginInfo(SysLoginInfoBo bo) {
SysLoginInfo loginInfo = MapstructUtils.convert(bo, SysLoginInfo.class);
loginInfo.setLoginTime(new Date());
loginInfo.setLoginTime(LocalDateTime.now());
baseMapper.insert(loginInfo);
}

View File

@@ -23,10 +23,9 @@ import org.dromara.system.mapper.SysMessageMapper;
import org.dromara.system.service.ISysMessageService;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* 消息记录服务实现
@@ -212,7 +211,7 @@ public class SysMessageServiceImpl implements ISysMessageService, MessageService
// 分类匹配
lqw.eq(SysMessage::getCategory, category);
// 仅查询30天内消息
lqw.ge(SysMessage::getCreateTime, new Date(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(BOX_DAYS)));
lqw.ge(SysMessage::getCreateTime, LocalDateTime.now().minusDays(BOX_DAYS));
// 全局消息 或 当前用户在接收人范围内
lqw.and(wrapper -> wrapper.eq(SysMessage::getSendUserIds, GLOBAL_USER_IDS)
.or()

View File

@@ -19,8 +19,8 @@ import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -99,7 +99,7 @@ public class SysOperLogServiceImpl implements ISysOperLogService {
@Override
public void insertOperlog(SysOperLogBo bo) {
SysOperLog operLog = MapstructUtils.convert(bo, SysOperLog.class);
operLog.setOperTime(new Date());
operLog.setOperTime(LocalDateTime.now());
baseMapper.insert(operLog);
}

View File

@@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import java.io.Serial;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 请假对象 test_leave
@@ -42,12 +42,12 @@ public class TestLeave extends BaseEntity {
/**
* 开始时间
*/
private Date startDate;
private LocalDateTime startDate;
/**
* 结束时间
*/
private Date endDate;
private LocalDateTime endDate;
/**
* 请假天数

View File

@@ -12,7 +12,7 @@ import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
@@ -57,7 +57,7 @@ public class TestLeaveBo implements Serializable {
@NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class})
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date startDate;
private LocalDateTime startDate;
/**
* 结束时间
@@ -65,7 +65,7 @@ public class TestLeaveBo implements Serializable {
@NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class})
@DateTimeFormat(pattern = "yyyy-MM-dd")
@JsonFormat(pattern = "yyyy-MM-dd")
private Date endDate;
private LocalDateTime endDate;
/**
* 请假天数

View File

@@ -10,7 +10,7 @@ import org.dromara.workflow.domain.FlowCategory;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 流程分类视图对象 wf_category
@@ -64,6 +64,6 @@ public class FlowCategoryVo implements Serializable {
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
private LocalDateTime createTime;
}

View File

@@ -6,7 +6,7 @@ import org.dromara.workflow.common.constant.FlowConstant;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 流程定义视图
@@ -24,12 +24,12 @@ public class FlowDefinitionVo implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
/**
* 更新时间
*/
private Date updateTime;
private LocalDateTime updateTime;
/**
* 删除标记

View File

@@ -9,7 +9,7 @@ import org.dromara.workflow.common.constant.FlowConstant;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
/**
@@ -28,12 +28,12 @@ public class FlowHisTaskVo implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
/**
* 更新时间
*/
private Date updateTime;
private LocalDateTime updateTime;
/**
* 删除标记
@@ -215,7 +215,7 @@ public class FlowHisTaskVo implements Serializable {
*
* @param createTime 创建时间
*/
public void setCreateTime(Date createTime) {
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
updateRunDuration();
}
@@ -225,7 +225,7 @@ public class FlowHisTaskVo implements Serializable {
*
* @param updateTime 更新时间
*/
public void setUpdateTime(Date updateTime) {
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
updateRunDuration();
}

View File

@@ -5,7 +5,7 @@ import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.workflow.common.constant.FlowConstant;
import java.util.Date;
import java.time.LocalDateTime;
/**
* 流程实例视图对象。
@@ -23,12 +23,12 @@ public class FlowInstanceVo {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
/**
* 更新时间
*/
private Date updateTime;
private LocalDateTime updateTime;
/**
* 删除标记

View File

@@ -10,7 +10,7 @@ import org.dromara.workflow.domain.FlowSpel;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -74,6 +74,6 @@ public class FlowSpelVo implements Serializable {
* 创建时间
*/
@ExcelProperty(value = "创建时间")
private Date createTime;
private LocalDateTime createTime;
}

View File

@@ -9,7 +9,7 @@ import org.dromara.workflow.common.constant.FlowConstant;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@@ -32,12 +32,12 @@ public class FlowTaskVo implements Serializable {
/**
* 创建时间
*/
private Date createTime;
private LocalDateTime createTime;
/**
* 更新时间
*/
private Date updateTime;
private LocalDateTime updateTime;
/**
* 删除标记

View File

@@ -8,7 +8,7 @@ import org.dromara.workflow.domain.TestLeave;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.time.LocalDateTime;
/**
@@ -47,13 +47,13 @@ public class TestLeaveVo implements Serializable {
* 开始时间
*/
@ExcelProperty(value = "开始时间")
private Date startDate;
private LocalDateTime startDate;
/**
* 结束时间
*/
@ExcelProperty(value = "结束时间")
private Date endDate;
private LocalDateTime endDate;
/**
* 请假天数

View File

@@ -11,6 +11,7 @@ import org.dromara.warm.flow.core.enums.NodeType;
import org.dromara.warm.flow.orm.entity.FlowDefinition;
import org.dromara.warm.flow.orm.entity.FlowHisTask;
import org.dromara.warm.flow.orm.entity.FlowInstance;
import org.dromara.workflow.common.enums.TaskStatusEnum;
import org.dromara.workflow.domain.FlowInstanceBizExt;
import org.dromara.workflow.domain.bo.FlowTaskBo;
import org.dromara.workflow.domain.vo.FlowHisTaskVo;
@@ -65,6 +66,7 @@ public interface FlwHisTaskMapper extends BaseMapperPlus<FlowHisTask, FlowHisTas
.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())

View File

@@ -1,23 +1,8 @@
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.warm.flow.core.enums.NodeType;
import org.dromara.warm.flow.orm.entity.*;
import org.dromara.workflow.domain.FlowInstanceBizExt;
import org.dromara.workflow.domain.bo.FlowTaskBo;
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;
import org.dromara.warm.flow.orm.entity.FlowUser;
/**
* 任务信息Mapper接口
@@ -27,94 +12,4 @@ import static org.dromara.workflow.common.constant.FlowConstant.NOT_DELETED;
*/
public interface FlwUserMapper extends BaseMapperPlus<FlowUser, FlowUser>, MPJBaseMapper<FlowUser> {
default Page<FlowTaskVo> getListRunTask(Page<FlowTaskVo> page, FlowTaskBo bo, List<String> categoryIds, String userId) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<FlowTask> wrapper = JoinWrappers.lambda("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)
.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())
.orderByDesc("t", FlowTask::getCreateTime)
.orderByDesc("t", FlowTask::getUpdateTime);
return wrapper.page(page, FlowTaskVo.class);
}
default Page<FlowTaskVo> getTaskCopyByPage(Page<FlowTaskVo> page, FlowTaskBo bo, List<String> categoryIds, String userId) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<FlowUser> 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)
.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)
.orderByDesc("a", FlowUser::getCreateTime)
.orderByDesc("b", FlowHisTask::getUpdateTime);
return wrapper.page(page, FlowTaskVo.class);
}
}

View File

@@ -5,6 +5,8 @@ import org.dromara.workflow.domain.bo.FlowCategoryBo;
import org.dromara.workflow.domain.vo.FlowCategoryVo;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* 流程分类Service接口
@@ -29,6 +31,14 @@ public interface IFlwCategoryService {
*/
String selectCategoryNameById(Long categoryId);
/**
* 根据流程分类ID查询流程分类名称
*
* @param categoryIds 流程分类ID
* @return 流程分类名称
*/
Map<Long, String> selectCategoryNameByIds(Set<Long> categoryIds);
/**
* 查询符合条件的流程分类列表
*

View File

@@ -3,6 +3,7 @@ package org.dromara.workflow.service.impl;
import cn.hutool.core.convert.Convert;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.core.TranslationInterface;
import org.dromara.workflow.common.ConditionalOnEnable;
@@ -10,6 +11,10 @@ import org.dromara.workflow.common.constant.FlowConstant;
import org.dromara.workflow.service.IFlwCategoryService;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* 流程分类名称翻译实现
*
@@ -24,15 +29,30 @@ public class CategoryNameTranslationImpl implements TranslationInterface<String>
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));
}
@Override
public Map<Object, String> translationBatch(Set<Object> keys, String other) {
Set<Long> categoryIds = collectLongIds(keys);
if (categoryIds.isEmpty()) {
return Map.of();
}
Map<Long, String> categoryNames = flwCategoryService.selectCategoryNameByIds(categoryIds);
Map<Object, String> result = new LinkedHashMap<>(keys.size());
for (Object key : keys) {
result.put(key, buildValue(key, categoryNames));
}
return result;
}
private String buildValue(Object source, Map<Long, String> categoryNames) {
if (source instanceof String ids) {
return joinMappedValues(ids, categoryNames::get);
}
return source == null ? null : categoryNames.get(Convert.toLong(source));
}
}

View File

@@ -26,8 +26,7 @@ import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
/**
* 流程分类Service业务层处理
@@ -70,6 +69,23 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ
return ObjectUtils.notNullGetter(category, FlowCategory::getCategoryName);
}
/**
* 根据流程分类ID查询流程分类名称
*
* @param categoryIds 流程分类ID
* @return 流程分类名称
*/
@Override
public Map<Long, String> selectCategoryNameByIds(Set<Long> categoryIds) {
if (CollUtil.isEmpty(categoryIds)) {
return Collections.emptyMap();
}
List<FlowCategory> list = baseMapper.selectList(new LambdaQueryWrapper<FlowCategory>()
.select(FlowCategory::getCategoryId, FlowCategory::getCategoryName)
.in(FlowCategory::getCategoryId, categoryIds));
return StreamUtils.toMap(list, FlowCategory::getCategoryId, FlowCategory::getCategoryName);
}
/**
* 查询符合条件的流程分类列表
*

View File

@@ -12,7 +12,6 @@ import org.dromara.common.core.domain.dto.DeptDTO;
import org.dromara.common.core.domain.dto.TaskAssigneeDTO;
import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.domain.model.TaskAssigneeBody;
import org.dromara.common.core.enums.FormatsType;
import org.dromara.common.core.service.*;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StreamUtils;
@@ -191,7 +190,7 @@ public class FlwTaskAssigneeServiceImpl implements IFlwTaskAssigneeService, Hand
.setHandlerCode(assignee -> StringUtils.blankToDefault(assignee.getHandlerCode(), ""))
.setHandlerName(assignee -> StringUtils.blankToDefault(assignee.getHandlerName(), ""))
.setGroupName(assignee -> this.getGroupName(type, assignee.getGroupName()))
.setCreateTime(assignee -> DateUtils.parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM_SS, assignee.getCreateTime()));
.setCreateTime(assignee -> DateUtils.formatDateTime(assignee.getCreateTime()));
}
/**

View File

@@ -17,6 +17,7 @@ import org.dromara.common.core.domain.dto.UserDTO;
import org.dromara.common.core.enums.BusinessStatusEnum;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.service.UserService;
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.ValidatorUtils;

View File

@@ -1,7 +1,7 @@
package org.dromara.workflow.service.impl;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import java.time.temporal.ChronoUnit;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
@@ -126,7 +126,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
*/
@Override
public TestLeaveVo insertByBo(TestLeaveBo bo) {
long day = DateUtil.betweenDay(bo.getStartDate(), bo.getEndDate(), true);
long day = ChronoUnit.DAYS.between(bo.getStartDate(), bo.getEndDate());
// 截止日期也算一天
bo.setLeaveDays((int) day + 1);
bo.setApplyCode(System.currentTimeMillis() + StrUtil.EMPTY);
@@ -150,7 +150,7 @@ public class TestLeaveServiceImpl implements ITestLeaveService {
@Transactional(rollbackFor = Exception.class)
@Override
public TestLeaveVo submitAndFlowStart(TestLeaveBo bo) {
long day = DateUtil.betweenDay(bo.getStartDate(), bo.getEndDate(), true);
long day = ChronoUnit.DAYS.between(bo.getStartDate(), bo.getEndDate());
// 截止日期也算一天
bo.setLeaveDays((int) day + 1);
if (ObjectUtil.isNull(bo.getId())) {