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 97fe67f61..a1d96464b 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 @@ -1,8 +1,9 @@ package cn.iocoder.yudao.framework.common.util.date; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; -import java.time.Duration; +import java.time.*; import java.util.Calendar; import java.util.Date; @@ -25,6 +26,34 @@ public class DateUtils { public static final String FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = "yyyy-MM-dd HH:mm:ss"; + /** + * 将 LocalDateTime 转换成 Date + * + * @param date LocalDateTime + * @return LocalDateTime + */ + public static Date of(LocalDateTime date) { + // 将此日期时间与时区相结合以创建 ZonedDateTime + ZonedDateTime zonedDateTime = date.atZone(ZoneId.systemDefault()); + // 本地时间线 LocalDateTime 到即时时间线 Instant 时间戳 + Instant instant = zonedDateTime.toInstant(); + // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间 + return Date.from(instant); + } + + /** + * 将 Date 转换成 LocalDateTime + * + * @param date Date + * @return LocalDateTime + */ + public static LocalDateTime of(Date date) { + // 转为时间戳 + Instant instant = date.toInstant(); + // UTC时间(世界协调时间,UTC + 00:00)转北京(北京,UTC + 8:00)时间 + return LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + } + public static Date addTime(Duration duration) { return new Date(System.currentTimeMillis() + duration.toMillis()); } @@ -33,6 +62,11 @@ public class DateUtils { return System.currentTimeMillis() > time.getTime(); } + public static boolean isExpired(LocalDateTime time) { + LocalDateTime now = LocalDateTime.now(); + return now.isAfter(time); + } + public static long diff(Date endTime, Date startTime) { return endTime.getTime() - startTime.getTime(); } @@ -40,24 +74,28 @@ public class DateUtils { /** * 创建指定时间 * - * @param year 年 - * @param mouth 月 - * @param day 日 + * @param year 年 + * @param mouth 月 + * @param day 日 * @return 指定时间 */ public static Date buildTime(int year, int mouth, int day) { return buildTime(year, mouth, day, 0, 0, 0); } + public static LocalDateTime buildLocalDateTime(int year, int mouth, int day) { + return LocalDateTime.of(year, mouth, day, 0, 0, 0); + } + /** * 创建指定时间 * - * @param year 年 - * @param mouth 月 - * @param day 日 - * @param hour 小时 - * @param minute 分钟 - * @param second 秒 + * @param year 年 + * @param mouth 月 + * @param day 日 + * @param hour 小时 + * @param minute 分钟 + * @param second 秒 * @return 指定时间 */ public static Date buildTime(int year, int mouth, int day, @@ -83,6 +121,16 @@ public class DateUtils { return a.compareTo(b) > 0 ? a : b; } + public static LocalDateTime max(LocalDateTime a, LocalDateTime b) { + if (a == null) { + return b; + } + if (b == null) { + return a; + } + return a.isAfter(b) ? a : b; + } + public static boolean beforeNow(Date date) { return date.getTime() < System.currentTimeMillis(); } @@ -91,6 +139,10 @@ public class DateUtils { return date.getTime() >= System.currentTimeMillis(); } + public static boolean afterNow(LocalDateTime localDateTime) { + return localDateTime.isAfter(LocalDateTime.now()); + } + /** * 计算当期时间相差的日期 * @@ -135,4 +187,14 @@ public class DateUtils { return DateUtil.isSameDay(date, new Date()); } + /** + * 是否今天 + * + * @param date 日期 + * @return 是否 + */ + public static boolean isToday(LocalDateTime date) { + return LocalDateTimeUtil.isSameDay(date, LocalDateTime.now()); + } + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java b/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java index da8a60152..870c74f11 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-error-code/src/main/java/cn/iocoder/yudao/framework/errorcode/core/loader/ErrorCodeLoaderImpl.java @@ -10,7 +10,7 @@ import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.event.EventListener; import org.springframework.scheduling.annotation.Scheduled; -import java.util.Date; +import java.time.LocalDateTime; import java.util.List; /** @@ -41,7 +41,7 @@ public class ErrorCodeLoaderImpl implements ErrorCodeLoader { /** * 缓存错误码的最大更新时间,用于后续的增量轮询,判断是否有更新 */ - private Date maxUpdateTime; + private LocalDateTime maxUpdateTime; @EventListener(ApplicationReadyEvent.class) public void loadErrorCodes() { diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java index d3b94d25a..75819cbe1 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/aop/OperateLogAspect.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.operatelog.core.aop; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; @@ -32,6 +33,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.annotation.Annotation; import java.lang.reflect.Array; +import java.time.LocalDateTime; import java.util.*; import java.util.function.Predicate; import java.util.stream.IntStream; @@ -94,7 +96,7 @@ public class OperateLogAspect { } // 记录开始时间 - Date startTime = new Date(); + LocalDateTime startTime = LocalDateTime.now(); try { // 执行原有方法 Object result = joinPoint.proceed(); @@ -128,7 +130,7 @@ public class OperateLogAspect { private void log(ProceedingJoinPoint joinPoint, cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog, ApiOperation apiOperation, - Date startTime, Object result, Throwable exception) { + LocalDateTime startTime, Object result, Throwable exception) { try { // 判断不记录的情况 if (!isLogEnable(joinPoint, operateLog)) { @@ -145,7 +147,7 @@ public class OperateLogAspect { private void log0(ProceedingJoinPoint joinPoint, cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog, ApiOperation apiOperation, - Date startTime, Object result, Throwable exception) { + LocalDateTime startTime, Object result, Throwable exception) { OperateLog operateLogObj = new OperateLog(); // 补全通用字段 operateLogObj.setTraceId(TracerUtils.getTraceId()); @@ -226,7 +228,7 @@ public class OperateLogAspect { private static void fillMethodFields(OperateLog operateLogObj, ProceedingJoinPoint joinPoint, cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog operateLog, - Date startTime, Object result, Throwable exception) { + LocalDateTime startTime, Object result, Throwable exception) { MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); operateLogObj.setJavaMethod(methodSignature.toString()); if (operateLog == null || operateLog.logArgs()) { @@ -235,7 +237,7 @@ public class OperateLogAspect { if (operateLog == null || operateLog.logResultData()) { operateLogObj.setResultData(obtainResultData(result)); } - operateLogObj.setDuration((int) (System.currentTimeMillis() - startTime.getTime())); + operateLogObj.setDuration((int) (LocalDateTimeUtil.between(startTime, LocalDateTime.now()).toMillis())); // (正常)处理 resultCode 和 resultMsg 字段 if (result instanceof CommonResult) { CommonResult commonResult = (CommonResult) result; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLog.java b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLog.java index 7bce1c6bd..1e3b8c8a8 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLog.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-operatelog/src/main/java/cn/iocoder/yudao/framework/operatelog/core/service/OperateLog.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.operatelog.core.service; import lombok.Data; -import java.util.Date; +import java.time.LocalDateTime; import java.util.Map; /** @@ -85,7 +85,7 @@ public class OperateLog { /** * 开始时间 */ - private Date startTime; + private LocalDateTime startTime; /** * 执行时长,单位:毫秒 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderNotifyRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderNotifyRespDTO.java index a5c24c24e..91f3b2a37 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderNotifyRespDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderNotifyRespDTO.java @@ -5,7 +5,7 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; -import java.util.Date; +import java.time.LocalDateTime; /** * 支付通知 Response DTO @@ -33,7 +33,7 @@ public class PayOrderNotifyRespDTO { /** * 支付成功时间 */ - private Date successTime; + private LocalDateTime successTime; /** * 通知的原始数据 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java index 6f99bb80d..ea3628931 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java @@ -7,7 +7,7 @@ import org.hibernate.validator.constraints.URL; import javax.validation.constraints.DecimalMin; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; -import java.util.Date; +import java.time.LocalDateTime; import java.util.Map; /** @@ -68,7 +68,7 @@ public class PayOrderUnifiedReqDTO { * 支付过期时间 */ @NotNull(message = "支付过期时间不能为空") - private Date expireTime; + private LocalDateTime expireTime; // ========== 拓展参数 ========== /** diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundNotifyDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundNotifyDTO.java index 05fcff3c1..edb593d82 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundNotifyDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundNotifyDTO.java @@ -5,7 +5,7 @@ import lombok.Builder; import lombok.Data; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; /** * 从渠道返回数据中解析得到的支付退款通知的Notify DTO @@ -57,7 +57,7 @@ public class PayRefundNotifyDTO { /** * 退款成功时间 */ - private Date refundSuccessTime; + private LocalDateTime refundSuccessTime; } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/config/PayProperties.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/config/PayProperties.java new file mode 100644 index 000000000..d26c394ed --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/config/PayProperties.java @@ -0,0 +1,38 @@ +package pay.config; + +import lombok.Data; +import org.hibernate.validator.constraints.URL; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotEmpty; + +@ConfigurationProperties(prefix = "yudao.pay") +@Validated +@Data +public class PayProperties { + + /** + * 支付回调地址 + * 注意,支付渠道统一回调到 payNotifyUrl 地址,由支付模块统一处理;然后,自己的支付模块,在回调 PayAppDO.payNotifyUrl 地址 + */ + @NotEmpty(message = "支付回调地址不能为空") + @URL(message = "支付回调地址的格式必须是 URL") + private String payNotifyUrl; + /** + * 退款回调地址 + * 注意点,同 {@link #payNotifyUrl} 属性 + */ + @NotEmpty(message = "退款回调地址不能为空") + @URL(message = "退款回调地址的格式必须是 URL") + private String refundNotifyUrl; + + + /** + * 支付完成的返回地址 + */ + @URL(message = "支付返回的地址的格式必须是 URL") + @NotEmpty(message = "支付返回的地址不能为空") + private String payReturnUrl; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/config/YudaoPayAutoConfiguration.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/config/YudaoPayAutoConfiguration.java new file mode 100644 index 000000000..aa683baaf --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/config/YudaoPayAutoConfiguration.java @@ -0,0 +1,24 @@ +package pay.config; + +import cn.iocoder.yudao.framework.pay.config.PayProperties; +import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; +import cn.iocoder.yudao.framework.pay.core.client.impl.PayClientFactoryImpl; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 支付配置类 + * + * @author 芋道源码 + */ +@Configuration +@EnableConfigurationProperties(PayProperties.class) +public class YudaoPayAutoConfiguration { + + @Bean + public PayClientFactory payClientFactory() { + return new PayClientFactoryImpl(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/AbstractPayCodeMapping.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/AbstractPayCodeMapping.java new file mode 100644 index 000000000..0754c8cee --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/AbstractPayCodeMapping.java @@ -0,0 +1,34 @@ +package pay.core.client; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; +import cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants; +import lombok.extern.slf4j.Slf4j; + +/** + * 将 API 的错误码,转换为通用的错误码 + * + * @see PayCommonResult + * @see PayFrameworkErrorCodeConstants + * + * @author 芋道源码 + */ +@Slf4j +public abstract class AbstractPayCodeMapping { + + public final ErrorCode apply(String apiCode, String apiMsg) { + if (apiCode == null) { + log.error("[apply][API 错误码为空,请排查]"); + return PayFrameworkErrorCodeConstants.EXCEPTION; + } + ErrorCode errorCode = this.apply0(apiCode, apiMsg); + if (errorCode == null) { + log.error("[apply][API 错误码({}) 错误提示({}) 无法匹配]", apiCode, apiMsg); + return PayFrameworkErrorCodeConstants.PAY_UNKNOWN; + } + return errorCode; + } + + protected abstract ErrorCode apply0(String apiCode, String apiMsg); + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayClient.java new file mode 100644 index 000000000..b19ffb32f --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayClient.java @@ -0,0 +1,74 @@ +package pay.core.client; + + +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.*; + +/** + * 支付客户端,用于对接各支付渠道的 SDK,实现发起支付、退款等功能 + * + * @author 芋道源码 + */ +public interface PayClient { + + /** + * 获得渠道编号 + * + * @return 渠道编号 + */ + Long getId(); + + /** + * 调用支付渠道,统一下单 + * + * @param reqDTO 下单信息 + * @return 各支付渠道的返回结果 + */ + cn.iocoder.yudao.framework.pay.core.client.PayCommonResult unifiedOrder(PayOrderUnifiedReqDTO reqDTO); + + /** + * 解析支付单的通知结果 + * + * @param data 通知结果 + * @return 解析结果 + * @throws Exception 解析失败,抛出异常 + */ + PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception; + + /** + * 调用支付渠道,进行退款 + * @param reqDTO 统一退款请求信息 + * @return 各支付渠道的统一返回结果 + */ + PayCommonResult unifiedRefund(PayRefundUnifiedReqDTO reqDTO); + + /** + * 解析支付退款通知数据 + * @param notifyData 支付退款通知请求数据 + * @return 支付退款通知的Notify DTO + */ + PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData); + + // TODO @芋艿:后续改成非 default,避免不知道去实现 + /** + * 验证是否渠道通知 + * + * @param notifyData 通知数据 + * @return 默认是 true + */ + default boolean verifyNotifyData(PayNotifyDataDTO notifyData) { + return true; + } + + // TODO @芋艿:后续改成非 default,避免不知道去实现 + /** + * 判断是否为退款通知 + * + * @param notifyData 通知数据 + * @return 默认是 false + */ + default boolean isRefundNotify(PayNotifyDataDTO notifyData){ + return false; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayClientConfig.java new file mode 100644 index 000000000..18ca10c9c --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayClientConfig.java @@ -0,0 +1,42 @@ +package pay.core.client; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validator; +import java.util.Set; + +/** + * 支付客户端的配置,本质是支付渠道的配置 + * 每个不同的渠道,需要不同的配置,通过子类来定义 + * + * @author 芋道源码 + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS) +// @JsonTypeInfo 注解的作用,Jackson 多态 +// 1. 序列化到时数据库时,增加 @class 属性。 +// 2. 反序列化到内存对象时,通过 @class 属性,可以创建出正确的类型 +public interface PayClientConfig { + + /** + * 配置验证参数是 + * + * @param validator 校验对象 + * @return 配置好的验证参数 + */ + Set> verifyParam(Validator validator); + + // TODO @aquan:貌似抽象一个 validation group 就好了! + /** + * 参数校验 + * + * @param validator 校验对象 + */ + default void validate(Validator validator) { + Set> violations = verifyParam(validator); + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayClientFactory.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayClientFactory.java new file mode 100644 index 000000000..d058533d2 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayClientFactory.java @@ -0,0 +1,31 @@ +package pay.core.client; + +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; + +/** + * 支付客户端的工厂接口 + * + * @author 芋道源码 + */ +public interface PayClientFactory { + + /** + * 获得支付客户端 + * + * @param channelId 渠道编号 + * @return 支付客户端 + */ + PayClient getPayClient(Long channelId); + + /** + * 创建支付客户端 + * + * @param channelId 渠道编号 + * @param channelCode 渠道编码 + * @param config 支付配置 + */ + void createOrUpdatePayClient(Long channelId, String channelCode, + Config config); + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayCommonResult.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayCommonResult.java new file mode 100644 index 000000000..f633b85e0 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/PayCommonResult.java @@ -0,0 +1,58 @@ +package pay.core.client; + +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.common.exception.ErrorCode; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping; +import cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +/** + * 支付的 CommonResult 拓展类 + * + * 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段 + * + * @author 芋道源码 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class PayCommonResult extends CommonResult { + + /** + * API 返回错误码 + * + * 由于第三方的错误码可能是字符串,所以使用 String 类型 + */ + private String apiCode; + /** + * API 返回提示 + */ + private String apiMsg; + + private PayCommonResult() { + } + + public static PayCommonResult build(String apiCode, String apiMsg, T data, AbstractPayCodeMapping codeMapping) { + Assert.notNull(codeMapping, "参数 codeMapping 不能为空"); + PayCommonResult result = new PayCommonResult().setApiCode(apiCode).setApiMsg(apiMsg); + result.setData(data); + // 翻译错误码 + if (codeMapping != null) { + ErrorCode errorCode = codeMapping.apply(apiCode, apiMsg); + result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg()); + } + return result; + } + + public static PayCommonResult error(Throwable ex) { + PayCommonResult result = new PayCommonResult<>(); + result.setCode(PayFrameworkErrorCodeConstants.EXCEPTION.getCode()); + result.setMsg(ExceptionUtil.getRootCauseMessage(ex)); + return result; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayNotifyDataDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayNotifyDataDTO.java new file mode 100644 index 000000000..fc1907fa9 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayNotifyDataDTO.java @@ -0,0 +1,29 @@ +package pay.core.client.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.ToString; + +import java.util.Map; + + +/** + * 支付订单,退款订单回调,渠道的统一通知请求数据 + */ +@Data +@ToString +@Builder +public class PayNotifyDataDTO { + + + /** + * HTTP 回调接口的 request body + */ + private String body; + + + /** + * HTTP 回调接口 content type 为 application/x-www-form-urlencoded 的所有参数 + */ + private Map params; +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayOrderNotifyRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayOrderNotifyRespDTO.java new file mode 100644 index 000000000..57e0ec445 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayOrderNotifyRespDTO.java @@ -0,0 +1,54 @@ +package pay.core.client.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * 支付通知 Response DTO + * + * @author 芋道源码 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class PayOrderNotifyRespDTO { + + /** + * 支付订单号(支付模块的) + */ + private String orderExtensionNo; + /** + * 支付渠道编号 + */ + private String channelOrderNo; + /** + * 支付渠道用户编号 + */ + private String channelUserId; + /** + * 支付成功时间 + */ + private LocalDateTime successTime; + + /** + * 通知的原始数据 + * + * 主要用于持久化,方便后续修复数据,或者排错 + */ + private String data; + + /** + * TODO @jason 结合其他的渠道定义成枚举, + * alipay + * TRADE_CLOSED,未付款交易超时关闭,或支付完成后全额退款。 + * TRADE_SUCCESS, 交易支付成功 + * TRADE_FINISHED 交易结束,不可退款。 + */ + private String tradeStatus; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayOrderUnifiedReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayOrderUnifiedReqDTO.java new file mode 100644 index 000000000..ad68a5edf --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayOrderUnifiedReqDTO.java @@ -0,0 +1,81 @@ +package pay.core.client.dto; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import org.hibernate.validator.constraints.URL; + +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.util.Map; + +/** + * 统一下单 Request DTO + * + * @author 芋道源码 + */ +@Data +public class PayOrderUnifiedReqDTO { + + /** + * 用户 IP + */ + @NotEmpty(message = "用户 IP 不能为空") + private String userIp; + + // ========== 商户相关字段 ========== + + /** + * 商户订单编号 + */ + @NotEmpty(message = "商户订单编号不能为空") + private String merchantOrderId; + /** + * 商品标题 + */ + @NotEmpty(message = "商品标题不能为空") + @Length(max = 32, message = "商品标题不能超过 32") + private String subject; + /** + * 商品描述信息 + */ + @NotEmpty(message = "商品描述信息不能为空") + @Length(max = 128, message = "商品描述信息长度不能超过128") + private String body; + /** + * 支付结果的 notify 回调地址 + */ + @NotEmpty(message = "支付结果的回调地址不能为空") + @URL(message = "支付结果的 notify 回调地址必须是 URL 格式") + private String notifyUrl; + /** + * 支付结果的 return 回调地址 + */ + @URL(message = "支付结果的 return 回调地址必须是 URL 格式") + private String returnUrl; + + // ========== 订单相关字段 ========== + + /** + * 支付金额,单位:分 + */ + @NotNull(message = "支付金额不能为空") + @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") + private Long amount; + + /** + * 支付过期时间 + */ + @NotNull(message = "支付过期时间不能为空") + private LocalDateTime expireTime; + + // ========== 拓展参数 ========== + /** + * 支付渠道的额外参数 + * + * 例如说,微信公众号需要传递 openid 参数 + */ + private Map channelExtras; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayRefundNotifyDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayRefundNotifyDTO.java new file mode 100644 index 000000000..be5c2231d --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayRefundNotifyDTO.java @@ -0,0 +1,63 @@ +package pay.core.client.dto; + +import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum; +import lombok.Builder; +import lombok.Data; +import lombok.ToString; + +import java.time.LocalDateTime; + +/** + * 从渠道返回数据中解析得到的支付退款通知的Notify DTO + * + * @author jason + */ +@Data +@ToString +@Builder +public class PayRefundNotifyDTO { + + /** + * 支付渠道编号 + */ + private String channelOrderNo; + + + /** + * 交易订单号,根据规则生成 + * 调用支付渠道时,使用该字段作为对接的订单号。 + * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no + * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no + * 这里对应 pay_extension 里面的 no + * 例如说,P202110132239124200055 + */ + private String tradeNo; + + /** + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no + * 退款请求号。 + * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。 + * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更, + * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。 + * 退款单请求号,根据规则生成 + * + * 例如说,RR202109181134287570000 + */ + private String reqNo; + + + /** + * 退款是否成功 + */ + private PayNotifyRefundStatusEnum status; + + + + /** + * 退款成功时间 + */ + private LocalDateTime refundSuccessTime; + + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayRefundUnifiedReqDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayRefundUnifiedReqDTO.java new file mode 100644 index 000000000..fc9a13a13 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayRefundUnifiedReqDTO.java @@ -0,0 +1,74 @@ +package pay.core.client.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.hibernate.validator.constraints.URL; + +import javax.validation.constraints.DecimalMin; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * 统一 退款 Request DTO + * + * @author jason + */ +@Accessors(chain = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class PayRefundUnifiedReqDTO { + + /** + * 用户 IP + */ + private String userIp; + + // TODO @jason:这个是否为非必传字段呀,只需要传递 payTradeNo 字段即可。尽可能精简 + /** + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 transaction_id + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 trade_no + * 渠道订单号 + */ + private String channelOrderNo; + + /** + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no + * 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空 + */ + private String payTradeNo; + + /** + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no + * 退款请求单号 同一退款请求单号多次请求只退一笔。 + * 使用 商户的退款单号。{PayRefundDO 字段 merchantRefundNo} + */ + @NotEmpty(message = "退款请求单号") + private String merchantRefundId; + + /** + * 退款原因 + */ + @NotEmpty(message = "退款原因不能为空") + private String reason; + + /** + * 退款金额,单位:分 + */ + @NotNull(message = "退款金额不能为空") + @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") + private Long amount; + + /** + * 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要 + */ + @URL(message = "支付结果的 notify 回调地址必须是 URL 格式") + private String notifyUrl; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayRefundUnifiedRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayRefundUnifiedRespDTO.java new file mode 100644 index 000000000..4adeb98ff --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/dto/PayRefundUnifiedRespDTO.java @@ -0,0 +1,24 @@ +package pay.core.client.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +/** + * 统一退款 Response DTO + * + * @author jason + */ +@Accessors(chain = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Data +public class PayRefundUnifiedRespDTO { + + /** + * 渠道退款单编号 + */ + private String channelRefundId; +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/AbstractPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/AbstractPayClient.java new file mode 100644 index 000000000..2d10cea18 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/AbstractPayClient.java @@ -0,0 +1,115 @@ +package pay.core.client.impl; + +import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO; +import lombok.extern.slf4j.Slf4j; + +import javax.validation.Validation; + +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; + +/** + * 支付客户端的抽象类,提供模板方法,减少子类的冗余代码 + * + * @author 芋道源码 + */ +@Slf4j +public abstract class AbstractPayClient implements PayClient { + + /** + * 渠道编号 + */ + private final Long channelId; + /** + * 渠道编码 + */ + private final String channelCode; + /** + * 错误码枚举类 + */ + protected AbstractPayCodeMapping codeMapping; + /** + * 支付配置 + */ + protected Config config; + + public AbstractPayClient(Long channelId, String channelCode, Config config, AbstractPayCodeMapping codeMapping) { + this.channelId = channelId; + this.channelCode = channelCode; + this.codeMapping = codeMapping; + this.config = config; + } + + /** + * 初始化 + */ + public final void init() { + doInit(); + log.info("[init][配置({}) 初始化完成]", config); + } + + /** + * 自定义初始化 + */ + protected abstract void doInit(); + + public final void refresh(Config config) { + // 判断是否更新 + if (config.equals(this.config)) { + return; + } + log.info("[refresh][配置({})发生变化,重新初始化]", config); + this.config = config; + // 初始化 + this.init(); + } + + protected Double calculateAmount(Long amount) { + return amount / 100.0; + } + + @Override + public Long getId() { + return channelId; + } + + @Override + public final PayCommonResult unifiedOrder(PayOrderUnifiedReqDTO reqDTO) { + Validation.buildDefaultValidatorFactory().getValidator().validate(reqDTO); + // 执行短信发送 + PayCommonResult result; + try { + result = doUnifiedOrder(reqDTO); + } catch (Throwable ex) { + // 打印异常日志 + log.error("[unifiedOrder][request({}) 发起支付失败]", toJsonString(reqDTO), ex); + // 封装返回 + return PayCommonResult.error(ex); + } + return result; + } + + protected abstract PayCommonResult doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) + throws Throwable; + + @Override + public PayCommonResult unifiedRefund(PayRefundUnifiedReqDTO reqDTO) { + PayCommonResult resp; + try { + resp = doUnifiedRefund(reqDTO); + } catch (Throwable ex) { + // 记录异常日志 + log.error("[unifiedRefund][request({}) 发起退款失败]", toJsonString(reqDTO), ex); + resp = PayCommonResult.error(ex); + } + return resp; + } + + protected abstract PayCommonResult doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/PayClientFactoryImpl.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/PayClientFactoryImpl.java new file mode 100644 index 000000000..2a0c0d074 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/PayClientFactoryImpl.java @@ -0,0 +1,81 @@ +package pay.core.client.impl; + +import cn.hutool.core.lang.Assert; +import cn.iocoder.yudao.framework.pay.core.client.PayClient; +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPcPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayQrPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayWapPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXLitePayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXNativePayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient; +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * 支付客户端的工厂实现类 + * + * @author 芋道源码 + */ +@Slf4j +public class PayClientFactoryImpl implements PayClientFactory { + + /** + * 支付客户端 Map + * key:渠道编号 + */ + private final ConcurrentMap> clients = new ConcurrentHashMap<>(); + + @Override + public PayClient getPayClient(Long channelId) { + cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient client = clients.get(channelId); + if (client == null) { + log.error("[getPayClient][渠道编号({}) 找不到客户端]", channelId); + } + return client; + } + + @Override + @SuppressWarnings("unchecked") + public void createOrUpdatePayClient(Long channelId, String channelCode, + Config config) { + cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient client = (cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient) clients.get(channelId); + if (client == null) { + client = this.createPayClient(channelId, channelCode, config); + client.init(); + clients.put(client.getId(), client); + } else { + client.refresh(config); + } + } + + @SuppressWarnings("unchecked") + private cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient createPayClient( + Long channelId, String channelCode, Config config) { + PayChannelEnum channelEnum = PayChannelEnum.getByCode(channelCode); + Assert.notNull(channelEnum, String.format("支付渠道(%s) 为空", channelEnum)); + // 创建客户端 + // TODO @芋艿 WX_LITE WX_APP 如果不添加在 项目启动的时候去初始化会报错无法启动。所以我手动加了两个,具体需要你来配 + switch (channelEnum) { + case WX_PUB: return (cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient) new WXPubPayClient(channelId, (WXPayClientConfig) config); + case WX_LITE: return (cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient) new WXLitePayClient(channelId, (WXPayClientConfig) config); //微信小程序请求支付 + case WX_APP: return (cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient) new WXPubPayClient(channelId, (WXPayClientConfig) config); + case WX_NATIVE: return (cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient) new WXNativePayClient(channelId, (WXPayClientConfig) config); + case ALIPAY_WAP: return (cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config); + case ALIPAY_QR: return (cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config); + case ALIPAY_APP: return (cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config); + case ALIPAY_PC: return (AbstractPayClient) new AlipayPcPayClient(channelId, (AlipayPayClientConfig) config); + } + // 创建失败,错误日志 + 抛出异常 + log.error("[createPayClient][配置({}) 找不到合适的客户端实现]", config); + throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", config)); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AbstractAlipayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AbstractAlipayClient.java new file mode 100644 index 000000000..a230bb841 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AbstractAlipayClient.java @@ -0,0 +1,157 @@ +package pay.core.client.impl.alipay; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping; +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.*; +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.enums.PayNotifyRefundStatusEnum; +import com.alipay.api.AlipayApiException; +import com.alipay.api.AlipayConfig; +import com.alipay.api.DefaultAlipayClient; +import com.alipay.api.domain.AlipayTradeRefundModel; +import com.alipay.api.internal.util.AlipaySignature; +import com.alipay.api.request.AlipayTradeRefundRequest; +import com.alipay.api.response.AlipayTradeRefundResponse; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; + +/** + * 支付宝抽象类, 实现支付宝统一的接口。如退款 + * + * @author jason + */ +@Slf4j +public abstract class AbstractAlipayClient extends AbstractPayClient { + + protected DefaultAlipayClient client; + + public AbstractAlipayClient(Long channelId, String channelCode, + AlipayPayClientConfig config, AbstractPayCodeMapping codeMapping) { + super(channelId, channelCode, config, codeMapping); + } + + @Override + @SneakyThrows + protected void doInit() { + AlipayConfig alipayConfig = new AlipayConfig(); + BeanUtil.copyProperties(config, alipayConfig, false); + this.client = new DefaultAlipayClient(alipayConfig); + } + + /** + * 从支付宝通知返回参数中解析 PayOrderNotifyRespDTO, 通知具体参数参考 + * //https://opendocs.alipay.com/open/203/105286 + * @param data 通知结果 + * @return 解析结果 PayOrderNotifyRespDTO + * @throws Exception 解析失败,抛出异常 + */ + @Override + public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception { + Map params = strToMap(data.getBody()); + + return PayOrderNotifyRespDTO.builder().orderExtensionNo(params.get("out_trade_no")) + .channelOrderNo(params.get("trade_no")).channelUserId(params.get("seller_id")) + .tradeStatus(params.get("trade_status")) + .successTime(LocalDateTimeUtil.parse(params.get("notify_time"), "yyyy-MM-dd HH:mm:ss")) + .data(data.getBody()).build(); + } + + @Override + public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) { + Map params = strToMap(notifyData.getBody()); + PayRefundNotifyDTO notifyDTO = PayRefundNotifyDTO.builder().channelOrderNo(params.get("trade_no")) + .tradeNo(params.get("out_trade_no")) + .reqNo(params.get("out_biz_no")) + .status(PayNotifyRefundStatusEnum.SUCCESS) + .refundSuccessTime(LocalDateTimeUtil.parse(params.get("gmt_refund"), "yyyy-MM-dd HH:mm:ss")) + .build(); + return notifyDTO; + } + + @Override + public boolean isRefundNotify(PayNotifyDataDTO notifyData) { + if (notifyData.getParams().containsKey("refund_fee")) { + return true; + } else { + return false; + } + } + + @Override + public boolean verifyNotifyData(PayNotifyDataDTO notifyData) { + boolean verifyResult = false; + try { + verifyResult = AlipaySignature.rsaCheckV1(notifyData.getParams(), config.getAlipayPublicKey(), StandardCharsets.UTF_8.name(), "RSA2"); + } catch (AlipayApiException e) { + log.error("[AlipayClient verifyNotifyData][(notify param is :{}) 验证失败]", toJsonString(notifyData.getParams()), e); + } + return verifyResult; + } + + /** + * 支付宝统一的退款接口 alipay.trade.refund + * @param reqDTO 退款请求 request DTO + * @return 退款请求 Response + */ + @Override + protected PayCommonResult doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { + AlipayTradeRefundModel model=new AlipayTradeRefundModel(); + model.setTradeNo(reqDTO.getChannelOrderNo()); + model.setOutTradeNo(reqDTO.getPayTradeNo()); + model.setOutRequestNo(reqDTO.getMerchantRefundId()); + model.setRefundAmount(calculateAmount(reqDTO.getAmount()).toString()); + model.setRefundReason(reqDTO.getReason()); + AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest(); + refundRequest.setBizModel(model); + try { + AlipayTradeRefundResponse response = client.execute(refundRequest); + log.info("[doUnifiedRefund][response({}) 发起退款 渠道返回", toJsonString(response)); + if (response.isSuccess()) { + //退款导致触发的异步通知是发送到支付接口中设置的notify_url + //支付宝不返回退款单号,设置为空 + PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO(); + respDTO.setChannelRefundId(""); + return PayCommonResult.build(response.getCode(), response.getMsg(), respDTO, codeMapping); + } + // 失败。需要抛出异常 + return PayCommonResult.build(response.getCode(), response.getMsg(), null, codeMapping); + } catch (AlipayApiException e) { + // TODO 记录异常日志 + log.error("[doUnifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), e); + return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping); + } + } + + + + /** + * 支付宝统一回调参数 str 转 map + * + * @param s 支付宝支付通知回调参数 + * @return map 支付宝集合 + */ + public static Map strToMap(String s) { + // TODO @zxy:这个可以使用 hutool 的 HttpUtil decodeParams 方法么? + Map stringStringMap = new HashMap<>(); + // 调整时间格式 + String s3 = s.replaceAll("%3A", ":"); + // 获取 map + String s4 = s3.replace("+", " "); + String[] split = s4.split("&"); + for (String s1 : split) { + String[] split1 = s1.split("="); + stringStringMap.put(split1[0], split1[1]); + } + return stringStringMap; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayPayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayPayClientConfig.java new file mode 100644 index 000000000..dd3bd5f00 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayPayClientConfig.java @@ -0,0 +1,117 @@ +package pay.core.client.impl.alipay; + +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; +import lombok.Data; + +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.util.Set; + +// TODO 芋艿:参数校验 + +/** + * 支付宝的 PayClientConfig 实现类 + * 属性主要来自 {@link com.alipay.api.AlipayConfig} 的必要属性 + * + * @author 芋道源码 + */ +@Data +public class AlipayPayClientConfig implements PayClientConfig { + + /** + * 网关地址 - 线上 + */ + public static final String SERVER_URL_PROD = "https://openapi.alipay.com/gateway.do"; + /** + * 网关地址 - 沙箱 + */ + public static final String SERVER_URL_SANDBOX = "https://openapi.alipaydev.com/gateway.do"; + + /** + * 公钥类型 - 公钥模式 + */ + public static final Integer MODE_PUBLIC_KEY = 1; + /** + * 公钥类型 - 证书模式 + */ + public static final Integer MODE_CERTIFICATE = 2; + + /** + * 签名算法类型 - RSA + */ + public static final String SIGN_TYPE_DEFAULT = "RSA2"; + + /** + * 网关地址 + * 1. {@link #SERVER_URL_PROD} + * 2. {@link #SERVER_URL_SANDBOX} + */ + @NotBlank(message = "网关地址不能为空", groups = {ModePublicKey.class, ModeCertificate.class}) + private String serverUrl; + + /** + * 开放平台上创建的应用的 ID + */ + @NotBlank(message = "开放平台上创建的应用的 ID不能为空", groups = {ModePublicKey.class, ModeCertificate.class}) + private String appId; + + /** + * 签名算法类型,推荐:RSA2 + *

+ * {@link #SIGN_TYPE_DEFAULT} + */ + @NotBlank(message = "签名算法类型不能为空", groups = {ModePublicKey.class, ModeCertificate.class}) + private String signType; + + /** + * 公钥类型 + * 1. {@link #MODE_PUBLIC_KEY} 情况,privateKey + alipayPublicKey + * 2. {@link #MODE_CERTIFICATE} 情况,appCertContent + alipayPublicCertContent + rootCertContent + */ + @NotNull(message = "公钥类型不能为空", groups = {ModePublicKey.class, ModeCertificate.class}) + private Integer mode; + + // ========== 公钥模式 ========== + /** + * 商户私钥 + */ + @NotBlank(message = "商户私钥不能为空", groups = {ModePublicKey.class}) + private String privateKey; + + /** + * 支付宝公钥字符串 + */ + @NotBlank(message = "支付宝公钥字符串不能为空", groups = {ModePublicKey.class}) + private String alipayPublicKey; + + // ========== 证书模式 ========== + /** + * 指定商户公钥应用证书内容字符串 + */ + @NotBlank(message = "指定商户公钥应用证书内容不能为空", groups = {ModeCertificate.class}) + private String appCertContent; + /** + * 指定支付宝公钥证书内容字符串 + */ + @NotBlank(message = "指定支付宝公钥证书内容不能为空", groups = {ModeCertificate.class}) + private String alipayPublicCertContent; + /** + * 指定根证书内容字符串 + */ + @NotBlank(message = "指定根证书内容字符串不能为空", groups = {ModeCertificate.class}) + private String rootCertContent; + + public interface ModePublicKey { + } + + public interface ModeCertificate { + } + + @Override + public Set> verifyParam(Validator validator) { + return validator.validate(this, + MODE_PUBLIC_KEY.equals(this.getMode()) ? ModePublicKey.class : ModeCertificate.class); + } +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayPayCodeMapping.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayPayCodeMapping.java new file mode 100644 index 000000000..558266f52 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayPayCodeMapping.java @@ -0,0 +1,28 @@ +package pay.core.client.impl.alipay; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; +import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping; + +import java.util.Objects; + +/** + * 支付宝的 PayCodeMapping 实现类 + * + * @author 芋道源码 + */ +public class AlipayPayCodeMapping extends AbstractPayCodeMapping { + + @Override + protected ErrorCode apply0(String apiCode, String apiMsg) { + if (Objects.equals(apiCode, "10000")) { + return GlobalErrorCodeConstants.SUCCESS; + } + // alipay wap api code 返回为null, 暂时定为-9999 + if (Objects.equals(apiCode, "-9999")) { + return GlobalErrorCodeConstants.SUCCESS; + } + return null; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayPcPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayPcPayClient.java new file mode 100644 index 000000000..15326fa35 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayPcPayClient.java @@ -0,0 +1,63 @@ +package pay.core.client.impl.alipay; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AbstractAlipayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayCodeMapping; +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; +import com.alibaba.fastjson.JSONObject; +import com.alipay.api.AlipayApiException; +import com.alipay.api.domain.AlipayTradePagePayModel; +import com.alipay.api.request.AlipayTradePagePayRequest; +import com.alipay.api.response.AlipayTradePagePayResponse; +import lombok.extern.slf4j.Slf4j; + + +/** + * 支付宝【PC网站支付】的 PayClient 实现类 + * 文档:https://opendocs.alipay.com/open/270/105898 + * + * @author XGD + */ +@Slf4j +public class AlipayPcPayClient extends AbstractAlipayClient { + + public AlipayPcPayClient(Long channelId, AlipayPayClientConfig config) { + super(channelId, PayChannelEnum.ALIPAY_PC.getCode(), config, new AlipayPayCodeMapping()); + } + + @Override + public PayCommonResult doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { + // 构建 AlipayTradePagePayModel 请求 + AlipayTradePagePayModel model = new AlipayTradePagePayModel(); + // 构建 AlipayTradePagePayRequest + AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); + request.setBizModel(model); + JSONObject bizContent = new JSONObject(); + // 参数说明可查看: https://opendocs.alipay.com/open/028r8t?scene=22 + bizContent.put("out_trade_no", reqDTO.getMerchantOrderId()); + bizContent.put("total_amount", calculateAmount(reqDTO.getAmount())); + bizContent.put("subject", reqDTO.getSubject()); + bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY"); + // PC扫码支付的方式:支持前置模式和跳转模式。4: 订单码-可定义宽度的嵌入式二维码 + bizContent.put("qr_pay_mode", "4"); + // 自定义二维码宽度 + bizContent.put("qrcode_width", "150"); + request.setBizContent(bizContent.toJSONString()); + request.setNotifyUrl(reqDTO.getNotifyUrl()); + request.setReturnUrl(""); + // 执行请求 + AlipayTradePagePayResponse response; + try { + response = client.pageExecute(request); + } catch (AlipayApiException e) { + log.error("[unifiedOrder][request({}) 发起支付失败]", JsonUtils.toJsonString(reqDTO), e); + return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping); + } + // 响应为表单格式,前端可嵌入响应的页面或关闭当前支付窗口 + return PayCommonResult.build(StrUtil.blankToDefault(response.getCode(),"10000") ,response.getMsg(), response, codeMapping); + } +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayQrPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayQrPayClient.java new file mode 100644 index 000000000..a9eec8d0c --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayQrPayClient.java @@ -0,0 +1,55 @@ +package pay.core.client.impl.alipay; + +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AbstractAlipayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayCodeMapping; +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; +import com.alipay.api.AlipayApiException; +import com.alipay.api.domain.AlipayTradePrecreateModel; +import com.alipay.api.request.AlipayTradePrecreateRequest; +import com.alipay.api.response.AlipayTradePrecreateResponse; +import lombok.extern.slf4j.Slf4j; + +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; + +/** + * 支付宝【扫码支付】的 PayClient 实现类 + * 文档:https://opendocs.alipay.com/apis/02890k + * + * @author 芋道源码 + */ +@Slf4j +public class AlipayQrPayClient extends AbstractAlipayClient { + + public AlipayQrPayClient(Long channelId, AlipayPayClientConfig config) { + super(channelId, PayChannelEnum.ALIPAY_QR.getCode(), config, new AlipayPayCodeMapping()); + } + + @Override + public PayCommonResult doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { + // 构建 AlipayTradePrecreateModel 请求 + AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); + model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setSubject(reqDTO.getSubject()); + model.setBody(reqDTO.getBody()); + model.setTotalAmount(calculateAmount(reqDTO.getAmount()).toString()); // 单位:元 + // TODO 芋艿:userIp + expireTime + // 构建 AlipayTradePrecreateRequest + AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); + request.setBizModel(model); + request.setNotifyUrl(reqDTO.getNotifyUrl()); + request.setReturnUrl(reqDTO.getReturnUrl()); + // 执行请求 + AlipayTradePrecreateResponse response; + try { + response = client.execute(request); + } catch (AlipayApiException e) { + log.error("[unifiedOrder][request({}) 发起支付失败]", toJsonString(reqDTO), e); + return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping); + } + // TODO 芋艿:sub Code 需要测试下各种失败的情况 + return PayCommonResult.build(response.getCode(), response.getMsg(), response, codeMapping); + } +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayWapPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayWapPayClient.java new file mode 100644 index 000000000..72af37eff --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/alipay/AlipayWapPayClient.java @@ -0,0 +1,78 @@ +package pay.core.client.impl.alipay; + +import cn.hutool.core.date.DateUtil; +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AbstractAlipayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayCodeMapping; +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; +import com.alipay.api.AlipayApiException; +import com.alipay.api.domain.AlipayTradeWapPayModel; +import com.alipay.api.request.AlipayTradeWapPayRequest; +import com.alipay.api.response.AlipayTradeWapPayResponse; +import lombok.extern.slf4j.Slf4j; + +import java.util.Objects; + +/** + * 支付宝【手机网站】的 PayClient 实现类 + * 文档:https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay + * + * @author 芋道源码 + */ +@Slf4j +public class AlipayWapPayClient extends AbstractAlipayClient { + + + public AlipayWapPayClient(Long channelId, AlipayPayClientConfig config) { + super(channelId, PayChannelEnum.ALIPAY_WAP.getCode(), config, new AlipayPayCodeMapping()); + } + + @Override + public PayCommonResult doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { + // 构建 AlipayTradeWapPayModel 请求 + AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); + model.setOutTradeNo(reqDTO.getMerchantOrderId()); + model.setSubject(reqDTO.getSubject()); + model.setBody(reqDTO.getBody()); + model.setTotalAmount(calculateAmount(reqDTO.getAmount()).toString()); + model.setProductCode("QUICK_WAP_PAY"); // TODO 芋艿:这里咋整 + //TODO 芋艿:这里咋整 jason @芋艿 可以去掉吧, + // TODO 芋艿 似乎这里不用传sellerId + // https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay + //model.setSellerId("2088102147948060"); + model.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(),"yyyy-MM-dd HH:mm:ss")); + // TODO 芋艿:userIp + // 构建 AlipayTradeWapPayRequest + AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest(); + request.setBizModel(model); + request.setNotifyUrl(reqDTO.getNotifyUrl()); + request.setReturnUrl(reqDTO.getReturnUrl()); + + // 执行请求 + AlipayTradeWapPayResponse response; + try { + response = client.pageExecute(request); + } catch (AlipayApiException e) { + return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping); + } + + // TODO 芋艿:sub Code + if(response.isSuccess() && Objects.isNull(response.getCode()) && Objects.nonNull(response.getBody())){ + //成功alipay wap 成功 code 为 null , body 为form 表单 + return PayCommonResult.build("-9999", "Success", response, codeMapping); + }else { + return PayCommonResult.build(response.getCode(), response.getMsg(), response, codeMapping); + } + } + + + + + + + + + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXCodeMapping.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXCodeMapping.java new file mode 100644 index 000000000..cacb29ec0 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXCodeMapping.java @@ -0,0 +1,56 @@ +package pay.core.client.impl.wx; + +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.exception.ErrorCode; +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; +import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping; + +import java.util.Objects; + +import static cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants.*; + +/** + * 微信支付 PayCodeMapping 实现类 + * + * @author 芋道源码 + */ +public class WXCodeMapping extends AbstractPayCodeMapping { + + /** + * 错误码 - 成功 + * 由于 weixin-java-pay 封装的 Result 未返回 code,所以自己定义下 + */ + public static final String CODE_SUCCESS = "SUCCESS"; + /** + * 错误提示 - 成功 + */ + public static final String MESSAGE_SUCCESS = "成功"; + + @Override + protected ErrorCode apply0(String apiCode, String apiMsg) { + if (Objects.equals(apiCode, CODE_SUCCESS)) { + return GlobalErrorCodeConstants.SUCCESS; + } + if (Objects.equals(apiCode, "FAIL")) { + if (Objects.equals(apiMsg, "AppID不存在,请检查后再试")) { + return PAY_CONFIG_APP_ID_ERROR; + } + if (Objects.equals(apiMsg, "签名错误,请检查后再试") + || Objects.equals(apiMsg, "签名错误")) { + return PAY_CONFIG_SIGN_ERROR; + } + } + if (Objects.equals(apiCode, "PARAM_ERROR")) { + if (Objects.equals(apiMsg, "无效的openid")) { + return PAY_OPENID_ERROR; + } + } + if (Objects.equals(apiCode, "CustomErrorCode")) { + if (StrUtil.contains(apiMsg, "必填字段")) { + return PAY_PARAM_MISSING; + } + } + return null; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXLitePayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXLitePayClient.java new file mode 100644 index 000000000..72b197214 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXLitePayClient.java @@ -0,0 +1,206 @@ +package pay.core.client.impl.wx; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.io.FileUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.*; +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; +import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result; +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import lombok.extern.slf4j.Slf4j; + +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS; +import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS; + + +/** + * 微信小程序下支付 + * + * @author zwy + */ +@Slf4j +public class WXLitePayClient extends AbstractPayClient { + + private WxPayService client; + + public WXLitePayClient(Long channelId, cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig config) { + super(channelId, PayChannelEnum.WX_LITE.getCode(), config, new WXCodeMapping()); + } + + @Override + protected void doInit() { + WxPayConfig payConfig = new WxPayConfig(); + BeanUtil.copyProperties(config, payConfig, "keyContent"); + payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式 +// if (StrUtil.isNotEmpty(config.getKeyContent())) { +// payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8)); +// } + if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) { + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决 + payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath()); + } + if (StrUtil.isNotEmpty(config.getPrivateCertContent())) { + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决 + payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath()); + } + // 真实客户端 + this.client = new WxPayServiceImpl(); + client.setConfig(payConfig); + } + + @Override + public PayCommonResult doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { + WxPayMpOrderResult response; + try { + switch (config.getApiVersion()) { + case cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig.API_VERSION_V2: + response = this.unifiedOrderV2(reqDTO); + break; + case cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig.API_VERSION_V3: + WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO); + // 将 V3 的结果,统一转换成 V2。返回的字段是一致的 + response = new WxPayMpOrderResult(); + BeanUtil.copyProperties(responseV3, response, true); + break; + default: + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); + } + } catch (WxPayException e) { + log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e); + return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"), + ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()), null, codeMapping); + } + return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, response, codeMapping); + } + + private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() + .outTradeNo(reqDTO.getMerchantOrderId()) + .body(reqDTO.getBody()) + .totalFee(reqDTO.getAmount().intValue()) // 单位分 + .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss")) // v2的时间格式 + .spbillCreateIp(reqDTO.getUserIp()) + .openid(getOpenid(reqDTO)) + .notifyUrl(reqDTO.getNotifyUrl()) + .build(); + // 执行请求 + return client.createOrder(request); + } + + private WxPayUnifiedOrderV3Result.JsapiResult unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); + request.setOutTradeNo(reqDTO.getMerchantOrderId()); + + request.setDescription(reqDTO.getBody()); + request.setAmount(new WxPayUnifiedOrderV3Request + .Amount() + .setTotal(reqDTO + .getAmount() + .intValue())); // 单位分 + request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")); // v3的时间格式 + request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO))); + request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); + request.setNotifyUrl(reqDTO.getNotifyUrl()); + // 执行请求 + return client.createOrderV3(TradeTypeEnum.JSAPI, request); + } + + private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) { + String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid"); + if (StrUtil.isEmpty(openid)) { + throw new IllegalArgumentException("支付请求的 openid 不能为空!"); + } + return openid; + } + + /** + * + * 微信支付回调 分 v2 和v3 的处理方式 + * + * @param data 通知结果 + * @return 支付回调对象 + * @throws WxPayException 微信异常类 + */ + @Override + public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException { + log.info("[parseOrderNotify][微信支付回调data数据:{}]", data.getBody()); + // 微信支付 v2 回调结果处理 + switch (config.getApiVersion()) { + case cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig.API_VERSION_V2: + return parseOrderNotifyV2(data); + case WXPayClientConfig.API_VERSION_V3: + return parseOrderNotifyV3(data); + default: + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); + } + } + + private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyDataDTO data) throws WxPayException { + WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null); + WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult(); + // 转换结果 + Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"), + "支付结果非 SUCCESS"); + + return PayOrderNotifyRespDTO + .builder() + .orderExtensionNo(result.getOutTradeNo()) + .channelOrderNo(result.getTradeState()) + .successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")) + .data(data.getBody()) + .build(); + } + + private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyDataDTO data) throws WxPayException { + WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody()); + Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS"); + // 转换结果 + return PayOrderNotifyRespDTO + .builder() + .orderExtensionNo(notifyResult.getOutTradeNo()) + .channelOrderNo(notifyResult.getTransactionId()) + .channelUserId(notifyResult.getOpenid()) + .successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss")) + .data(data.getBody()) + .build(); + + } + + @Override + public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) { + //TODO 需要实现 + throw new UnsupportedOperationException("需要实现"); + } + + + @Override + protected PayCommonResult doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { + //TODO 需要实现 + throw new UnsupportedOperationException(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXNativePayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXNativePayClient.java new file mode 100644 index 000000000..d7a2ce2c8 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXNativePayClient.java @@ -0,0 +1,189 @@ +package pay.core.client.impl.wx; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.io.FileUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.*; +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; +import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import lombok.extern.slf4j.Slf4j; + +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS; +import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS; + +/** + * 微信 App 支付 + * + * @author zwy + */ +@Slf4j +public class WXNativePayClient extends AbstractPayClient { + + private WxPayService client; + + public WXNativePayClient(Long channelId, cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig config) { + super(channelId, PayChannelEnum.WX_NATIVE.getCode(), config, new WXCodeMapping()); + } + + @Override + protected void doInit() { + WxPayConfig payConfig = new WxPayConfig(); + BeanUtil.copyProperties(config, payConfig, "keyContent"); + payConfig.setTradeType(WxPayConstants.TradeType.NATIVE); // 设置使用 native 支付方式 +// if (StrUtil.isNotEmpty(config.getKeyContent())) { +// payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8)); +// } + if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) { + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决 + payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath()); + } + if (StrUtil.isNotEmpty(config.getPrivateCertContent())) { + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决 + payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath()); + } + // 真实客户端 + this.client = new WxPayServiceImpl(); + client.setConfig(payConfig); + } + + @Override + public PayCommonResult doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { + // 这里原生的返回的是支付的 url 所以直接使用string接收 + // "invokeResponse": "weixin://wxpay/bizpayurl?pr=EGYAem7zz" + String responseV3; + try { + switch (config.getApiVersion()) { + case cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig.API_VERSION_V2: + responseV3 = unifiedOrderV2(reqDTO).getCodeUrl(); + break; + case cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig.API_VERSION_V3: + responseV3 = this.unifiedOrderV3(reqDTO); + break; + default: + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); + } + } catch (WxPayException e) { + log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e); + return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"), + ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()), null, codeMapping); + } + return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, responseV3, codeMapping); + } + + private WxPayNativeOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { + //前端 + String tradeType = reqDTO.getChannelExtras().get("trade_type"); + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest + .newBuilder() + .outTradeNo(reqDTO.getMerchantOrderId()) + .body(reqDTO.getBody()) + .totalFee(reqDTO.getAmount().intValue()) // 单位分 + .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")) + .spbillCreateIp(reqDTO.getUserIp()) + .notifyUrl(reqDTO.getNotifyUrl()) + .productId(tradeType) + .build(); + // 执行请求 + return client.createOrder(request); + } + + private String unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); + request.setOutTradeNo(reqDTO.getMerchantOrderId()); + request.setDescription(reqDTO.getBody()); + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分 + request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); + request.setNotifyUrl(reqDTO.getNotifyUrl()); + // 执行请求 + return client.createOrderV3(TradeTypeEnum.NATIVE, request); + } + + /** + * + * 微信支付回调 分v2 和v3 的处理方式 + * + * @param data 通知结果 + * @return 支付回调对象 + * @throws WxPayException 微信异常类 + */ + @Override + public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException { + log.info("微信支付回调data数据:{}", data.getBody()); + // 微信支付 v2 回调结果处理 + switch (config.getApiVersion()) { + case cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig.API_VERSION_V2: + return parseOrderNotifyV2(data); + case WXPayClientConfig.API_VERSION_V3: + return parseOrderNotifyV3(data); + default: + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); + } + } + + private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyDataDTO data) throws WxPayException { + WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null); + WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult(); + // 转换结果 + Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"), + "支付结果非 SUCCESS"); + return PayOrderNotifyRespDTO + .builder() + .orderExtensionNo(result.getOutTradeNo()) + .channelOrderNo(result.getTradeState()) + .successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")) + .data(data.getBody()) + .build(); + } + + private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyDataDTO data) throws WxPayException { + WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody()); + Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS"); + // 转换结果 + return PayOrderNotifyRespDTO + .builder() + .orderExtensionNo(notifyResult.getOutTradeNo()) + .channelOrderNo(notifyResult.getTransactionId()) + .channelUserId(notifyResult.getOpenid()) + .successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss")) + .data(data.getBody()) + .build(); + + } + + @Override + public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) { + // TODO 需要实现 + throw new UnsupportedOperationException("需要实现"); + } + + + @Override + protected PayCommonResult doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { + // TODO 需要实现 + throw new UnsupportedOperationException(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXPayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXPayClientConfig.java new file mode 100644 index 000000000..4004f0d9e --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXPayClientConfig.java @@ -0,0 +1,111 @@ +package pay.core.client.impl.wx; + +import cn.hutool.core.io.IoUtil; +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; +import lombok.Data; + +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import javax.validation.constraints.NotBlank; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.Set; + +/** + * 微信支付的 PayClientConfig 实现类 + * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性 + * + * @author 芋道源码 + */ +@Data +public class WXPayClientConfig implements PayClientConfig { + + /** + * API 版本 - V2 + * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1 + */ + public static final String API_VERSION_V2 = "v2"; + /** + * API 版本 - V3 + * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml + */ + public static final String API_VERSION_V3 = "v3"; + + /** + * 公众号或者小程序的 appid + */ + @NotBlank(message = "APPID 不能为空", groups = {V2.class, V3.class}) + private String appId; + /** + * 商户号 + */ + @NotBlank(message = "商户号 不能为空", groups = {V2.class, V3.class}) + private String mchId; + /** + * API 版本 + */ + @NotBlank(message = "API 版本 不能为空", groups = {V2.class, V3.class}) + private String apiVersion; + + // ========== V2 版本的参数 ========== + + /** + * 商户密钥 + */ + @NotBlank(message = "商户密钥 不能为空", groups = V2.class) + private String mchKey; + /** + * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. + * 对应的字符串 + * + * 注意,可通过 {@link #main(String[])} 读取 + */ + /// private String keyContent; + + // ========== V3 版本的参数 ========== + /** + * apiclient_key.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. + * 对应的字符串 + * 注意,可通过 {@link #main(String[])} 读取 + */ + @NotBlank(message = "apiclient_key 不能为空", groups = V3.class) + private String privateKeyContent; + /** + * apiclient_cert.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. + * 对应的字符串 + *

+ * 注意,可通过 {@link #main(String[])} 读取 + */ + @NotBlank(message = "apiclient_cert 不能为空", groups = V3.class) + private String privateCertContent; + /** + * apiV3 密钥值 + */ + @NotBlank(message = "apiV3 密钥值 不能为空", groups = V3.class) + private String apiV3Key; + + /** + * 分组校验 v2版本 + */ + public interface V2 { + } + + /** + * 分组校验 v3版本 + */ + public interface V3 { + } + + @Override + public Set> verifyParam(Validator validator) { + return validator.validate(this, this.getApiVersion().equals(API_VERSION_V2) ? V2.class : V3.class); + } + + public static void main(String[] args) throws FileNotFoundException { + String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"; + /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_key.pem"; + /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"; + System.out.println(IoUtil.readUtf8(new FileInputStream(path))); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXPubPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXPubPayClient.java new file mode 100644 index 000000000..0c070933e --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/client/impl/wx/WXPubPayClient.java @@ -0,0 +1,199 @@ +package pay.core.client.impl.wx; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.io.FileUtils; +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; +import cn.iocoder.yudao.framework.pay.core.client.dto.*; +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; +import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result; +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.config.WxPayConfig; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import com.github.binarywang.wxpay.service.WxPayService; +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; +import lombok.extern.slf4j.Slf4j; + +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; +import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS; +import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS; + +/** + * 微信支付(公众号)的 PayClient 实现类 + * + * @author 芋道源码 + */ +@Slf4j +public class WXPubPayClient extends AbstractPayClient { + + private WxPayService client; + + public WXPubPayClient(Long channelId, cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig config) { + super(channelId, PayChannelEnum.WX_PUB.getCode(), config, new WXCodeMapping()); + } + + @Override + protected void doInit() { + WxPayConfig payConfig = new WxPayConfig(); + BeanUtil.copyProperties(config, payConfig, "keyContent"); + payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式 +// if (StrUtil.isNotEmpty(config.getKeyContent())) { +// payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8)); +// } + if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) { + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决 + payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath()); + } + if (StrUtil.isNotEmpty(config.getPrivateCertContent())) { + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决 + payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath()); + } + // 真实客户端 + this.client = new WxPayServiceImpl(); + client.setConfig(payConfig); + } + + @Override + public PayCommonResult doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { + WxPayMpOrderResult response; + try { + switch (config.getApiVersion()) { + case cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig.API_VERSION_V2: + response = this.unifiedOrderV2(reqDTO); + break; + case cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig.API_VERSION_V3: + WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO); + // 将 V3 的结果,统一转换成 V2。返回的字段是一致的 + response = new WxPayMpOrderResult(); + BeanUtil.copyProperties(responseV3, response, true); + break; + default: + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); + } + } catch (WxPayException e) { + log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e); + return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"), + ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()),null, codeMapping); + } + return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, response, codeMapping); + } + + + private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() + .outTradeNo(reqDTO.getMerchantOrderId()) + .body(reqDTO.getBody()) + .totalFee(reqDTO.getAmount().intValue()) // 单位分 + .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")) + .spbillCreateIp(reqDTO.getUserIp()) + .openid(getOpenid(reqDTO)) + .notifyUrl(reqDTO.getNotifyUrl()) + .build(); + // 执行请求 + return client.createOrder(request); + } + + private WxPayUnifiedOrderV3Result.JsapiResult unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); + request.setOutTradeNo(reqDTO.getMerchantOrderId()); + request.setDescription(reqDTO.getBody()); + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分 + request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")); + request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO))); + request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); + request.setNotifyUrl(reqDTO.getNotifyUrl()); + // 执行请求 + return client.createOrderV3(TradeTypeEnum.JSAPI, request); + } + + private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) { + String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid"); + if (StrUtil.isEmpty(openid)) { + throw new IllegalArgumentException("支付请求的 openid 不能为空!"); + } + return openid; + } + + /** + * + * 微信支付回调 分v2 和v3 的处理方式 + * + * @param data 通知结果 + * @return 支付回调对象 + * @throws WxPayException 微信异常类 + */ + @Override + public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException { + log.info("[parseOrderNotify][微信支付回调data数据: {}]", data.getBody()); + // 微信支付 v2 回调结果处理 + switch (config.getApiVersion()) { + case cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig.API_VERSION_V2: + return parseOrderNotifyV2(data); + case WXPayClientConfig.API_VERSION_V3: + return parseOrderNotifyV3(data); + default: + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); + } + } + + private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyDataDTO data) throws WxPayException { + WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null); + WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult(); + // 转换结果 + Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"), + "支付结果非 SUCCESS"); + return PayOrderNotifyRespDTO + .builder() + .orderExtensionNo(result.getOutTradeNo()) + .channelOrderNo(result.getTradeState()) + .successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")) + .data(data.getBody()) + .build(); + } + + private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyDataDTO data) throws WxPayException { + WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody()); + Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS"); + // 转换结果 + return PayOrderNotifyRespDTO + .builder() + .orderExtensionNo(notifyResult.getOutTradeNo()) + .channelOrderNo(notifyResult.getTransactionId()) + .channelUserId(notifyResult.getOpenid()) + .successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss")) + .data(data.getBody()) + .build(); + + } + + @Override + public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) { + // TODO 需要实现 + throw new UnsupportedOperationException("需要实现"); + } + + @Override + protected PayCommonResult doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { + // TODO 需要实现 + throw new UnsupportedOperationException(); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayChannelEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayChannelEnum.java new file mode 100644 index 000000000..7f9681d17 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayChannelEnum.java @@ -0,0 +1,60 @@ +package pay.core.enums; + +import cn.hutool.core.util.ArrayUtil; +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 支付渠道的编码的枚举 + * 枚举值 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum PayChannelEnum { + + WX_PUB("wx_pub", "微信 JSAPI 支付", WXPayClientConfig.class), // 公众号网页 + WX_LITE("wx_lite", "微信小程序支付", WXPayClientConfig.class), + WX_APP("wx_app", "微信 App 支付", WXPayClientConfig.class), + WX_NATIVE("wx_native", "微信 native 支付", WXPayClientConfig.class), + + ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付", AlipayPayClientConfig.class), + ALIPAY_WAP("alipay_wap", "支付宝 Wap 网站支付", AlipayPayClientConfig.class), + ALIPAY_APP("alipay_app", "支付宝App 支付", AlipayPayClientConfig.class), + ALIPAY_QR("alipay_qr", "支付宝扫码支付", AlipayPayClientConfig.class); + + /** + * 编码 + *

+ * 参考 https://www.pingxx.com/api/支付渠道属性值.html + */ + private final String code; + /** + * 名字 + */ + private final String name; + + /** + * 配置类 + */ + private final Class configClass; + + /** + * 微信支付 + */ + public static final String WECHAT = "WECHAT"; + + /** + * 支付宝支付 + */ + public static final String ALIPAY = "ALIPAY"; + + public static PayChannelEnum getByCode(String code) { + return ArrayUtil.firstMatch(o -> o.getCode().equals(code), values()); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayChannelRefundRespEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayChannelRefundRespEnum.java new file mode 100644 index 000000000..d1a70445e --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayChannelRefundRespEnum.java @@ -0,0 +1,23 @@ +package pay.core.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 渠道统一的退款返回结果 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum PayChannelRefundRespEnum { + + SUCCESS(1, "退款成功"), + FAILURE(2, "退款失败"), + PROCESSING(3,"退款处理中"), + CLOSED(4, "退款关闭"); + + private final Integer status; + private final String name; + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayFrameworkErrorCodeConstants.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayFrameworkErrorCodeConstants.java new file mode 100644 index 000000000..f1401aac5 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayFrameworkErrorCodeConstants.java @@ -0,0 +1,27 @@ +package pay.core.enums; + +import cn.iocoder.yudao.framework.common.exception.ErrorCode; + +/** + * 支付框架的错误码枚举 + * + * 短信框架,使用 2-002-000-000 段 + * + * @author 芋道源码 + */ +public interface PayFrameworkErrorCodeConstants { + + ErrorCode PAY_UNKNOWN = new ErrorCode(2002000000, "未知错误,需要解析"); + + // ========== 配置相关相关 2002000100 ========== + ErrorCode PAY_CONFIG_APP_ID_ERROR = new ErrorCode(2002000100, "支付渠道 AppId 不正确"); + ErrorCode PAY_CONFIG_SIGN_ERROR = new ErrorCode(2002000100, "签名错误"); // 例如说,微信支付,配置错了 mchId 或者 mchKey + + + // ========== 其它相关 2002000900 开头 ========== + ErrorCode PAY_OPENID_ERROR = new ErrorCode(2002000900, "无效的 openid"); // 例如说,微信 openid 未授权过 + ErrorCode PAY_PARAM_MISSING = new ErrorCode(2002000901, "请求参数缺失"); // 例如说,支付少传了金额 + + ErrorCode EXCEPTION = new ErrorCode(2002000999, "调用异常"); + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayNotifyRefundStatusEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayNotifyRefundStatusEnum.java new file mode 100644 index 000000000..bd6f4b0e0 --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/pay/core/enums/PayNotifyRefundStatusEnum.java @@ -0,0 +1,20 @@ +package pay.core.enums; + +/** + * 退款通知, 统一的渠道退款状态 + * + * @author jason + */ +public enum PayNotifyRefundStatusEnum { + /** + * 支付宝 中 全额退款 trade_status=TRADE_CLOSED, 部分退款 trade_status=TRADE_SUCCESS + * 退款成功 + */ + SUCCESS, + + /** + * 支付宝退款通知没有这个状态 + * 退款异常 + */ + ABNORMAL; +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsReceiveRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsReceiveRespDTO.java index 8c841ebea..4def4a25c 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsReceiveRespDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsReceiveRespDTO.java @@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.sms.core.client.dto; import lombok.Data; -import java.util.Date; +import java.time.LocalDateTime; /** * 消息接收 Response DTO @@ -32,7 +32,7 @@ public class SmsReceiveRespDTO { /** * 用户接收时间 */ - private Date receiveTime; + private LocalDateTime receiveTime; /** * 短信 API 发送返回的序号 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java index ae93a88b6..59723c4c9 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java @@ -4,6 +4,8 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; @@ -11,8 +13,6 @@ import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient; import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import com.aliyuncs.AcsRequest; import com.aliyuncs.AcsResponse; import com.aliyuncs.DefaultAcsClient; @@ -28,7 +28,7 @@ import com.google.common.annotations.VisibleForTesting; import lombok.Data; import lombok.extern.slf4j.Slf4j; -import java.util.Date; +import java.time.LocalDateTime; import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -166,13 +166,13 @@ public class AliyunSmsClient extends AbstractSmsClient { */ @JsonProperty("send_time") @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private Date sendTime; + private LocalDateTime sendTime; /** * 状态报告时间 */ @JsonProperty("report_time") @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private Date reportTime; + private LocalDateTime reportTime; /** * 是否接收成功 */ diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java index 23bb01a02..743c1aed8 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java @@ -22,7 +22,7 @@ import com.tencentcloudapi.sms.v20210111.SmsClient; import com.tencentcloudapi.sms.v20210111.models.*; import lombok.Data; -import java.util.Date; +import java.time.LocalDateTime; import java.util.List; import java.util.function.Function; import java.util.function.Supplier; @@ -240,7 +240,7 @@ public class TencentSmsClient extends AbstractSmsClient { */ @JsonProperty("user_receive_time") @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private Date receiveTime; + private LocalDateTime receiveTime; /** * 国家(或地区)码 diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java index 12cb6635c..c5d1f88d0 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java @@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.URLUtil; import cn.iocoder.yudao.framework.common.core.KeyValue; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; @@ -12,7 +13,6 @@ import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient; import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; @@ -23,6 +23,7 @@ import com.yunpian.sdk.model.Template; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import java.time.LocalDateTime; import java.util.*; import java.util.function.Function; import java.util.function.Supplier; @@ -198,7 +199,7 @@ public class YunpianSmsClient extends AbstractSmsClient { */ @JsonProperty("user_receive_time") @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private Date userReceiveTime; + private LocalDateTime userReceiveTime; } diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/dataobject/BaseDO.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/dataobject/BaseDO.java index 7bb555450..19abaa6fe 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/dataobject/BaseDO.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/dataobject/BaseDO.java @@ -7,7 +7,7 @@ import lombok.Data; import org.apache.ibatis.type.JdbcType; import java.io.Serializable; -import java.util.Date; +import java.time.LocalDateTime; /** * 基础实体对象 @@ -21,12 +21,12 @@ public abstract class BaseDO implements Serializable { * 创建时间 */ @TableField(fill = FieldFill.INSERT) - private Date createTime; + private LocalDateTime createTime; /** * 最后更新时间 */ @TableField(fill = FieldFill.INSERT_UPDATE) - private Date updateTime; + private LocalDateTime updateTime; /** * 创建者,目前使用 SysUser 的 id 编号 * diff --git a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java index 65910b16e..94dada129 100644 --- a/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java +++ b/yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/handler/DefaultDBFieldHandler.java @@ -5,7 +5,7 @@ import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import org.apache.ibatis.reflection.MetaObject; -import java.util.Date; +import java.time.LocalDateTime; import java.util.Objects; /** @@ -22,7 +22,7 @@ public class DefaultDBFieldHandler implements MetaObjectHandler { if (Objects.nonNull(metaObject) && metaObject.getOriginalObject() instanceof BaseDO) { BaseDO baseDO = (BaseDO) metaObject.getOriginalObject(); - Date current = new Date(); + LocalDateTime current = LocalDateTime.now(); // 创建时间为空,则以当前时间为插入时间 if (Objects.isNull(baseDO.getCreateTime())) { baseDO.setCreateTime(current); @@ -49,7 +49,7 @@ public class DefaultDBFieldHandler implements MetaObjectHandler { // 更新时间为空,则以当前时间为更新时间 Object modifyTime = getFieldValByName("updateTime", metaObject); if (Objects.isNull(modifyTime)) { - setFieldValByName("updateTime", new Date(), metaObject); + setFieldValByName("updateTime", LocalDateTime.now(), metaObject); } // 当前登录用户不为空,更新人为空,则当前登录用户为更新人 diff --git a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java index d41c3951e..8664dae42 100644 --- a/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java +++ b/yudao-framework/yudao-spring-boot-starter-test/src/main/java/cn/iocoder/yudao/framework/test/core/util/RandomUtils.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.test.core.util; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.RandomUtil; import cn.hutool.core.util.StrUtil; @@ -8,6 +9,7 @@ import uk.co.jemos.podam.api.PodamFactory; import uk.co.jemos.podam.api.PodamFactoryImpl; import java.lang.reflect.Type; +import java.time.LocalDateTime; import java.util.Arrays; import java.util.Date; import java.util.List; @@ -76,6 +78,10 @@ public class RandomUtils { return RandomUtil.randomDay(0, RANDOM_DATE_MAX); } + public static LocalDateTime randomLocalDateTime() { + return LocalDateTimeUtil.of(randomDate()); + } + public static Short randomShort() { return (short) RandomUtil.randomInt(0, Short.MAX_VALUE); } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java index 6417189b0..3a2ab6bd4 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/filter/ApiAccessLogFilter.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.framework.apilog.core.filter; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.extra.servlet.ServletUtil; @@ -7,7 +8,6 @@ import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLog; import cn.iocoder.yudao.framework.apilog.core.service.ApiAccessLogFrameworkService; import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.monitor.TracerUtils; import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; import cn.iocoder.yudao.framework.web.config.WebProperties; @@ -20,7 +20,8 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.util.Date; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; import java.util.Map; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; @@ -47,7 +48,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter { protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 获得开始时间 - Date beginTim = new Date(); + LocalDateTime beginTime = LocalDateTime.now(); // 提前获得参数,避免 XssFilter 过滤处理 Map queryString = ServletUtil.getParamMap(request); String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtil.getBody(request) : null; @@ -56,15 +57,15 @@ public class ApiAccessLogFilter extends ApiRequestFilter { // 继续过滤器 filterChain.doFilter(request, response); // 正常执行,记录日志 - createApiAccessLog(request, beginTim, queryString, requestBody, null); + createApiAccessLog(request, beginTime, queryString, requestBody, null); } catch (Exception ex) { // 异常执行,记录日志 - createApiAccessLog(request, beginTim, queryString, requestBody, ex); + createApiAccessLog(request, beginTime, queryString, requestBody, ex); throw ex; } } - private void createApiAccessLog(HttpServletRequest request, Date beginTime, + private void createApiAccessLog(HttpServletRequest request, LocalDateTime beginTime, Map queryString, String requestBody, Exception ex) { ApiAccessLog accessLog = new ApiAccessLog(); try { @@ -75,7 +76,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter { } } - private void buildApiAccessLogDTO(ApiAccessLog accessLog, HttpServletRequest request, Date beginTime, + private void buildApiAccessLogDTO(ApiAccessLog accessLog, HttpServletRequest request, LocalDateTime beginTime, Map queryString, String requestBody, Exception ex) { // 处理用户信息 accessLog.setUserId(WebFrameworkUtils.getLoginUserId(request)); @@ -103,8 +104,8 @@ public class ApiAccessLogFilter extends ApiRequestFilter { accessLog.setUserIp(ServletUtil.getClientIP(request)); // 持续时间 accessLog.setBeginTime(beginTime); - accessLog.setEndTime(new Date()); - accessLog.setDuration((int) DateUtils.diff(accessLog.getEndTime(), accessLog.getBeginTime())); + accessLog.setEndTime(LocalDateTime.now()); + accessLog.setDuration((int) LocalDateTimeUtil.between(accessLog.getBeginTime(), accessLog.getEndTime(), ChronoUnit.SECONDS)); } } diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java index a92b06c26..7799c42f6 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiAccessLog.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.apilog.core.service; import lombok.Data; import javax.validation.constraints.NotNull; -import java.util.Date; +import java.time.LocalDateTime; /** * API 访问日志 @@ -61,12 +61,12 @@ public class ApiAccessLog { * 开始请求时间 */ @NotNull(message = "开始请求时间不能为空") - private Date beginTime; + private LocalDateTime beginTime; /** * 结束请求时间 */ @NotNull(message = "结束请求时间不能为空") - private Date endTime; + private LocalDateTime endTime; /** * 执行时长,单位:毫秒 */ diff --git a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java index 3f5d539cb..087dd5d08 100644 --- a/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java +++ b/yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/service/ApiErrorLog.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.apilog.core.service; import lombok.Data; import javax.validation.constraints.NotNull; -import java.util.Date; +import java.time.LocalDateTime; /** * API 错误日志 @@ -61,7 +61,7 @@ public class ApiErrorLog { * 异常时间 */ @NotNull(message = "异常时间不能为空") - private Date exceptionTime; + private LocalDateTime exceptionTime; /** * 异常名 */ 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 fdf77de96..a9e231285 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 @@ -30,7 +30,8 @@ import javax.servlet.http.HttpServletRequest; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.ValidationException; -import java.util.Date; +import java.time.LocalDateTime; +import java.time.LocalDateTime; import java.util.Map; import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.*; @@ -267,7 +268,7 @@ public class GlobalExceptionHandler { errorLog.setRequestMethod(request.getMethod()); errorLog.setUserAgent(ServletUtils.getUserAgent(request)); errorLog.setUserIp(ServletUtil.getClientIP(request)); - errorLog.setExceptionTime(new Date()); + errorLog.setExceptionTime(LocalDateTime.now()); } } diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLog.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLog.java index dd2bc24b0..52b46fc23 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLog.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLog.java @@ -5,8 +5,7 @@ import org.springframework.cloud.gateway.route.Route; import org.springframework.http.HttpStatus; import org.springframework.util.MultiValueMap; -import java.util.Date; -import java.util.Map; +import java.time.LocalDateTime; /** * 网关的访问日志 @@ -80,11 +79,11 @@ public class AccessLog { /** * 开始请求时间 */ - private Date startTime; + private LocalDateTime startTime; /** * 结束请求时间 */ - private Date endTime; + private LocalDateTime endTime; /** * 执行时长,单位:毫秒 */ diff --git a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java index f753ef5f1..4f9c34c53 100644 --- a/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java +++ b/yudao-gateway/src/main/java/cn/iocoder/yudao/gateway/filter/logging/AccessLogFilter.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.gateway.filter.logging; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.json.JSONUtil; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; @@ -38,12 +39,13 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.nio.charset.StandardCharsets; -import java.util.Date; +import java.time.LocalDateTime; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static cn.hutool.core.date.DatePattern.NORM_DATETIME_MS_FORMAT; +import static cn.hutool.core.date.DatePattern.NORM_DATETIME_MS_FORMATTER; /** * 网关的访问日志过滤器 @@ -89,8 +91,8 @@ public class AccessLogFilter implements GlobalFilter, Ordered { values.put("responseHeaders", gatewayLog.getResponseHeaders() != null ? JsonUtils.toJsonString(gatewayLog.getResponseHeaders().toSingleValueMap()) : null); values.put("httpStatus", gatewayLog.getHttpStatus()); - values.put("startTime", DateUtil.format(gatewayLog.getStartTime(), NORM_DATETIME_MS_FORMAT)); - values.put("endTime", DateUtil.format(gatewayLog.getEndTime(), NORM_DATETIME_MS_FORMAT)); + values.put("startTime", LocalDateTimeUtil.format(gatewayLog.getStartTime(), NORM_DATETIME_MS_FORMATTER)); + values.put("endTime", LocalDateTimeUtil.format(gatewayLog.getEndTime(), NORM_DATETIME_MS_FORMATTER)); values.put("duration", gatewayLog.getDuration() != null ? gatewayLog.getDuration() + " ms" : null); log.info("[writeAccessLog][网关日志:{}]", JsonUtils.toJsonPrettyString(values)); } @@ -112,7 +114,7 @@ public class AccessLogFilter implements GlobalFilter, Ordered { gatewayLog.setRequestUrl(request.getURI().getRawPath()); gatewayLog.setQueryParams(request.getQueryParams()); gatewayLog.setRequestHeaders(request.getHeaders()); - gatewayLog.setStartTime(new Date()); + gatewayLog.setStartTime(LocalDateTime.now()); gatewayLog.setUserIp(WebFrameworkUtils.getClientIP(exchange)); // 继续 filter 过滤 @@ -179,8 +181,9 @@ public class AccessLogFilter implements GlobalFilter, Ordered { if (body instanceof Flux) { DataBufferFactory bufferFactory = response.bufferFactory(); // 计算执行时间 - gatewayLog.setEndTime(new Date()); - gatewayLog.setDuration((int) DateUtils.diff(gatewayLog.getEndTime(), gatewayLog.getStartTime())); + gatewayLog.setEndTime(LocalDateTime.now()); + gatewayLog.setDuration((int) (LocalDateTimeUtil.between(gatewayLog.getStartTime(), + gatewayLog.getEndTime()).toMillis())); // 设置其它字段 gatewayLog.setUserId(SecurityFrameworkUtils.getLoginUserId(exchange)); gatewayLog.setUserType(SecurityFrameworkUtils.getLoginUserType(exchange)); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java index 76cf62046..e49f09879 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java @@ -7,7 +7,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import javax.validation.constraints.NotNull; -import java.util.Date; +import java.time.LocalDateTime; import java.util.List; @ApiModel("管理后台 - 动态表单 Response VO") @@ -28,6 +28,6 @@ public class BpmFormRespVO extends BpmFormBaseVO { private List fields; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java index 2bbe95192..2608607bf 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java @@ -9,7 +9,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - 用户组分页 Request VO") @Data @@ -25,6 +25,6 @@ public class BpmUserGroupPageReqVO extends PageParam { @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @ApiModelProperty(value = "创建时间") - private Date[] createTime; + private LocalDateTime[] createTime; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java index 55018d502..f240016d6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group; import lombok.*; + +import java.time.LocalDateTime; import java.util.*; import io.swagger.annotations.*; @@ -14,6 +16,6 @@ public class BpmUserGroupRespVO extends BpmUserGroupBaseVO { private Long id; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelPageItemRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelPageItemRespVO.java index 7442b5ab0..10d23892e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelPageItemRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelPageItemRespVO.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - 流程模型的分页的每一项 Response VO") @Data @@ -21,7 +21,7 @@ public class BpmModelPageItemRespVO extends BpmModelBaseVO { private String formName; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; /** * 最新部署的流程定义 @@ -39,7 +39,7 @@ public class BpmModelPageItemRespVO extends BpmModelBaseVO { private Integer version; @ApiModelProperty(value = "部署时间", required = true) - private Date deploymentTime; + private LocalDateTime deploymentTime; @ApiModelProperty(value = "中断状态", required = true, example = "1", notes = "参见 SuspensionState 枚举") private Integer suspensionState; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java index bce0f9100..571850ff0 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - 流程模型的创建 Request VO") @Data @@ -21,6 +21,6 @@ public class BpmModelRespVO extends BpmModelBaseVO { private String bpmnXml; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageItemRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageItemRespVO.java index 75990f08a..1952e95a7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageItemRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageItemRespVO.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - 流程定义的分页的每一项 Response VO") @Data @@ -18,6 +18,6 @@ public class BpmProcessDefinitionPageItemRespVO extends BpmProcessDefinitionResp private String formName; @ApiModelProperty(value = "部署时间", required = true) - private Date deploymentTime; + private LocalDateTime deploymentTime; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveBaseVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveBaseVO.java index cbc568a11..be34b428d 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveBaseVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveBaseVO.java @@ -1,11 +1,12 @@ package cn.iocoder.yudao.module.bpm.controller.admin.oa.vo; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; -import javax.validation.constraints.*; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; + import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; /** @@ -18,11 +19,11 @@ public class BpmOALeaveBaseVO { @ApiModelProperty(value = "请假的开始时间", required = true) @NotNull(message = "开始时间不能为空") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date startTime; + private LocalDateTime startTime; @ApiModelProperty(value = "请假的结束时间", required = true) @NotNull(message = "结束时间不能为空") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date endTime; + private LocalDateTime endTime; @ApiModelProperty(value = "请假类型", required = true, example = "1", notes = "参见 bpm_oa_type 枚举") private Integer type; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java index 2ebab63e9..5230e2722 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java @@ -15,7 +15,8 @@ public class BpmOALeaveCreateReqVO extends BpmOALeaveBaseVO { @AssertTrue(message = "结束时间,需要在开始时间之后") public boolean isEndTimeValid() { - return !getEndTime().before(getStartTime()); + return !getEndTime().isBefore(getStartTime()); } + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java index 908c48e06..42b3ec66c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java @@ -5,7 +5,7 @@ import io.swagger.annotations.*; import org.springframework.format.annotation.DateTimeFormat; import javax.validation.constraints.NotNull; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -24,7 +24,7 @@ public class BpmOALeaveRespVO extends BpmOALeaveBaseVO { @ApiModelProperty(value = "申请时间", required = true) @NotNull(message = "申请时间不能为空") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date createTime; + private LocalDateTime createTime; @ApiModelProperty(value = "流程id") private String processInstanceId; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java index ef9d11ac1..7f183f50c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java @@ -4,7 +4,7 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - 流程活动的 Response VO") @Data @@ -16,9 +16,9 @@ public class BpmActivityRespVO { private String type; @ApiModelProperty(value = "流程活动的开始时间", required = true) - private Date startTime; + private LocalDateTime startTime; @ApiModelProperty(value = "流程活动的结束时间", required = true) - private Date endTime; + private LocalDateTime endTime; @ApiModelProperty(value = "关联的流程任务的编号", example = "2048", notes = "关联的流程任务,只有 UserTask 等类型才有") private String taskId; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceMyPageReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceMyPageReqVO.java index db297e268..e52d45f44 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceMyPageReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceMyPageReqVO.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -35,6 +35,6 @@ public class BpmProcessInstanceMyPageReqVO extends PageParam { @ApiModelProperty(value = "创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date[] createTime; + private LocalDateTime[] createTime; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageItemRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageItemRespVO.java index 5b5d5797a..0d2534b53 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageItemRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageItemRespVO.java @@ -4,7 +4,7 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; -import java.util.Date; +import java.time.LocalDateTime; import java.util.List; @ApiModel("管理后台 - 流程实例的分页 Item Response VO") @@ -30,10 +30,10 @@ public class BpmProcessInstancePageItemRespVO { private Integer result; @ApiModelProperty(value = "提交时间", required = true) - private Date createTime; + private LocalDateTime createTime; @ApiModelProperty(value = "结束时间", required = true) - private Date endTime; + private LocalDateTime endTime; /** * 当前任务 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java index a1547b175..58374dae2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java @@ -4,7 +4,7 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; -import java.util.Date; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -28,10 +28,10 @@ public class BpmProcessInstanceRespVO { private Integer result; @ApiModelProperty(value = "提交时间", required = true) - private Date createTime; + private LocalDateTime createTime; @ApiModelProperty(value = "结束时间", required = true) - private Date endTime; + private LocalDateTime endTime; @ApiModelProperty(value = "提交的表单值", required = true) private Map formVariables; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDonePageItemRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDonePageItemRespVO.java index c5e2c23ae..a9bb93f9e 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDonePageItemRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDonePageItemRespVO.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - 流程任务的 Done 已完成的分页项 Response VO") @Data @@ -15,7 +15,7 @@ import java.util.Date; public class BpmTaskDonePageItemRespVO extends BpmTaskTodoPageItemRespVO { @ApiModelProperty(value = "结束时间", required = true) - private Date endTime; + private LocalDateTime endTime; @ApiModelProperty(value = "持续时间", required = true, example = "1000") private Long durationInMillis; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDonePageReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDonePageReqVO.java index 99145b434..a347d58fe 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDonePageReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskDonePageReqVO.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -23,10 +23,10 @@ public class BpmTaskDonePageReqVO extends PageParam { @ApiModelProperty(value = "开始的创建收间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date beginCreateTime; + private LocalDateTime beginCreateTime; @ApiModelProperty(value = "结束的创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date endCreateTime; + private LocalDateTime endCreateTime; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskTodoPageItemRespVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskTodoPageItemRespVO.java index e25e3aa49..5c2cbf26a 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskTodoPageItemRespVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskTodoPageItemRespVO.java @@ -4,7 +4,7 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - 流程任务的 Running 进行中的分页项 Response VO") @Data @@ -17,10 +17,10 @@ public class BpmTaskTodoPageItemRespVO { private String name; @ApiModelProperty(value = "接收时间", required = true) - private Date claimTime; + private LocalDateTime claimTime; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; @ApiModelProperty(value = "激活状态", required = true, example = "1", notes = "参见 SuspensionState 枚举") private Integer suspensionState; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskTodoPageReqVO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskTodoPageReqVO.java index 9f248e198..9d2ad18c2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskTodoPageReqVO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskTodoPageReqVO.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -23,10 +23,10 @@ public class BpmTaskTodoPageReqVO extends PageParam { @ApiModelProperty(value = "开始的创建收间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date beginCreateTime; + private LocalDateTime beginCreateTime; @ApiModelProperty(value = "结束的创建时间") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date endCreateTime; + private LocalDateTime endCreateTime; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmFormConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmFormConvert.java index 6ba757417..151b4c80b 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmFormConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmFormConvert.java @@ -1,11 +1,11 @@ package cn.iocoder.yudao.module.bpm.convert.definition; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormSimpleRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormUpdateReqVO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java index 4d1b2a003..9f79032f6 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmModelConvert.java @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.bpm.convert.definition; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.*; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO; @@ -44,7 +45,7 @@ public interface BpmModelConvert { default BpmModelPageItemRespVO convert(Model model, BpmFormDO form, Deployment deployment, ProcessDefinition processDefinition) { BpmModelPageItemRespVO modelRespVO = new BpmModelPageItemRespVO(); modelRespVO.setId(model.getId()); - modelRespVO.setCreateTime(model.getCreateTime()); + modelRespVO.setCreateTime(DateUtils.of(model.getCreateTime())); // 通用 copy copyTo(model, modelRespVO); // Form @@ -57,7 +58,7 @@ public interface BpmModelConvert { if (modelRespVO.getProcessDefinition() != null) { modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); - modelRespVO.getProcessDefinition().setDeploymentTime(deployment.getDeploymentTime()); + modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime())); } return modelRespVO; } @@ -65,7 +66,7 @@ public interface BpmModelConvert { default BpmModelRespVO convert(Model model) { BpmModelRespVO modelRespVO = new BpmModelRespVO(); modelRespVO.setId(model.getId()); - modelRespVO.setCreateTime(model.getCreateTime()); + modelRespVO.setCreateTime(DateUtils.of(model.getCreateTime())); // 通用 copy copyTo(model, modelRespVO); return modelRespVO; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java index 8f5bfcd26..ad57a55b4 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmProcessDefinitionConvert.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.bpm.convert.definition; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageItemRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; @@ -67,7 +68,7 @@ public interface BpmProcessDefinitionConvert { BpmProcessDefinitionPageItemRespVO respVO = convert(bean); respVO.setSuspensionState(bean.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); if (deployment != null) { - respVO.setDeploymentTime(deployment.getDeploymentTime()); + respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())); } if (form != null) { respVO.setFormName(form.getName()); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmUserGroupConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmUserGroupConvert.java index bbf00ba52..b5cd631f7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmUserGroupConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/definition/BpmUserGroupConvert.java @@ -1,17 +1,16 @@ package cn.iocoder.yudao.module.bpm.convert.definition; -import java.util.*; - +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupRespVO; import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.group.BpmUserGroupUpdateReqVO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmUserGroupDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; - import org.mapstruct.Mapper; import org.mapstruct.Named; import org.mapstruct.factory.Mappers; +import java.util.List; + /** * 用户组 Convert * diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/message/BpmMessageConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/message/BpmMessageConvert.java index 181e29618..99b7bebf2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/message/BpmMessageConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/message/BpmMessageConvert.java @@ -17,4 +17,5 @@ public interface BpmMessageConvert { @Mapping(source = "templateCode", target = "templateCode") @Mapping(source = "templateParams", target = "templateParams") SmsSendSingleToUserReqDTO convert(Long userId, String templateCode, Map templateParams); + } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/oa/BpmOALeaveConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/oa/BpmOALeaveConvert.java index f87531bfc..8c56e1dda 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/oa/BpmOALeaveConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/oa/BpmOALeaveConvert.java @@ -1,9 +1,9 @@ package cn.iocoder.yudao.module.bpm.convert.oa; +import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.oa.vo.BpmOALeaveRespVO; import cn.iocoder.yudao.module.bpm.dal.dataobject.oa.BpmOALeaveDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import org.mapstruct.Mapper; import org.mapstruct.factory.Mappers; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java index 91d405925..a4ba5cc85 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmTaskConvert.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.bpm.convert.task; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.BpmTaskDonePageItemRespVO; @@ -151,7 +152,7 @@ public interface BpmTaskConvert { BpmTaskExtDO taskExtDO = new BpmTaskExtDO().setTaskId(task.getId()) .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())).setName(task.getName()) .setProcessDefinitionId(task.getProcessDefinitionId()).setProcessInstanceId(task.getProcessInstanceId()); - taskExtDO.setCreateTime(task.getCreateTime()); + taskExtDO.setCreateTime(LocalDateTimeUtil.of(task.getCreateTime())); return taskExtDO; } diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java index 31381a556..92c7c154c 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.bpm.dal.dataobject.oa; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import lombok.*; + +import java.time.LocalDateTime; import java.util.*; import com.baomidou.mybatisplus.annotation.*; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; @@ -46,11 +48,11 @@ public class BpmOALeaveDO extends BaseDO { /** * 开始时间 */ - private Date startTime; + private LocalDateTime startTime; /** * 结束时间 */ - private Date endTime; + private LocalDateTime endTime; /** * 请假天数 */ diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmActivityDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmActivityDO.java index 183a04147..190fcf5ed 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmActivityDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmActivityDO.java @@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.*; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; @@ -80,14 +80,14 @@ public class BpmActivityDO { */ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private Date startTime; + private LocalDateTime startTime; /** * 任务结束时间 */ @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private Date endTime; + private LocalDateTime endTime; private Integer transactionOrder; diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceExtDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceExtDO.java index b37709962..293cc2dd7 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceExtDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmProcessInstanceExtDO.java @@ -11,7 +11,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; import java.util.Map; /** @@ -79,7 +79,7 @@ public class BpmProcessInstanceExtDO extends BaseDO { * * 冗余 HistoricProcessInstance 的 endTime 属性 */ - private Date endTime; + private LocalDateTime endTime; /** * 提交的表单值 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmTaskExtDO.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmTaskExtDO.java index 5c6b70fc3..a79fce412 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmTaskExtDO.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/dal/dataobject/task/BpmTaskExtDO.java @@ -8,7 +8,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; /** * Bpm 流程任务的拓展表 @@ -67,7 +67,7 @@ public class BpmTaskExtDO extends BaseDO { * * 冗余 HistoricTaskInstance 的 endTime 属性 */ - private Date endTime; + private LocalDateTime endTime; /** * 流程实例的编号 diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java index d0aadd95e..33d178901 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/oa/BpmOALeaveServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.bpm.service.oa; import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; @@ -46,7 +47,7 @@ public class BpmOALeaveServiceImpl implements BpmOALeaveService { @Transactional(rollbackFor = Exception.class) public Long createLeave(Long userId, BpmOALeaveCreateReqVO createReqVO) { // 插入 OA 请假单 - long day = DateUtil.betweenDay(createReqVO.getStartTime(), createReqVO.getEndTime(), false); + long day = LocalDateTimeUtil.between(createReqVO.getStartTime(), createReqVO.getEndTime()).toDays(); BpmOALeaveDO leave = BpmOALeaveConvert.INSTANCE.convert(createReqVO).setUserId(userId).setDay(day) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); leaveMapper.insert(leave); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index 076ecd8fc..a7468ac4f 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -1 +1 @@ -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. * * HistoricProcessInstance & ProcessInstance 的关系: * 1. * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List getProcessInstances(Set ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData(); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()).getCheckedData(); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List getHistoricProcessInstances(Set ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(new Date()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstance instance = runtimeService.startProcessInstanceById(definition.getId(), businessKey, variables); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables)); return instance.getId(); } } \ No newline at end of file +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.*; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionExtDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.task.BpmProcessInstanceExtDO; import cn.iocoder.yudao.module.bpm.dal.mysql.task.BpmProcessInstanceExtMapper; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceResultEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.bpm.core.event.BpmProcessInstanceResultEventPublisher; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.task.api.Task; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; import javax.validation.Valid; import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. * * HistoricProcessInstance & ProcessInstance 的关系: * 1. * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private BpmProcessInstanceExtMapper processInstanceExtMapper; @Resource @Lazy // 解决循环依赖 private BpmTaskService taskService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private HistoryService historyService; @Resource private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; @Resource private BpmProcessInstanceResultEventPublisher processInstanceResultEventPublisher; @Resource private BpmMessageService messageService; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List getProcessInstances(Set ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult getMyProcessInstancePage(Long userId, BpmProcessInstanceMyPageReqVO pageReqVO) { // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 PageResult pageResult = processInstanceExtMapper.selectPage(userId, pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return new PageResult<>(pageResult.getTotal()); } // 获得流程 Task Map List processInstanceIds = convertList(pageResult.getList(), BpmProcessInstanceExtDO::getProcessInstanceId); Map> taskMap = taskService.getTaskMapByProcessInstanceIds(processInstanceIds); // 转换返回 return BpmProcessInstanceConvert.INSTANCE.convertPage(pageResult, taskMap); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey()); } @Override public BpmProcessInstanceRespVO getProcessInstanceVO(String id) { // 获得流程实例 HistoricProcessInstance processInstance = getHistoricProcessInstance(id); if (processInstance == null) { return null; } BpmProcessInstanceExtDO processInstanceExt = processInstanceExtMapper.selectByProcessInstanceId(id); Assert.notNull(processInstanceExt, "流程实例拓展({}) 不存在", id); // 获得流程定义 ProcessDefinition processDefinition = processDefinitionService .getProcessDefinition(processInstance.getProcessDefinitionId()); Assert.notNull(processDefinition, "流程定义({}) 不存在", processInstance.getProcessDefinitionId()); BpmProcessDefinitionExtDO processDefinitionExt = processDefinitionService.getProcessDefinitionExt( processInstance.getProcessDefinitionId()); Assert.notNull(processDefinitionExt, "流程定义拓展({}) 不存在", id); String bpmnXml = processDefinitionService.getProcessDefinitionBpmnXML(processInstance.getProcessDefinitionId()); // 获得 User AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData(); DeptRespDTO dept = null; if (startUser != null) { dept = deptApi.getDept(startUser.getDeptId()).getCheckedData(); } // 拼接结果 return BpmProcessInstanceConvert.INSTANCE.convert2(processInstance, processInstanceExt, processDefinition, processDefinitionExt, bpmnXml, startUser, dept); } @Override public void cancelProcessInstance(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 通过删除流程实例,实现流程实例的取消, // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。通过历史表查询 deleteProcessInstance(cancelReqVO.getId(), BpmProcessInstanceDeleteReasonEnum.CANCEL_TASK.format(cancelReqVO.getReason())); } /** * 获得历史的流程实例 * * @param id 流程实例的编号 * @return 历史的流程实例 */ @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult(); } @Override public List getHistoricProcessInstances(Set ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public void createProcessInstanceExt(ProcessInstance instance) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition2(instance.getProcessDefinitionId()); // 插入 BpmProcessInstanceExtDO 对象 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getId()) .setProcessDefinitionId(definition.getId()) .setName(instance.getProcessDefinitionName()) .setStartUserId(Long.valueOf(instance.getStartUserId())) .setCategory(definition.getCategory()) .setStatus(BpmProcessInstanceStatusEnum.RUNNING.getStatus()) .setResult(BpmProcessInstanceResultEnum.PROCESS.getResult()); processInstanceExtMapper.insert(instanceExtDO); } @Override public void updateProcessInstanceExtCancel(FlowableCancelledEvent event) { // 判断是否为 Reject 不通过。如果是,则不进行更新. // 因为,updateProcessInstanceExtReject 方法,已经进行更新了 if (BpmProcessInstanceDeleteReasonEnum.isRejectReason((String)event.getCause())) { return; } // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(event.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Override public void updateProcessInstanceExtComplete(ProcessInstance instance) { // 需要主动查询,因为 instance 只有 id 属性 // 另外,此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); // 更新拓展表 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO() .setProcessInstanceId(instance.getProcessInstanceId()) .setEndTime(LocalDateTime.now()) // 由于 ProcessInstance 里没有办法拿到 endTime,所以这里设置 .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()); // 如果正常完全,说明审批通过 processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被通过的消息 messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.convert2ApprovedReq(instance)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } @Transactional(rollbackFor = Exception.class) public void updateProcessInstanceExtReject(String id, String reason) { // 需要主动查询,因为 instance 只有 id 属性 ProcessInstance processInstance = getProcessInstance(id); // 删除流程实例,以实现驳回任务时,取消整个审批流程 deleteProcessInstance(id, StrUtil.format(BpmProcessInstanceDeleteReasonEnum.REJECT_TASK.format(reason))); // 更新 status + result // 注意,不能和上面的逻辑更换位置。因为 deleteProcessInstance 会触发流程的取消,进而调用 updateProcessInstanceExtCancel 方法, // 设置 result 为 BpmProcessInstanceStatusEnum.CANCEL,显然和 result 不一定是一致的 BpmProcessInstanceExtDO instanceExtDO = new BpmProcessInstanceExtDO().setProcessInstanceId(id) .setStatus(BpmProcessInstanceStatusEnum.FINISH.getStatus()) .setResult(BpmProcessInstanceResultEnum.REJECT.getResult()); processInstanceExtMapper.updateByProcessInstanceId(instanceExtDO); // 发送流程被不通过的消息 messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.convert2RejectReq(processInstance, reason)); // 发送流程实例的状态事件 processInstanceResultEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.convert(this, processInstance, instanceExtDO.getResult())); } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map variables, String businessKey) { // 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 创建流程实例 ProcessInstance instance = runtimeService.startProcessInstanceById(definition.getId(), businessKey, variables); // 设置流程名字 runtimeService.setProcessInstanceName(instance.getId(), definition.getName()); // 补全流程实例的拓展表 processInstanceExtMapper.updateByProcessInstanceId(new BpmProcessInstanceExtDO().setProcessInstanceId(instance.getId()) .setFormVariables(variables)); return instance.getId(); } } \ No newline at end of file diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java index b2c12bd2b..28862b0d2 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.number.NumberUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*; @@ -32,6 +33,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager import javax.annotation.Resource; import javax.validation.Valid; +import java.time.LocalDateTime; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; @@ -74,10 +76,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskQuery.taskNameLike("%" + pageVO.getName() + "%"); } if (pageVO.getBeginCreateTime() != null) { - taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime()); + taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getBeginCreateTime())); } if (pageVO.getEndCreateTime() != null) { - taskQuery.taskCreatedBefore(pageVO.getEndCreateTime()); + taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getEndCreateTime())); } // 执行查询 List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); @@ -106,10 +108,10 @@ public class BpmTaskServiceImpl implements BpmTaskService { taskQuery.taskNameLike("%" + pageVO.getName() + "%"); } if (pageVO.getBeginCreateTime() != null) { - taskQuery.taskCreatedAfter(pageVO.getBeginCreateTime()); + taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getBeginCreateTime())); } if (pageVO.getEndCreateTime() != null) { - taskQuery.taskCreatedBefore(pageVO.getEndCreateTime()); + taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getEndCreateTime())); } // 执行查询 List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); @@ -205,7 +207,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 更新任务拓展表为不通过 taskExtMapper.updateByTaskId( new BpmTaskExtDO().setTaskId(task.getId()).setResult(BpmProcessInstanceResultEnum.REJECT.getResult()) - .setEndTime(new Date()).setReason(reqVO.getReason())); + .setEndTime(LocalDateTime.now()).setReason(reqVO.getReason())); } @Override @@ -249,7 +251,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { public void updateTaskExtComplete(Task task) { BpmTaskExtDO taskExtDO = BpmTaskConvert.INSTANCE.convert2TaskExt(task) .setResult(BpmProcessInstanceResultEnum.APPROVE.getResult()) // 不设置也问题不大,因为 Complete 一般是审核通过,已经设置 - .setEndTime(new Date()); + .setEndTime(LocalDateTime.now()); taskExtMapper.updateByTaskId(taskExtDO); } @@ -280,7 +282,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { // 更新任务 taskExtMapper.updateById(new BpmTaskExtDO().setId(taskExt.getId()).setResult(BpmProcessInstanceResultEnum.CANCEL.getResult()) - .setEndTime(new Date()).setReason(BpmProcessInstanceDeleteReasonEnum.translateReason(task.getDeleteReason()))); + .setEndTime(LocalDateTime.now()).setReason(BpmProcessInstanceDeleteReasonEnum.translateReason(task.getDeleteReason()))); } }); diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/application.yaml b/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/application.yaml index f629bed50..677656171 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/application.yaml +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/main/resources/application.yaml @@ -16,7 +16,7 @@ spring: # Jackson 配置项 jackson: serialization: - write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳 + write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 fail-on-empty-beans: false # 允许序列化无属性的 Bean diff --git a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java index 1053c95e1..6fb9d91a1 100644 --- a/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java +++ b/yudao-module-bpm/yudao-module-bpm-biz/src/test/java/cn/iocoder/yudao/module/bpm/service/definition/BpmUserGroupServiceTest.java @@ -17,8 +17,9 @@ import org.springframework.context.annotation.Import; import javax.annotation.Resource; -import java.util.Date; +import java.time.LocalDateTime; +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildLocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.buildTime; import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS; @@ -106,7 +107,7 @@ public class BpmUserGroupServiceTest extends BaseDbUnitTest { BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class, o -> { // 等会查询到 o.setName("芋道源码"); o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(DateUtils.buildTime(2021, 11, 11)); + o.setCreateTime(buildLocalDateTime(2021, 11, 11)); }); userGroupMapper.insert(dbUserGroup); // 测试 name 不匹配 @@ -114,12 +115,13 @@ public class BpmUserGroupServiceTest extends BaseDbUnitTest { // 测试 status 不匹配 userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); // 测试 createTime 不匹配 - userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setCreateTime(DateUtils.buildTime(2021, 12, 12)))); + userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setCreateTime(buildLocalDateTime(2021, 12, 12)))); // 准备参数 BpmUserGroupPageReqVO reqVO = new BpmUserGroupPageReqVO(); reqVO.setName("源码"); reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime((new Date[]{buildTime(2021, 11, 10),buildTime(2021, 11, 12)})); + reqVO.setCreateTime((new LocalDateTime[]{buildLocalDateTime(2021, 11, 10), + buildLocalDateTime(2021, 11, 12)})); // 调用 PageResult pageResult = userGroupService.getUserGroupPage(reqVO); diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiAccessLogCreateReqDTO.java b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiAccessLogCreateReqDTO.java index eac503b22..f4e8e6495 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiAccessLogCreateReqDTO.java +++ b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiAccessLogCreateReqDTO.java @@ -5,7 +5,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotNull; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("RPC 服务 - API 访问日志创建 Request DTO") @Data @@ -40,10 +40,10 @@ public class ApiAccessLogCreateReqDTO { @ApiModelProperty(value = "开始时间", required = true) @NotNull(message = "开始请求时间不能为空") - private Date beginTime; + private LocalDateTime beginTime; @ApiModelProperty(value = "结束时间", required = true) @NotNull(message = "结束请求时间不能为空") - private Date endTime; + private LocalDateTime endTime; @ApiModelProperty(value = "执行时长,单位:毫秒", required = true) @NotNull(message = "执行时长不能为空") private Integer duration; diff --git a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiErrorLogCreateReqDTO.java b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiErrorLogCreateReqDTO.java index 2da5046c2..06aab01b5 100644 --- a/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiErrorLogCreateReqDTO.java +++ b/yudao-module-infra/yudao-module-infra-api/src/main/java/cn/iocoder/yudao/module/infra/api/logger/dto/ApiErrorLogCreateReqDTO.java @@ -5,7 +5,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.NotNull; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("RPC 服务 - API 错误日志创建 Request DTO") @Data @@ -40,7 +40,7 @@ public class ApiErrorLogCreateReqDTO { @ApiModelProperty(value = "异常时间", required = true) @NotNull(message = "异常时间不能为空") - private Date exceptionTime; + private LocalDateTime exceptionTime; @ApiModelProperty(value = "异常名", required = true) @NotNull(message = "异常名不能为空") private String exceptionName; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java index 36dd3a05c..125b71801 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/column/CodegenColumnRespVO.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - 代码生成字段定义 Response VO") @Data @@ -18,6 +18,6 @@ public class CodegenColumnRespVO extends CodegenColumnBaseVO { private Long id; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java index adff119b0..bf940c995 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTablePageReqVO.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -26,6 +26,6 @@ public class CodegenTablePageReqVO extends PageParam { @ApiModelProperty(value = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date[] createTime; + private LocalDateTime[] createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java index bd1004bc7..7ef96b9a8 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/codegen/vo/table/CodegenTableRespVO.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - 代码生成表定义 Response VO") @Data @@ -21,9 +21,9 @@ public class CodegenTableRespVO extends CodegenTableBaseVO { private Integer dataSourceConfigId; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; @ApiModelProperty(value = "更新时间", required = true) - private Date updateTime; + private LocalDateTime updateTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java index af63aa0dd..134b2b2f1 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/db/vo/DataSourceConfigRespVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.infra.controller.admin.db.vo; import lombok.*; + +import java.time.LocalDateTime; import java.util.*; import io.swagger.annotations.*; @@ -14,6 +16,6 @@ public class DataSourceConfigRespVO extends DataSourceConfigBaseVO { private Integer id; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java index 3752b37c5..f8ea275f3 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigPageReqVO.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -26,6 +26,6 @@ public class FileConfigPageReqVO extends PageParam { @ApiModelProperty(value = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date[] createTime; + private LocalDateTime[] createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java index 74291925b..c2341e486 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/config/FileConfigRespVO.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import javax.validation.constraints.NotNull; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - 文件配置 Response VO") @Data @@ -31,6 +31,6 @@ public class FileConfigRespVO extends FileConfigBaseVO { private FileClientConfig config; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java index 0ae43bbe0..774ed4f39 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FilePageReqVO.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -26,6 +26,6 @@ public class FilePageReqVO extends PageParam { @ApiModelProperty(value = "创建时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date[] createTime; + private LocalDateTime[] createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java index 8e39a7520..ff51725bf 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileRespVO.java @@ -4,7 +4,7 @@ import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel(value = "管理后台 - 文件 Response VO", description = "不返回 content 字段,太大") @Data @@ -26,6 +26,6 @@ public class FileRespVO { private Integer size; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogBaseVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogBaseVO.java index 619dea65a..077f26133 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogBaseVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogBaseVO.java @@ -5,7 +5,7 @@ import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import javax.validation.constraints.NotNull; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -54,12 +54,12 @@ public class ApiAccessLogBaseVO { @ApiModelProperty(value = "开始请求时间", required = true) @NotNull(message = "开始请求时间不能为空") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date beginTime; + private LocalDateTime beginTime; @ApiModelProperty(value = "结束请求时间", required = true) @NotNull(message = "结束请求时间不能为空") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date endTime; + private LocalDateTime endTime; @ApiModelProperty(value = "执行时长", required = true, example = "100") @NotNull(message = "执行时长不能为空") diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogExcelVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogExcelVO.java index 5c1995c6a..21447743d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogExcelVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogExcelVO.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.system.enums.DictTypeConstants; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; -import java.util.Date; +import java.time.LocalDateTime; /** * API 访问日志 Excel VO @@ -48,10 +48,10 @@ public class ApiAccessLogExcelVO { private String userAgent; @ExcelProperty("开始请求时间") - private Date beginTime; + private LocalDateTime beginTime; @ExcelProperty("结束请求时间") - private Date endTime; + private LocalDateTime endTime; @ExcelProperty("执行时长") private Integer duration; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogExportReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogExportReqVO.java index dc2ef8714..406446d98 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogExportReqVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogExportReqVO.java @@ -5,7 +5,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -27,7 +27,7 @@ public class ApiAccessLogExportReqVO { @ApiModelProperty(value = "开始时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date[] beginTime; + private LocalDateTime[] beginTime; @ApiModelProperty(value = "执行时长", example = "100", notes = "大于等于,单位:毫秒") private Integer duration; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java index 97ff8e949..a141a758b 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogPageReqVO.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -32,7 +32,7 @@ public class ApiAccessLogPageReqVO extends PageParam { @ApiModelProperty(value = "开始时间", example = "[2022-07-01 00:00:00, 2022-07-01 23:59:59]") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date[] beginTime; + private LocalDateTime[] beginTime; @ApiModelProperty(value = "执行时长", example = "100", notes = "大于等于,单位:毫秒") private Integer duration; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java index 0458affb0..2a6125d38 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apiaccesslog/ApiAccessLogRespVO.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - API 访问日志 Response VO") @Data @@ -18,6 +18,6 @@ public class ApiAccessLogRespVO extends ApiAccessLogBaseVO { private Long id; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogBaseVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogBaseVO.java index 08fbdace8..ad0b44368 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogBaseVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogBaseVO.java @@ -5,7 +5,7 @@ import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; import javax.validation.constraints.NotNull; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -55,7 +55,7 @@ public class ApiErrorLogBaseVO { @ApiModelProperty(value = "异常发生时间", required = true) @NotNull(message = "异常发生时间不能为空") @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private Date exceptionTime; + private LocalDateTime exceptionTime; @ApiModelProperty(value = "异常名", required = true) @NotNull(message = "异常名不能为空") diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogExcelVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogExcelVO.java index 7e1e2ef0c..08f10894f 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogExcelVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogExcelVO.java @@ -6,7 +6,7 @@ import cn.iocoder.yudao.module.infra.enums.DictTypeConstants; import com.alibaba.excel.annotation.ExcelProperty; import lombok.Data; -import java.util.Date; +import java.time.LocalDateTime; /** * API 错误日志 Excel VO @@ -48,7 +48,7 @@ public class ApiErrorLogExcelVO { private String userAgent; @ExcelProperty("异常发生时间") - private Date exceptionTime; + private LocalDateTime exceptionTime; @ExcelProperty("异常名") private String exceptionName; @@ -75,14 +75,14 @@ public class ApiErrorLogExcelVO { private Integer exceptionLineNumber; @ExcelProperty("创建时间") - private Date createTime; + private LocalDateTime createTime; @ExcelProperty(value = "处理状态", converter = DictConvert.class) @DictFormat(DictTypeConstants.API_ERROR_LOG_PROCESS_STATUS) private Integer processStatus; @ExcelProperty("处理时间") - private Date processTime; + private LocalDateTime processTime; @ExcelProperty("处理用户编号") private Integer processUserId; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogExportReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogExportReqVO.java index d8e121e0b..e2c241138 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogExportReqVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogExportReqVO.java @@ -5,7 +5,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -27,7 +27,7 @@ public class ApiErrorLogExportReqVO { @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @ApiModelProperty(value = "异常发生时间") - private Date[] exceptionTime; + private LocalDateTime[] exceptionTime; @ApiModelProperty(value = "处理状态", example = "0") private Integer processStatus; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java index fda23ba14..56496325d 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogPageReqVO.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -32,7 +32,7 @@ public class ApiErrorLogPageReqVO extends PageParam { @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @ApiModelProperty(value = "异常发生时间") - private Date[] exceptionTime; + private LocalDateTime[] exceptionTime; @ApiModelProperty(value = "处理状态", example = "0") private Integer processStatus; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java index 0f0c2724d..c108b548c 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/logger/vo/apierrorlog/ApiErrorLogRespVO.java @@ -6,7 +6,7 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import java.util.Date; +import java.time.LocalDateTime; @ApiModel("管理后台 - API 错误日志 Response VO") @Data @@ -18,10 +18,10 @@ public class ApiErrorLogRespVO extends ApiErrorLogBaseVO { private Integer id; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; @ApiModelProperty(value = "处理时间", required = true) - private Date processTime; + private LocalDateTime processTime; @ApiModelProperty(value = "处理用户编号", example = "233") private Integer processUserId; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExcelVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExcelVO.java index e87d89586..d255c86d5 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExcelVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExcelVO.java @@ -1,6 +1,8 @@ package cn.iocoder.yudao.module.infra.controller.admin.test.vo; import lombok.*; + +import java.time.LocalDateTime; import java.util.*; import io.swagger.annotations.*; @@ -33,6 +35,6 @@ public class TestDemoExcelVO { private String remark; @ExcelProperty("创建时间") - private Date createTime; + private LocalDateTime createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExportReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExportReqVO.java index 8836919a6..c685bffa7 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExportReqVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoExportReqVO.java @@ -5,7 +5,7 @@ import io.swagger.annotations.ApiModelProperty; import lombok.Data; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -30,6 +30,6 @@ public class TestDemoExportReqVO { @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @ApiModelProperty(value = "创建时间") - private Date[] createTime; + private LocalDateTime[] createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoPageReqVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoPageReqVO.java index 0ee9e060c..be68e57a6 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoPageReqVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoPageReqVO.java @@ -8,7 +8,7 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.springframework.format.annotation.DateTimeFormat; -import java.util.Date; +import java.time.LocalDateTime; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @@ -35,6 +35,6 @@ public class TestDemoPageReqVO extends PageParam { @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @ApiModelProperty(value = "创建时间") - private Date[] createTime; + private LocalDateTime[] createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoRespVO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoRespVO.java index 56d6b0a11..93f5aecca 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoRespVO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/test/vo/TestDemoRespVO.java @@ -1,8 +1,12 @@ package cn.iocoder.yudao.module.infra.controller.admin.test.vo; -import lombok.*; -import java.util.*; -import io.swagger.annotations.*; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +import java.time.LocalDateTime; @ApiModel("管理后台 - 字典类型 Response VO") @Data @@ -14,6 +18,6 @@ public class TestDemoRespVO extends TestDemoBaseVO { private Long id; @ApiModelProperty(value = "创建时间", required = true) - private Date createTime; + private LocalDateTime createTime; } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java index 03a433f96..ab4292bf9 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiAccessLogDO.java @@ -8,7 +8,7 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; -import java.util.Date; +import java.time.LocalDateTime; /** * API 访问日志 @@ -84,11 +84,11 @@ public class ApiAccessLogDO extends BaseDO { /** * 开始请求时间 */ - private Date beginTime; + private LocalDateTime beginTime; /** * 结束请求时间 */ - private Date endTime; + private LocalDateTime endTime; /** * 执行时长,单位:毫秒 */ diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java index 524e8a2eb..7dc040981 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/dataobject/logger/ApiErrorLogDO.java @@ -8,7 +8,7 @@ import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableName; import lombok.*; -import java.util.Date; +import java.time.LocalDateTime; /** * API 异常数据 @@ -84,7 +84,7 @@ public class ApiErrorLogDO extends BaseDO { /** * 异常发生时间 */ - private Date exceptionTime; + private LocalDateTime exceptionTime; /** * 异常名 * @@ -145,7 +145,7 @@ public class ApiErrorLogDO extends BaseDO { /** * 处理时间 */ - private Date processTime; + private LocalDateTime processTime; /** * 处理用户编号 * diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java index 69a1e1cd7..e770dcafd 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileConfigMapper.java @@ -8,7 +8,7 @@ import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileConfigDO; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Select; -import java.util.Date; +import java.time.LocalDateTime; /** * 文件配置 Mapper @@ -27,6 +27,6 @@ public interface FileConfigMapper extends BaseMapperX { } @Select("SELECT COUNT(*) FROM infra_file_config WHERE update_time > #{maxUpdateTime}") - Long selectCountByUpdateTimeGt(Date maxUpdateTime); + Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime); } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java index 9eb0a3a93..52014d514 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/db/DatabaseTableServiceImpl.java @@ -53,7 +53,7 @@ public class DatabaseTableServiceImpl implements DatabaseTableService { if (StrUtil.isNotEmpty(name)) { strategyConfig.addInclude(name); } - GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.ONLY_DATE).build(); // 只使用 Date 类型,不使用 LocalDate + GlobalConfig globalConfig = new GlobalConfig.Builder().dateType(DateType.ONLY_DATE).build(); // 只使用 LocalDateTime 类型,不使用 LocalDate ConfigBuilder builder = new ConfigBuilder(null, dataSourceConfig, strategyConfig.build(), null, globalConfig, null); // 按照名字排序 diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java index ea82c61b3..129889ef0 100755 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java @@ -32,7 +32,7 @@ import javax.annotation.PostConstruct; import javax.annotation.Resource; import javax.validation.Validator; import java.util.Collection; -import java.util.Date; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -60,7 +60,7 @@ public class FileConfigServiceImpl implements FileConfigService { * 缓存菜单的最大更新时间,用于后续的增量轮询,判断是否有更新 */ @Getter - private volatile Date maxUpdateTime; + private volatile LocalDateTime maxUpdateTime; @Resource private FileClientFactory fileClientFactory; @@ -118,7 +118,7 @@ public class FileConfigServiceImpl implements FileConfigService { * @param maxUpdateTime 当前文件配置的最大更新时间 * @return 文件配置列表 */ - private List loadFileConfigIfUpdate(Date maxUpdateTime) { + private List loadFileConfigIfUpdate(LocalDateTime maxUpdateTime) { // 第一步,判断是否要更新。 if (maxUpdateTime == null) { // 如果更新时间为空,说明 DB 一定有新数据 log.info("[loadFileConfigIfUpdate][首次加载全量文件配置]"); diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java index 7e0cc3d85..6d79f8754 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/java/cn/iocoder/yudao/module/infra/service/logger/ApiErrorLogServiceImpl.java @@ -16,7 +16,7 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import javax.annotation.Resource; -import java.util.Date; +import java.time.LocalDateTime; import java.util.List; /** @@ -59,7 +59,7 @@ public class ApiErrorLogServiceImpl implements ApiErrorLogService { } // 标记处理 apiErrorLogMapper.updateById(ApiErrorLogDO.builder().id(id).processStatus(processStatus) - .processUserId(processUserId).processTime(new Date()).build()); + .processUserId(processUserId).processTime(LocalDateTime.now()).build()); } } diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application.yaml b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application.yaml index b23c5710b..37977e898 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application.yaml +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/application.yaml @@ -16,7 +16,7 @@ spring: # Jackson 配置项 jackson: serialization: - write-dates-as-timestamps: true # 设置 Date 的格式,使用时间戳 + write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 fail-on-empty-beans: false # 允许序列化无属性的 Bean diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/_column.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/_column.vm index 2e2222509..69477d433 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/_column.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/_column.vm @@ -7,7 +7,7 @@ @NotNull(message = "${column.columnComment}不能为空") #end #end -#if (${column.javaType} == "Date")## 时间类型 +#if (${column.javaType} == "LocalDateTime")## 时间类型 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) #end private ${column.javaType} ${column.javaField}; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/baseVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/baseVO.vm index 8b9fbaac2..a0ad48d60 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/baseVO.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/baseVO.vm @@ -5,7 +5,9 @@ import java.util.*; #foreach ($column in $columns) #if (${column.javaType} == "BigDecimal") import java.math.BigDecimal; -#break +#end +#if (${column.javaType} == "LocalDateTime") +import java.time.LocalDateTime; #end #end import io.swagger.annotations.*; @@ -13,7 +15,7 @@ import javax.validation.constraints.*; ## 处理 Date 字段的引入 #foreach ($column in $columns) #if (${column.createOperation} && ${column.updateOperation} && ${column.listOperationResult} - && ${column.javaType} == "Date")## 时间类型 + && ${column.javaType} == "LocalDateTime")## 时间类型 import org.springframework.format.annotation.DateTimeFormat; import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/createReqVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/createReqVO.vm index ca13efac5..dc6cee6b6 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/createReqVO.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/createReqVO.vm @@ -7,7 +7,7 @@ import javax.validation.constraints.*; ## 处理 Date 字段的引入 #foreach ($column in $columns) #if (${column.createOperation} && (!${column.updateOperation} || !${column.listOperationResult}) - && ${column.javaType} == "Date")## 时间类型 + && ${column.javaType} == "LocalDateTime")## 时间类型 import org.springframework.format.annotation.DateTimeFormat; import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/excelVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/excelVO.vm index 6b3ea2467..8bfd92b69 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/excelVO.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/excelVO.vm @@ -5,7 +5,9 @@ import java.util.*; #foreach ($column in $columns) #if (${column.javaType} == "BigDecimal") import java.math.BigDecimal; -#break +#end +#if (${column.javaType} == "LocalDateTime") +import java.time.LocalDateTime; #end #end import io.swagger.annotations.*; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/exportReqVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/exportReqVO.vm index afd583c28..cd68dd82e 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/exportReqVO.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/exportReqVO.vm @@ -6,7 +6,8 @@ import io.swagger.annotations.*; import ${PageParamClassName}; ## 处理 Date 字段的引入 #foreach ($column in $columns) -#if (${column.listOperation} && ${column.javaType} == "Date")## 时间类型 +#if (${column.listOperation} && ${column.javaType} == "LocalDateTime")## 时间类型 +import java.time.LocalDateTime; import org.springframework.format.annotation.DateTimeFormat; import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/pageReqVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/pageReqVO.vm index df2be7036..f2664390a 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/pageReqVO.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/pageReqVO.vm @@ -6,8 +6,9 @@ import io.swagger.annotations.*; import ${PageParamClassName}; ## 处理 Date 字段的引入 #foreach ($column in $columns) -#if (${column.listOperation} && ${column.javaType} == "Date")## 时间类型 +#if (${column.listOperation} && ${column.javaType} == "LocalDateTime")## 时间类型 import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; #break diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/updateReqVO.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/updateReqVO.vm index c6057d55a..636991508 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/updateReqVO.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/controller/vo/updateReqVO.vm @@ -7,9 +7,9 @@ import javax.validation.constraints.*; ## 处理 Date 字段的引入 #foreach ($column in $columns) #if (${column.updateOperation} && (!${column.createOperation} || !${column.listOperationResult}) - && ${column.javaType} == "Date")## 时间类型 + && ${column.javaType} == "LocalDateTime")## 时间类型 import org.springframework.format.annotation.DateTimeFormat; - +import java.time.LocalDateTime; import static ${DateUtilsClassName}.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; #break #end diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do.vm index 4abae1591..d551d4b30 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/dal/do.vm @@ -5,7 +5,9 @@ import java.util.*; #foreach ($column in $columns) #if (${column.javaType} == "BigDecimal") import java.math.BigDecimal; -#break +#end +#if (${column.javaType} == "LocalDateTime") +import java.time.LocalDateTime; #end #end import com.baomidou.mybatisplus.annotation.*; diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm index 1b5c60a3f..e97fbb275 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/java/test/serviceTest.vm @@ -16,6 +16,7 @@ import ${PageResultClassName}; import javax.annotation.Resource; import org.springframework.context.annotation.Import; import java.util.*; +import java.time.LocalDateTime; import static cn.hutool.core.util.RandomUtil.*; import static ${basePackage}.module.${table.moduleName}.enums.ErrorCodeConstants.*; @@ -51,7 +52,7 @@ import static org.mockito.Mockito.*; #if (${column.listOperation}) #set ($JavaField = $column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)})##首字母大写 #if (${column.listOperationCondition} == "BETWEEN")## BETWEEN 的情况 - reqVO.set${JavaField}((new Date[]{})); + reqVO.set${JavaField}((new LocalDateTime[]{})); #else reqVO.set$JavaField(null); #end diff --git a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/index.vue.vm b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/index.vue.vm index c84a510ac..7a6add604 100644 --- a/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/index.vue.vm +++ b/yudao-module-infra/yudao-module-infra-biz/src/main/resources/codegen/vue/views/index.vue.vm @@ -65,15 +65,15 @@ #set ($javaField = $column.javaField) #set ($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) #set ($comment=$column.columnComment) -#if ($column.javaType == "Date")## 时间类型 +#if ($column.javaType == "LocalDateTime")## 时间类型 -