Просмотр исходного кода

Merge branch 'update-trade-20211101' of lianghanqiang/ldt into dev

July 4 лет назад
Родитель
Сommit
233f6e654c
34 измененных файлов с 1817 добавлено и 163 удалено
  1. 170 0
      ldt-core/src/main/java/org/springblade/common/aop/DecryptResolver.java
  2. 82 0
      ldt-core/src/main/java/org/springblade/common/aop/EncryptResponseBodyAdvice.java
  3. 13 0
      ldt-core/src/main/java/org/springblade/common/aop/bean/DecryptBodyDTO.java
  4. 25 0
      ldt-core/src/main/java/org/springblade/common/aop/bean/JsonRequest.java
  5. 22 0
      ldt-core/src/main/java/org/springblade/common/aop/core/DecryptRequestBody.java
  6. 20 0
      ldt-core/src/main/java/org/springblade/common/aop/core/EncryptResponse.java
  7. 33 0
      ldt-core/src/main/java/org/springblade/common/config/ApiEncryptConfig.java
  8. 34 0
      ldt-core/src/main/java/org/springblade/common/config/WebMvcConfig.java
  9. 78 0
      ldt-core/src/main/java/org/springblade/common/config/mq/OrderRabbitConfig.java
  10. 44 0
      ldt-core/src/main/java/org/springblade/common/constant/OrderConstant.java
  11. 26 0
      ldt-core/src/main/java/org/springblade/common/enums/FrozenSatus.java
  12. 26 0
      ldt-core/src/main/java/org/springblade/common/enums/FrozenType.java
  13. 5 0
      ldt-core/src/main/java/org/springblade/common/enums/ResCode.java
  14. 4 59
      ldt-core/src/main/java/org/springblade/common/utils/GlobalExceptionHandler.java
  15. 275 0
      ldt-core/src/main/java/org/springblade/common/utils/encrypt/AESEncryptUtil.java
  16. 468 0
      ldt-core/src/main/java/org/springblade/common/utils/encrypt/RSAEncryptUtil.java
  17. 42 0
      ldt-core/src/main/java/org/springblade/common/utils/encrypt/RequestUtil.java
  18. 55 0
      ldt-core/src/main/java/org/springblade/common/utils/encrypt/ResponseUtil.java
  19. 22 10
      ldt-core/src/main/java/org/springblade/gateway/client_gateway/controller/ClientTradeController.java
  20. 3 0
      ldt-core/src/main/java/org/springblade/gateway/web_gateway/controller/PressureTestController.java
  21. 32 24
      ldt-core/src/main/java/org/springblade/ldt/bills/entity/FrozenRec.java
  22. 2 1
      ldt-core/src/main/java/org/springblade/ldt/bills/service/IFrozenRecService.java
  23. 6 3
      ldt-core/src/main/java/org/springblade/ldt/bills/service/impl/FrozenRecServiceImpl.java
  24. 1 2
      ldt-core/src/main/java/org/springblade/payment/callback/trade/UserPayCS.java
  25. 65 51
      ldt-core/src/main/java/org/springblade/payment/handle/Trade.java
  26. 3 3
      ldt-core/src/main/java/org/springblade/payment/handle/handler/BalanceHandle.java
  27. 88 0
      ldt-core/src/main/java/org/springblade/payment/handle/handler/CancelTradeHandle.java
  28. 3 3
      ldt-core/src/main/java/org/springblade/payment/handle/handler/ChannelPointHandle.java
  29. 2 1
      ldt-core/src/main/java/org/springblade/payment/handle/handler/DiscountHandle.java
  30. 65 0
      ldt-core/src/main/java/org/springblade/payment/listener/consumer/OrderListener.java
  31. 60 0
      ldt-core/src/main/java/org/springblade/payment/listener/sender/OrderSend.java
  32. 21 1
      ldt-core/src/main/java/org/springblade/payment/plugin/YeePayPlugin.java
  33. 21 1
      ldt-core/src/main/resources/application-dev.yml
  34. 1 4
      ldt-core/src/main/resources/application.yml

+ 170 - 0
ldt-core/src/main/java/org/springblade/common/aop/DecryptResolver.java

@@ -0,0 +1,170 @@
+package org.springblade.common.aop;
+
+import cn.hutool.core.io.file.FileReader;
+import cn.hutool.crypto.digest.DigestAlgorithm;
+import cn.hutool.crypto.digest.Digester;
+import com.alibaba.fastjson.JSONObject;
+import org.apache.commons.lang3.StringUtils;
+import org.springblade.common.aop.bean.DecryptBodyDTO;
+import org.springblade.common.aop.core.DecryptRequestBody;
+import org.springblade.common.config.ApiEncryptConfig;
+import org.springblade.common.enums.ResCode;
+import org.springblade.common.utils.encrypt.AESEncryptUtil;
+import org.springblade.common.utils.encrypt.RSAEncryptUtil;
+import org.springblade.common.utils.encrypt.RequestUtil;
+import org.springblade.common.utils.encrypt.ResponseUtil;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.core.tool.api.R;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.MethodParameter;
+import org.springframework.stereotype.Component;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.Collection;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName EncryptResponseBodyAdvice.java
+ * @Description @EncryptResponse的方法的参数进行解密操作
+ * @createTime 2021年11月02日 10:45:00
+ */
+@Component
+public class DecryptResolver implements HandlerMethodArgumentResolver {
+
+	@Autowired
+	private ApiEncryptConfig apiEncryptConfig;
+	@Autowired
+	private HttpServletRequest httpServletRequest;
+	//@Value("${rsa.publickey}")//公钥 暂时不用
+	private String RSA_PUBLICKEY=null;
+	//@Value("${rsa.privatekey}")//私钥 暂时不用
+	private String RSA_PRIVATEKEY=null;
+
+	@Override
+	public boolean supportsParameter(MethodParameter methodParameter) {
+		return methodParameter.hasParameterAnnotation(DecryptRequestBody.class);
+	}
+
+	@Override
+	public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer,
+								  NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
+		Class<?> parameterType = methodParameter.getParameterType();
+		HttpServletRequest httpServletRequest = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
+		HttpServletResponse httpServletResponse = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
+		if (httpServletRequest == null) {
+			throw new ServiceException(ResCode.PARAMETER_ERROR);
+		}
+		DecryptRequestBody annotation = methodParameter.getParameter().getDeclaredAnnotation(DecryptRequestBody.class);
+		String result = RequestUtil.ReadAsChars2(httpServletRequest);
+		boolean decrypt = annotation.decrypt();
+		Class<?> type = methodParameter.getParameter().getType();
+		// 全局配置.yml > 注解 全局配置不解密则不解密
+		if (!apiEncryptConfig.getRequest()) {
+			decrypt = false;
+		}
+
+		// 解密后操作 解密前端传输套多一层{data:value}
+		if (decrypt) {
+			long timestamp = Long.parseLong(httpServletRequest.getHeader("timestamp"));
+			checkTimeout(timestamp);
+
+			DecryptBodyDTO decryptBody = JSONObject.parseObject(result, DecryptBodyDTO.class);
+			if (ObjectUtils.isEmpty(decryptBody) || StringUtils.isEmpty(decryptBody.getData())) {
+				throw new ServiceException(ResCode.PARAMETER_ERROR);
+			}
+
+			String sign = httpServletRequest.getHeader("sign");
+			if (ObjectUtils.isEmpty(sign)) {
+				throw new ServiceException(ResCode.PARAMETER_ERROR);
+			}
+
+			//签名验证
+			boolean verify = RSAEncryptUtil.verify(JSONObject.toJSONString(decryptBody).getBytes(), RSA_PUBLICKEY, sign);
+			if (!verify) {
+				throw new ServiceException(ResCode.INVALID_SIGNATURE);
+			}
+
+			// System.out.println("body解密前:" + result0.getData());
+			result = AESEncryptUtil.decrypt(decryptBody.getData());
+			// System.out.println("body解密后:" + result0);
+		}
+		try {
+			return validateClassParameter(JSONObject.parseObject(result, parameterType), httpServletResponse);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		throw new ServiceException(ResCode.PARAMETER_ERROR);
+	}
+
+	/**
+	 * 检查是否 超时
+	 *
+	 * @param timestamp 请求时间戳
+	 */
+	private void checkTimeout(long timestamp) {
+		//不允许请求时间与当前时间差距过大(正负10分钟)
+		if (ObjectUtils.isEmpty(timestamp)) {
+			throw new ServiceException(ResCode.PARAMETER_ERROR);
+		}
+		long currentTime = System.currentTimeMillis();
+		long subTime = currentTime - timestamp;
+		long tenMinuteMs = 2 * 60 * 1000;
+		if (subTime < -tenMinuteMs || subTime > tenMinuteMs) {
+			throw new ServiceException(ResCode.REQUEST_TIMEOUT);
+		}
+	}
+
+	/**
+	 * 读取文件中的密钥对
+	 */
+	private void fileReader() {
+		FileReader fileReader = new FileReader("test.properties");
+		String result = fileReader.readString();
+	}
+
+	// 检测参数上的注解
+	private Object validateClassParameter(Object result, HttpServletResponse httpServletResponse) throws Exception {
+		if (result == null) {
+			ResponseUtil.toResponseEncryptWriter(httpServletResponse, R.fail(ResCode.PARAMETER_ERROR), null);
+		}
+		Class<?> type = result.getClass();
+		Field[] declaredFields = type.getDeclaredFields();
+		for (Field declaredField : declaredFields) {
+			Annotation[] declaredAnnotations = declaredField.getDeclaredAnnotations();
+			for (Annotation declaredAnnotation : declaredAnnotations) {
+				Class<? extends Annotation> aClass = declaredAnnotation.annotationType();
+				if (aClass == NotNull.class) {
+					declaredField.setAccessible(true);
+					Object value = declaredField.get(result);
+					if (value == null) {
+						NotNull annotation = declaredField.getAnnotation(NotNull.class);
+						//LogUtils.systemLog.error("参数异常:{}", declaredField.getName() + annotation.message());
+						throw new ServiceException(ResCode.PARAMETER_ERROR);
+					}
+				} else if (aClass == NotEmpty.class) {
+					declaredField.setAccessible(true);
+					Collection value = (Collection) declaredField.get(result);
+					if (value == null || value.size() <= 0) {
+						NotEmpty annotation = declaredField.getAnnotation(NotEmpty.class);
+						//LogUtils.systemLog.error("参数异常:{}", declaredField.getName() + annotation.message());
+						throw new ServiceException(ResCode.PARAMETER_ERROR);
+					}
+				}
+			}
+		}
+		return result;
+	}
+
+}

+ 82 - 0
ldt-core/src/main/java/org/springblade/common/aop/EncryptResponseBodyAdvice.java

@@ -0,0 +1,82 @@
+package org.springblade.common.aop;
+
+import com.alibaba.fastjson.JSONObject;
+import org.springblade.common.aop.core.EncryptResponse;
+import org.springblade.common.config.ApiEncryptConfig;
+import org.springblade.common.utils.encrypt.AESEncryptUtil;
+import org.springblade.core.tool.api.R;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.core.MethodParameter;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.http.server.ServerHttpRequest;
+import org.springframework.http.server.ServerHttpResponse;
+import org.springframework.util.ObjectUtils;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName EncryptResponseBodyAdvice.java
+ * @Description @Encrypt的方法的数据进行加密操作
+ * @createTime 2021年11月02日 11:45:00
+ */
+@ControllerAdvice
+public class EncryptResponseBodyAdvice implements ResponseBodyAdvice<Object> {
+
+	@Value("${spring.crypto.request.decrypt.charset:UTF-8}")
+	private String charset = "UTF-8";
+
+	@Override
+	public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
+		return true;
+	}
+
+	@Autowired
+	private ApiEncryptConfig apiEncryptConfig;
+
+	@Override
+	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
+								  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
+								  ServerHttpResponse response) {
+
+		// 实现具体的加密方法
+		boolean encrypt = needEncrypt(returnType);
+		if (ObjectUtils.isEmpty(body) || !encrypt) {
+			return R.data(body);
+		}
+
+		String json = JSONObject.toJSONString(body);
+		//System.out.println("返回值加密前===" + json);
+		String encryptStr = AESEncryptUtil.encrypt(json).replaceAll(System.getProperty("line.separator"), "");
+		//System.out.println("返回值加密后====" + encryptStr);
+		return R.data(encryptStr);
+	}
+
+	private boolean needEncrypt(MethodParameter returnType) {
+		// 判断有无开启加密 全局配置.yml > 注解 全局配置不加密则不加密
+		if (!apiEncryptConfig.getResponse()) {
+			return false;
+		}
+		// 当全局开启加密时
+		boolean encrypt = false;
+		boolean classPresentAnno = returnType.getContainingClass().isAnnotationPresent(EncryptResponse.class);
+		boolean methodPresentAnno = returnType.getMethod().isAnnotationPresent(EncryptResponse.class);
+
+		if (classPresentAnno) {
+			// 类上标注的是否需要加密
+			encrypt = returnType.getContainingClass().getAnnotation(EncryptResponse.class).value();
+			// 类不加密,所有都不加密
+			if (!encrypt) {
+				return false;
+			}
+		}
+		if (methodPresentAnno) {
+			// 方法上标注的是否需要加密
+			encrypt = returnType.getMethod().getAnnotation(EncryptResponse.class).value();
+		}
+		return encrypt;
+	}
+}

+ 13 - 0
ldt-core/src/main/java/org/springblade/common/aop/bean/DecryptBodyDTO.java

@@ -0,0 +1,13 @@
+package org.springblade.common.aop.bean;
+
+import lombok.Data;
+
+/**
+ * @date: 2021/6/7 15:47
+ * @Author: AaronXu
+ * @Description: 接收body加密对象
+ */
+@Data
+public class DecryptBodyDTO {
+    private String data;
+}

+ 25 - 0
ldt-core/src/main/java/org/springblade/common/aop/bean/JsonRequest.java

@@ -0,0 +1,25 @@
+package org.springblade.common.aop.bean;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName JsonRequst.java
+ * @Description TODO
+ * @createTime 2021年11月02日 09:55:00
+ */
+@Data
+public class JsonRequest {
+
+	@ApiModelProperty(value = "参数签名", required = true)
+	private String sign;
+
+	@ApiModelProperty(value = "时间戳,精确到毫秒", required = true)
+	private long timestamp;
+
+	@ApiModelProperty(value = "请求的业务参数(AES加密后传入)")
+	private String body;
+
+}

+ 22 - 0
ldt-core/src/main/java/org/springblade/common/aop/core/DecryptRequestBody.java

@@ -0,0 +1,22 @@
+package org.springblade.common.aop.core;
+
+import java.lang.annotation.*;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName DecryptRequestBody.java
+ * @Description 解码 前端加密的requestBody
+ * @createTime 2021年11月02日 10:45:00
+ */
+@Target({ElementType.PARAMETER})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface DecryptRequestBody {
+
+	/**
+	 * 是否对结果解密
+	 */
+	boolean decrypt() default true;
+
+}

+ 20 - 0
ldt-core/src/main/java/org/springblade/common/aop/core/EncryptResponse.java

@@ -0,0 +1,20 @@
+package org.springblade.common.aop.core;
+
+import java.lang.annotation.*;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName EncryptResponse.java
+ * @Description 加密返回, 作用在类上/方法上, 全局 < 类 < 方法
+ * @createTime 2021年11月02日 11:22:00
+ */
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface EncryptResponse {
+	/**
+	 * 是否对结果加密
+	 */
+	boolean value() default true;
+}

+ 33 - 0
ldt-core/src/main/java/org/springblade/common/config/ApiEncryptConfig.java

@@ -0,0 +1,33 @@
+package org.springblade.common.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.PostConstruct;
+
+/**
+ * @date: 2021/6/7 10:59
+ * @Author: AaronXu
+ * @Description: 全局加密配置
+ */
+@Component
+@ConfigurationProperties(prefix = "encrypt")
+@Data
+public class ApiEncryptConfig {
+    private boolean request;
+    private boolean response;
+    public static boolean requestStatic;
+    public static boolean responseStatic;
+    @PostConstruct
+    public void init() {
+        requestStatic = request;
+        responseStatic = response;
+    }
+    public boolean getRequest() {
+        return request;
+    }
+    public boolean getResponse() {
+        return response;
+    }
+}

+ 34 - 0
ldt-core/src/main/java/org/springblade/common/config/WebMvcConfig.java

@@ -0,0 +1,34 @@
+package org.springblade.common.config;
+
+import org.springblade.common.aop.DecryptResolver;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName WebMvcConfig.java
+ * @Description TODO
+ * @createTime 2021年11月02日 10:22:00
+ */
+@Configuration
+@EnableWebMvc
+public class WebMvcConfig implements WebMvcConfigurer {
+
+	@Autowired
+	private ApplicationContext applicationContext;
+
+	@Override
+	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
+		DecryptResolver resolver = new DecryptResolver();
+		// 利用工厂给容器外的对象注入所需组件
+		applicationContext.getAutowireCapableBeanFactory().autowireBean(resolver);
+		argumentResolvers.add(resolver);
+	}
+}

+ 78 - 0
ldt-core/src/main/java/org/springblade/common/config/mq/OrderRabbitConfig.java

@@ -0,0 +1,78 @@
+package org.springblade.common.config.mq;
+
+import org.springblade.common.constant.OrderConstant;
+import org.springframework.amqp.core.*;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author: Silent
+ * @Description
+ * @Date: Created in 16:25 2021/11/1
+ * @Modified By:
+ */
+@Configuration
+public class OrderRabbitConfig {
+	/**
+	 * TTL队列
+	 * @return
+	 */
+	@Bean
+	public Queue orderQueueTTL(){
+		Map<String, Object> arguments = new HashMap<>();
+		// 超时时间(单位毫秒)
+		arguments.put("x-message-ttl", OrderConstant.ORDER_DELAY_TIME_OUT);
+		// 设置关联的死信队列
+		arguments.put("x-dead-letter-routing-key", OrderConstant.DLX_ROUTE_KEY);
+		arguments.put("x-dead-letter-exchange", OrderConstant.DLX_ORDER_EXCHANGE);
+		return new Queue(OrderConstant.TTL_ORDER_QUEUE, true, false, false, arguments);
+	}
+
+	/**
+	 * TTL交换机
+	 * @return
+	 */
+	@Bean
+	public Exchange orderExchangeTTL() {
+		return new DirectExchange(OrderConstant.TTL_ORDER_EXCHANGE, true, false);
+	}
+
+	/**
+	 * 绑定TTL
+	 * @return
+	 */
+	@Bean
+	public Binding orderBindingTTL(){
+		return BindingBuilder.bind(orderQueueTTL()).to(orderExchangeTTL()).with(OrderConstant.TTL_ROUTE_KEY).noargs();
+	}
+
+	/**
+	 * 死信队列
+	 * @return
+	 */
+	@Bean
+	public Queue orderQueueDLX(){
+		return new Queue(OrderConstant.DLX_ORDER_QUEUE, true, false, false, null);
+	}
+
+	/**
+	 * 死信交换机
+	 * @return
+	 */
+	@Bean
+	public Exchange orderExchangeDLX() {
+		return new DirectExchange(OrderConstant.DLX_ORDER_EXCHANGE, true, false);
+	}
+
+	/**
+	 * 绑定死信队列和死信交换机
+	 * @return
+	 */
+	@Bean
+	public Binding orderBindingDLX(){
+		return BindingBuilder.bind(orderQueueDLX()).to(orderExchangeDLX()).with(OrderConstant.DLX_ROUTE_KEY).noargs();
+	}
+}

+ 44 - 0
ldt-core/src/main/java/org/springblade/common/constant/OrderConstant.java

@@ -0,0 +1,44 @@
+package org.springblade.common.constant;
+
+/**
+ * @Author: Silent
+ * @Description 订单常量
+ * @Date: Created in 16:27 2021/11/1
+ * @Modified By:
+ */
+public interface OrderConstant {
+	/**
+	 * TTL订单队列常量
+	 */
+	String TTL_ORDER_QUEUE = "order-delay-ttl-queue";
+
+	/**
+	 * TTL订单交换机常量
+	 */
+	String TTL_ORDER_EXCHANGE = "order-delay-ttl-exchange";
+
+	/**
+	 * DLX订单队列常量
+	 */
+	String DLX_ORDER_QUEUE = "order-delay-dlx-queue";
+
+	/**
+	 * DLX订单交换机常量
+	 */
+	String DLX_ORDER_EXCHANGE = "order-delay-dlx-exchange";
+
+	/**
+	 * TTL路由key
+	 */
+	String TTL_ROUTE_KEY = "order.delay.ttl";
+
+	/**
+	 * DLX路由key
+	 */
+	String DLX_ROUTE_KEY = "order.delay.dlx";
+
+	/**
+	 * 订单超时时间(毫秒)
+	 */
+	long ORDER_DELAY_TIME_OUT = 90000L;
+}

+ 26 - 0
ldt-core/src/main/java/org/springblade/common/enums/FrozenSatus.java

@@ -0,0 +1,26 @@
+package org.springblade.common.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName FrozenSatus.java
+ * @Description TODO
+ * @createTime 2021年11月01日 16:29:00
+ */
+@AllArgsConstructor
+@Getter
+public enum FrozenSatus {
+	SUCCEED_FROZEN("SUCCEED_FROZEN", "已完成"),
+	RETURNED_FROZEN("RETURNED_FROZEN", "已退回"),
+	;
+
+	@EnumValue
+	private String value;
+	@JsonValue
+	private String dec;
+}

+ 26 - 0
ldt-core/src/main/java/org/springblade/common/enums/FrozenType.java

@@ -0,0 +1,26 @@
+package org.springblade.common.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.fasterxml.jackson.annotation.JsonValue;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName FrozenType.java
+ * @Description 冻结类型
+ * @createTime 2021年11月01日 16:54:00
+ */
+@AllArgsConstructor
+@Getter
+public enum FrozenType {
+	POINT("POINT", "积分"),
+	BALANCE("BALANCE", "余额"),
+	;
+
+	@EnumValue
+	private String value;
+	@JsonValue
+	private String dec;
+}

+ 5 - 0
ldt-core/src/main/java/org/springblade/common/enums/ResCode.java

@@ -18,6 +18,7 @@ public enum ResCode implements IResultCode {
 	PAY_TYPE_ERROR(501,"支付方式错误"),
 	PAY_SCENE_ERROR(501,"支付场景错误"),
 	TRADE_ERROR(501,"交易异常"),
+	TRADE_TIME_OUT(501,"订单超时"),
 	INIT_ORDER_FAIL(501,"初始化订单失败"),
 	CHANNEL_NOT_FOUND(501,"积分渠道查找失败!"),
 	USER_PAY_CALLBACK_ERROR(501,"用户支付回调异常"),
@@ -41,6 +42,10 @@ public enum ResCode implements IResultCode {
 
 	/*  通用   */
 	FAIL(505,"系统异常!"),
+	PARAMETER_ERROR(505,"参数异常!"),
+	REQUEST_TIMEOUT(505,"请求时间异常!"),
+
+	INVALID_SIGNATURE(413,"无效签名!"),
 
 
 	/*	系统模块 */

+ 4 - 59
ldt-core/src/main/java/org/springblade/common/utils/GlobalExceptionHandler.java

@@ -2,28 +2,19 @@ package org.springblade.common.utils;
 
 import com.yeepay.yop.sdk.exception.YopServiceException;
 import org.apache.commons.lang3.ObjectUtils;
-import org.springblade.common.enums.AppConstant;
 import org.springblade.core.tool.api.R;
-import org.springblade.ldt.bills.entity.BalanceBills;
-import org.springblade.ldt.bills.entity.Bills;
-import org.springblade.ldt.bills.entity.FrozenRec;
-import org.springblade.ldt.bills.entity.PointBills;
 import org.springblade.ldt.bills.service.IBalanceBillsService;
 import org.springblade.ldt.bills.service.IBillsService;
 import org.springblade.ldt.bills.service.IFrozenRecService;
 import org.springblade.ldt.bills.service.IPointBillsService;
-import org.springblade.ldt.user.entity.LoginUser;
-import org.springblade.ldt.user.entity.UserChannelPoint;
 import org.springblade.ldt.user.service.ILoginUserService;
 import org.springblade.ldt.user.service.IUserChannelPointService;
-import org.springblade.payment.entity.SuccessParams;
+import org.springblade.payment.handle.handler.CancelTradeHandle;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.ControllerAdvice;
 import org.springframework.web.bind.annotation.ExceptionHandler;
 import org.springframework.web.bind.annotation.ResponseBody;
 
-import java.util.Objects;
-
 
 /**
  * @author July
@@ -35,12 +26,7 @@ import java.util.Objects;
 @ControllerAdvice
 @ResponseBody
 public class GlobalExceptionHandler {
-	private ILoginUserService loginUserService;
-	private IBillsService billsService;
-	private IPointBillsService pointBillsService;
-	private IBalanceBillsService balanceBillsService;
-	private IFrozenRecService frozenRecService;
-	private IUserChannelPointService userChannelPointService;
+	private CancelTradeHandle cancelTradeHandle;
 
 	@ExceptionHandler(YopServiceException.class)
 	public R defaultErrorHandler(YopServiceException e) {
@@ -50,49 +36,8 @@ public class GlobalExceptionHandler {
 	@Transactional
 	@ExceptionHandler(TradeException.class)
 	public R tradeErrorHandler(TradeException e) {
-		SuccessParams successParams = e.getSuccessParams();
-
-		if (ObjectUtils.isNotEmpty(successParams)) {
-			Bills bills = successParams.getBills();
-			synchronized (bills.getId().toString().intern()) {
-				FrozenRec frozenRec = frozenRecService.getById(bills.getId());
-				UserChannelPoint channelPoint = userChannelPointService.getById(successParams.getChannelId());
-
-				//修改用户冻结余额或积分
-				LoginUser user = loginUserService.getById(bills.getPayId());
-				if (ObjectUtils.isNotEmpty(user)) {
-					if (Objects.equals(frozenRec.getFrozenType(), "point")) {
-						user.setChannelPoint(user.getChannelPoint().add(frozenRec.getFrozenNum()));
-						user.setFrozenPoint(user.getFrozenPoint().subtract(frozenRec.getFrozenNum()));
-						if (ObjectUtils.isNotEmpty(channelPoint)) {
-							channelPoint.setAvailable(channelPoint.getAvailable().add(frozenRec.getFrozenNum()));
-							userChannelPointService.saveOrUpdate(channelPoint);
-						}
-					} else if (Objects.equals(frozenRec.getFrozenType(), "balance")) {
-						user.setBalance(user.getBalance().add(frozenRec.getFrozenNum()));
-						user.setFrozenBalance(user.getFrozenBalance().subtract(frozenRec.getFrozenNum()));
-					}
-				}
-
-				if (ObjectUtils.isNotEmpty(frozenRec)) {
-					frozenRecService.removeById(frozenRec);
-				}
-				loginUserService.saveOrUpdate(user);
-			}
-			bills.setPayStatus(AppConstant.BillPayStatus.取消付款.name());
-			billsService.saveOrUpdate(bills);
-
-			PointBills pointBills = pointBillsService.getById(successParams.getPointBillsId());
-			if (ObjectUtils.isNotEmpty(pointBills)) {
-				pointBills.setPayStatus(AppConstant.BillPayStatus.取消付款.name());
-				pointBillsService.saveOrUpdate(pointBills);
-			}
-
-			BalanceBills balanceBills = balanceBillsService.getById(successParams.getBalanceBillsId());
-			if (ObjectUtils.isNotEmpty(balanceBills)) {
-				balanceBills.setPayStatus(AppConstant.BillPayStatus.取消付款.name());
-				balanceBillsService.saveOrUpdate(balanceBills);
-			}
+		if (ObjectUtils.isNotEmpty(e.getSuccessParams())) {
+			cancelTradeHandle.handle(e.getSuccessParams());
 		}
 		return R.fail(e.getResultCode(), e.getMessage());
 	}

+ 275 - 0
ldt-core/src/main/java/org/springblade/common/utils/encrypt/AESEncryptUtil.java

@@ -0,0 +1,275 @@
+package org.springblade.common.utils.encrypt;
+
+import org.apache.commons.lang3.StringUtils;
+import org.springblade.core.log.logger.BladeLogger;
+
+import javax.crypto.Cipher;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.util.Base64;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName AESEncryptUtil.java
+ * @Description AES对称加密
+ * @createTime 2021年11月02日 16:01:09
+ */
+public class AESEncryptUtil {
+
+	private static final String TAG = "AESEncryptUtil";
+	private static final String UTF8 = "UTF-8";
+	private static final String AES = "AES";
+	private static final String AES_CBC_PKCS5_PADDING = "AES/CBC/PKCS5Padding";
+	private static final String AES_CBC_NO_PADDING = "AES/CBC/NoPadding";
+
+	// private static final String aesKey = "64sfe88aec1b0d57";
+	// private static final String aesIV = "c709e1bcb45362hg";
+
+	private static final String aesKey = "m7wNr1ZZaUJjvkto";
+	private static final String aesIV = "Xs8AcmpeaUOWavBl";
+
+	private static BladeLogger bladeLogger;
+
+	/**
+	 * JDK只支持AES-128加密,也就是密钥长度必须是128bit;参数为密钥key,key的长度小于16字符时用"0"补充,key长度大于16字符时截取前16位
+	 **/
+	private static SecretKeySpec create128BitsKey(String key) {
+		if (key == null) {
+			key = "";
+		}
+		byte[] data = null;
+		StringBuffer buffer = new StringBuffer(16);
+		buffer.append(key);
+		// 小于16后面补0
+		while (buffer.length() < 16) {
+			buffer.append("0");
+		}
+		// 大于16,截取前16个字符
+		if (buffer.length() > 16) {
+			buffer.setLength(16);
+		}
+		try {
+			data = buffer.toString().getBytes(UTF8);
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+		return new SecretKeySpec(data, AES);
+	}
+
+	/**
+	 * 创建128位的偏移量,iv的长度小于16时后面补0,大于16,截取前16个字符;
+	 *
+	 * @param iv
+	 * @return
+	 */
+	private static IvParameterSpec create128BitsIV(String iv) {
+		if (iv == null) {
+			iv = "";
+		}
+		byte[] data = null;
+		StringBuffer buffer = new StringBuffer(16);
+		buffer.append(iv);
+		while (buffer.length() < 16) {
+			buffer.append("0");
+		}
+		if (buffer.length() > 16) {
+			buffer.setLength(16);
+		}
+		try {
+			data = buffer.toString().getBytes(UTF8);
+		} catch (UnsupportedEncodingException e) {
+			e.printStackTrace();
+		}
+		return new IvParameterSpec(data);
+	}
+
+	/**
+	 * 填充方式为Pkcs5Padding时,最后一个块需要填充χ个字节,填充的值就是χ,也就是填充内容由JDK确定
+	 *
+	 * @param srcContent
+	 * @param password
+	 * @param iv
+	 * @return
+	 */
+	// private static byte[] aesCbcPkcs5PaddingEncrypt(byte[] srcContent, String password, String iv) {
+	// SecretKeySpec key = create128BitsKey(password);
+	// IvParameterSpec ivParameterSpec = create128BitsIV(iv);
+	// try {
+	// Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_PADDING);
+	// cipher.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);
+	// byte[] encryptedContent = cipher.doFinal(srcContent);
+	// return encryptedContent;
+	// } catch (Exception e) {
+	// e.printStackTrace();
+	// }
+	// return null;
+	// }
+
+	// private static byte[] aesCbcPkcs5PaddingDecrypt(byte[] encryptedContent, String password, String iv) {
+	// SecretKeySpec key = create128BitsKey(password);
+	// IvParameterSpec ivParameterSpec = create128BitsIV(iv);
+	// try {
+	// Cipher cipher = Cipher.getInstance(AES_CBC_PKCS5_PADDING);
+	// cipher.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);
+	// byte[] decryptedContent = cipher.doFinal(encryptedContent);
+	// return decryptedContent;
+	// } catch (Exception e) {
+	// e.printStackTrace();
+	// }
+	// return null;
+	// }
+
+	/**
+	 * 填充方式为NoPadding时,最后一个块的填充内容由程序员确定,通常为0. AES/CBC/NoPadding加密的明文长度必须是16的整数倍,明文长度不满足16时,程序员要扩充到16的整数倍
+	 *
+	 * @param sSrc
+	 * @param aesKey
+	 * @param aesIV
+	 * @return
+	 */
+	private static byte[] aesCbcNoPaddingEncrypt(byte[] sSrc, String aesKey, String aesIV) {
+		// 加密的数据长度不是16的整数倍时,原始数据后面补0,直到长度满足16的整数倍
+		int len = sSrc.length;
+		// 计算补0后的长度
+		while (len % 16 != 0) {
+			len++;
+		}
+		byte[] result = new byte[len];
+		// 在最后补0
+		for (int i = 0; i < len; ++i) {
+			if (i < sSrc.length) {
+				result[i] = sSrc[i];
+			} else {
+				// 填充字符'a'
+				// result[i] = 'a';
+				result[i] = 0;
+			}
+		}
+		SecretKeySpec skeySpec = create128BitsKey(aesKey);
+		// 使用CBC模式,需要一个初始向量iv,可增加加密算法的强度
+		IvParameterSpec iv = create128BitsIV(aesIV);
+		Cipher cipher = null;
+		try {
+			// 算法/模式/补码方式
+			cipher = Cipher.getInstance(AES_CBC_NO_PADDING);
+			cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
+		} catch (Exception e) {
+			e.printStackTrace();
+			bladeLogger.info("aesCbcNoPaddingEncrypt Exception", e.getMessage());
+		}
+		byte[] encrypted = null;
+		try {
+			encrypted = cipher.doFinal(result);
+		} catch (Exception e) {
+			e.printStackTrace();
+			bladeLogger.info("aesCbcNoPaddingEncrypt Exception", e.getMessage());
+		}
+		return encrypted;
+	}
+
+	private static byte[] aesCbcNoPaddingDecrypt(byte[] sSrc, String aesKey, String aesIV) {
+		SecretKeySpec skeySpec = create128BitsKey(aesKey);
+		IvParameterSpec iv = create128BitsIV(aesIV);
+		try {
+			Cipher cipher = Cipher.getInstance(AES_CBC_NO_PADDING);
+			cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
+			byte[] decryptContent = cipher.doFinal(sSrc);
+			return decryptContent;
+		} catch (Exception e) {
+			bladeLogger.info("aesCbcNoPaddingDecrypt Exception", e.getMessage());
+		}
+		return null;
+	}
+
+	public static String decrypt(String encryptText) {
+		if (StringUtils.isBlank(encryptText)) {
+			return "";
+		}
+		try {
+
+			byte[] encryptByte = encryptText.getBytes();
+			byte[] decryptByte = Base64.getDecoder().decode(encryptByte);
+			byte[] decryptTextByte = aesCbcNoPaddingDecrypt(decryptByte, aesKey, aesIV);
+			String decryptText = new String(decryptTextByte);
+			return decryptText.trim();
+		} catch (Exception e) {
+			bladeLogger.info("decrypt Exception", e.getMessage());
+			return null;
+		}
+	}
+
+	public static String decrypt(String encryptText, String charsetName) {
+		if (StringUtils.isBlank(encryptText)) {
+			return "";
+		}
+		try {
+
+			byte[] encryptByte = encryptText.getBytes();
+			byte[] decryptByte = Base64.getDecoder().decode(encryptByte);
+			byte[] decryptTextByte = aesCbcNoPaddingDecrypt(decryptByte, aesKey, aesIV);
+			if (decryptByte == null) {
+				return "";
+			}
+			String decryptText = new String(decryptTextByte, charsetName);
+			return decryptText.trim();
+		} catch (Exception e) {
+			bladeLogger.info("decrypt Exception", e.getMessage());
+			return null;
+		}
+	}
+
+	public static String decrypt(String data, String aseKey, String aesIV) {
+		if (StringUtils.isBlank(data)) {
+			return "";
+		}
+		try {
+
+			byte[] encryptByte = data.getBytes();
+			byte[] decryptByte = Base64.getDecoder().decode(encryptByte);
+			byte[] decryptTextByte = aesCbcNoPaddingDecrypt(decryptByte, aseKey, aesIV);
+			String decryptText = new String(decryptTextByte);
+			return decryptText.trim();
+		} catch (Exception e) {
+			bladeLogger.info("decrypt Exception", e.getMessage());
+			return null;
+		}
+	}
+
+	public static String encrypt(String descryptText) {
+		if (StringUtils.isBlank(descryptText)) {
+			return "";
+		}
+		try {
+			byte[] encryptByte =
+				Base64.getEncoder().encode(aesCbcNoPaddingEncrypt(descryptText.getBytes(), aesKey, aesIV));
+			String encryptText = new String(encryptByte);
+			return encryptText;
+		} catch (Exception e) {
+			bladeLogger.info("encrypt Exception", e.getMessage());
+			return null;
+		}
+	}
+
+	public static String encrypt(String data, String aseKey, String aesIV) {
+		if (StringUtils.isBlank(data)) {
+			return "";
+		}
+		try {
+			byte[] encryptByte =
+				Base64.getEncoder().encode(aesCbcNoPaddingEncrypt(data.getBytes(), aseKey, aesIV));
+			String encryptText = new String(encryptByte);
+			return encryptText;
+		} catch (Exception e) {
+			bladeLogger.info("encrypt Exception", e.getMessage());
+			return null;
+		}
+	}
+
+	public static void main(String[] args) {
+
+//     System.out.println(decrypt("Ij0Spwx7QNScshoibM5GjXHZ+Xi0dDr6xsg3zGktLFbwDJdv46iSuW0iw3Ge/87IOEj8MBbvR4DWQVabCRtd/50xhzAbz4JbC3mIi/+yoPTn+JRVWJasg8ZFAuESivlIK+y6Xk7+90H+M6lYFGILH4h549xcRNltqaGK6YBZV1udTd69vtVmk3/gsd3ULHkqlsRfxObVn0itp4VkHuOwBVdrF6JWNpuqHBut4HlYR2hqhYVoiYHFOofsGOQoczxtVPiZiMVewod1MjGJ+4PN9b/vwHu9INWJZY5plS5jLsILgqCWP22RIzN0Uxm+y9TyW5Z5xDbMhrfEJdrDYQ4xXZ29jwbGQU19dFkvc4V8xEixaVOuPluB+OEUuuKded0ZHUrJ/0tcKX0P+vgSoDpJCkpSvZxiWZQcAwvRTYAptOUsAFROT+5WoiNqkCo/pTGL0iKmcrqJwiSPaVSKMMpyx+LY79XQEtVQiX6MOJNK3lU="));
+		System.out.println(encrypt("Qzpm18028660208"));
+	}
+}

+ 468 - 0
ldt-core/src/main/java/org/springblade/common/utils/encrypt/RSAEncryptUtil.java

@@ -0,0 +1,468 @@
+package org.springblade.common.utils.encrypt;
+
+import cn.hutool.core.codec.Base64;
+import cn.hutool.crypto.digest.DigestUtil;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.common.aop.bean.JsonRequest;
+import org.springframework.util.StringUtils;
+
+import javax.crypto.Cipher;
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.security.*;
+import java.security.interfaces.RSAPrivateKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.util.*;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName RSAEncryptUtil.java
+ * @Description RSA秘钥工具
+ * @createTime 2021年11月02日 16:01:09
+ */
+@Slf4j
+public class RSAEncryptUtil {
+
+	public static final String CHARSET = "UTF-8";
+
+	public static final String RSA_ALGORITHM = "RSA";
+	//定义签名算法
+	private final static String KEY_RSA_SIGNATURE = "MD5withRSA";
+	//定义公钥算法
+	private final static String KEY_RSA_PUBLICKEY = "RSAPublicKey";
+	//定义私钥算法
+	private final static String KEY_RSA_PRIVATEKEY = "RSAPrivateKey";
+
+	/**
+	 * 生成秘钥对
+	 */
+	private static Map<String, Object> generateKeyPairToMap() throws NoSuchAlgorithmException {
+		Map<String, Object> map = null;
+		try {
+			KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGORITHM);
+			KeyPair keyPair = generator.generateKeyPair();
+			// 公钥
+			RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
+			// 私钥
+			RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
+			// 将密钥封装为map
+			map = new HashMap<>();
+			map.put(KEY_RSA_PUBLICKEY, publicKey);
+			map.put(KEY_RSA_PRIVATEKEY, privateKey);
+		} catch (NoSuchAlgorithmException e) {
+			e.printStackTrace();
+		}
+		return map;
+	}
+
+	/**
+	 * 生成秘钥对
+	 */
+	private static void generateKeyPair() throws NoSuchAlgorithmException {
+		KeyPairGenerator keyTools = KeyPairGenerator.getInstance(RSA_ALGORITHM);
+		KeyPair keyPair = keyTools.generateKeyPair();
+		System.out.println(Base64.encode(keyPair.getPrivate().getEncoded()));
+		System.out.println(Base64.encode(keyPair.getPublic().getEncoded()));
+	}
+
+	/**
+	 * 得到公钥
+	 *
+	 * @param publicKey 密钥字符串(经过base64编码)
+	 * @throws Exception
+	 */
+	public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+		//通过X509编码的Key指令获得公钥对象
+		KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
+		X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decode(publicKey));
+		RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
+		return key;
+	}
+
+	/**
+	 * 获取秘钥对中的公钥
+	 *
+	 * @param map 秘钥对
+	 * @return 公钥
+	 */
+	public static String getPublicKey(Map<String, Object> map) {
+		String str = "";
+		try {
+			Key key = (Key) map.get(KEY_RSA_PUBLICKEY);
+			str = Base64.encode(key.getEncoded());
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return str;
+	}
+
+	/**
+	 * 得到私钥
+	 *
+	 * @param privateKey 密钥字符串(经过base64编码)
+	 * @throws Exception
+	 */
+	public static RSAPrivateKey getPrivateKey(String privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
+		//通过PKCS#8编码的Key指令获得私钥对象
+		KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM);
+		PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey));
+		RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
+		return key;
+	}
+
+	/**
+	 * 获取秘钥对中的私钥
+	 *
+	 * @param map 秘钥对
+	 * @return 私钥
+	 */
+	public static String getPrivateKey(Map<String, Object> map) {
+		String str = "";
+		try {
+			Key key = (Key) map.get(KEY_RSA_PRIVATEKEY);
+			str = Base64.encode(key.getEncoded());
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return str;
+	}
+
+	/**
+	 * 公钥加密
+	 *
+	 * @param data
+	 * @param publicKey
+	 * @return
+	 */
+	public static String publicEncrypt(String data, RSAPublicKey publicKey) {
+		return encode(data, publicKey);
+	}
+
+	/**
+	 * 公钥加密
+	 *
+	 * @param data
+	 * @param publicKey
+	 * @return
+	 */
+	public static String publicEncrypt(String data, String key) {
+		String result = null;
+		try {
+			byte[] bytes = Base64.decode(key);
+			// 取得公钥
+			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
+			KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
+			PublicKey publicKey = factory.generatePublic(keySpec);
+			// 对数据加密
+			//Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
+			Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
+			cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+			byte[] encode = cipher.doFinal(data.getBytes());
+			// 再进行Base64加密
+			result = Base64.encode(encode);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return result;
+	}
+
+	/**
+	 * 公钥解密
+	 *
+	 * @param data
+	 * @param publicKey
+	 * @return
+	 */
+
+	public static String publicDecrypt(String data, RSAPublicKey publicKey) {
+		return decode(data, publicKey);
+	}
+
+
+	/**
+	 * 私钥解密
+	 *
+	 * @param data
+	 * @param privateKey
+	 * @return
+	 */
+
+	public static String privateDecrypt(String data, RSAPrivateKey privateKey) {
+		return decode(data, privateKey);
+	}
+
+	/**
+	 * 私钥解密
+	 *
+	 * @param data 加密数据
+	 * @param key  私钥
+	 */
+	public static String privateDecrypt(byte[] data, String key) {
+		String result = null;
+		try {
+			// 对私钥解密
+			byte[] bytes = Base64.decode(key);
+			// 取得私钥
+			PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
+			KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
+			PrivateKey privateKey = factory.generatePrivate(keySpec);
+			// 对数据解密
+			//Cipher cipher = Cipher.getInstance("RSA/None/PKCS1Padding", "BC");
+			Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
+			cipher.init(Cipher.DECRYPT_MODE, privateKey);
+			return new String(cipher.doFinal(Base64.decode(data)), CHARSET);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return result;
+	}
+
+	/**
+	 * 私钥加密
+	 *
+	 * @param data
+	 * @param privateKey
+	 * @return
+	 */
+
+	public static String privateEncrypt(String data, RSAPrivateKey privateKey) {
+		return encode(data, privateKey);
+	}
+
+
+	private static String decode(String data, Key key) {
+		try {
+			Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
+			cipher.init(Cipher.DECRYPT_MODE, key);
+			return new String(cipher.doFinal(Base64.decode(data)), CHARSET);
+		} catch (Exception e) {
+			throw new RuntimeException("解密字符串[" + data + "]时遇到异常", e);
+		}
+	}
+
+
+	private static String encode(String data, Key key) {
+		try {
+			Cipher cipher = Cipher.getInstance(RSA_ALGORITHM);
+			cipher.init(Cipher.ENCRYPT_MODE, key);
+			return Base64.encode(cipher.doFinal(data.getBytes()));
+		} catch (Exception e) {
+			throw new RuntimeException("加密字符串[" + data + "]时遇到异常", e);
+		}
+	}
+
+	/**
+	 * bean转map
+	 *
+	 * @param obj
+	 * @return
+	 */
+	public static Map<String, Object> bean2Map(Object obj) {
+		if (obj == null) {
+			return null;
+		}
+		Map<String, Object> map = new HashMap<>();
+		try {
+			BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass());
+			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
+			for (PropertyDescriptor property : propertyDescriptors) {
+				String key = property.getName();
+				// 过滤class属性
+				if (!key.equals("class")) {
+					// 得到property对应的getter方法
+					Method getter = property.getReadMethod();
+					Object value = getter.invoke(obj);
+					if (StringUtils.isEmpty(value)) {
+						continue;
+					}
+					map.put(key, value);
+				}
+			}
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return map;
+	}
+
+	/**
+	 * 按照红黑树(Red-Black tree)的 NavigableMap 实现
+	 * 按照字母大小排序
+	 */
+	public static Map<String, Object> sort(Map<String, Object> map) {
+		if (map == null) {
+			return null;
+		}
+		Map<String, Object> result = new TreeMap<>((Comparator<String>) (o1, o2) -> {
+			return o1.compareTo(o2);
+		});
+		result.putAll(map);
+		return result;
+	}
+
+	/**
+	 * 组合参数
+	 *
+	 * @param map
+	 * @return 如:key1Value1Key2Value2....
+	 */
+	public static String groupStringParam(Map<String, Object> map) {
+		if (map == null) {
+			return null;
+		}
+		StringBuffer sb = new StringBuffer();
+		for (Map.Entry<String, Object> item : map.entrySet()) {
+			if (item.getValue() != null) {
+				sb.append(item.getKey());
+				if (item.getValue() instanceof List) {
+					sb.append(JSON.toJSONString(item.getValue()));
+				} else {
+					sb.append(item.getValue());
+				}
+			}
+		}
+		return sb.toString();
+	}
+
+	/**
+	 * 用私钥对信息生成数字签名
+	 *
+	 * @param data       加密数据
+	 * @param privateKey 私钥
+	 */
+	public static String sign(byte[] data, String privateKey) {
+		String str = "";
+		try {
+			// 解密由base64编码的私钥
+			byte[] bytes = Base64.decode(privateKey);
+			// 构造PKCS8EncodedKeySpec对象
+			PKCS8EncodedKeySpec pkcs = new PKCS8EncodedKeySpec(bytes);
+			// 指定的加密算法
+			KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
+			// 取私钥对象
+			PrivateKey key = factory.generatePrivate(pkcs);
+			// 用私钥对信息生成数字签名
+			Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
+			signature.initSign(key);
+			signature.update(data);
+			str = Base64.encode(signature.sign());
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return str;
+	}
+
+	/**
+	 * 校验数字签名
+	 *
+	 * @param data      加密数据
+	 * @param publicKey 公钥
+	 * @param sign      数字签名
+	 * @return 校验成功返回true,失败返回false
+	 */
+	public static boolean verify(byte[] data, String publicKey, String sign) {
+		boolean flag = false;
+		try {
+			// 解密由base64编码的公钥
+			byte[] bytes = Base64.decode(publicKey);
+			// 构造X509EncodedKeySpec对象
+			X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes);
+			// 指定的加密算法
+			KeyFactory factory = KeyFactory.getInstance(RSA_ALGORITHM);
+			// 取公钥对象
+			PublicKey key = factory.generatePublic(keySpec);
+			// 用公钥验证数字签名
+			Signature signature = Signature.getInstance(KEY_RSA_SIGNATURE);
+			signature.initVerify(key);
+			signature.update(data);
+			flag = signature.verify(Base64.decode(sign));
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return flag;
+	}
+
+	@SneakyThrows
+	public static void main(String[] args) {
+		/****先给调用方分配一组RSA密钥和一个appId****/
+		//初始化RSA密钥
+		Map<String, Object> init = RSAEncryptUtil.generateKeyPairToMap();
+		//私钥
+		String privateKey = RSAEncryptUtil.getPrivateKey(init);
+		//公钥
+		String publicKey = RSAEncryptUtil.getPublicKey(init);
+		/****先给调用方分配一组RSA密钥****/
+
+		/*****调用方(请求方)*****/
+		//业务参数
+		Map<String, Object> businessParams = new HashMap<>();
+		businessParams.put("name", "Longer");
+		businessParams.put("job", "程序猿");
+		businessParams.put("hobby", "打篮球");
+
+		JsonRequest jsonRequest = new JsonRequest();
+		jsonRequest.setTimestamp(System.currentTimeMillis());
+
+		//请求的业务参数进行加密
+		String body = "";
+		try {
+			body = AESEncryptUtil.encrypt(JSONObject.toJSONString(businessParams));
+		} catch (Exception e) {
+			throw new RuntimeException("报文加密异常", e);
+		}
+		jsonRequest.setBody(body);
+		//签名
+		Map<String, Object> paramMap = RSAEncryptUtil.bean2Map(jsonRequest);
+		paramMap.remove("sign");
+		// 参数排序
+		Map<String, Object> sortedMap = RSAEncryptUtil.sort(paramMap);
+		// 拼接参数:key1Value1key2Value2
+		String urlParams = RSAEncryptUtil.groupStringParam(sortedMap);
+		//私钥签名
+		String sign = RSAEncryptUtil.sign(urlParams.getBytes(), privateKey);
+		jsonRequest.setSign(sign);
+		//方便postman测试
+		String json = "{\"data\":\"35HbQ6r+q2IKjAM5gI0J5T21TJMK52tPbw73xc9oUv4Vt11a1b/6Cs3+Dy6rOP8dj+1I0OvZWNXDNqi2L2Uti6H3cvSHX8iBjBvJmofoMIojTYhUlMuGR4LoXoc1LItn3v8HiosCUUjd4an18C590dORIn00J/qLzPnlM/lREMrUz39k2bQ4ZamGkkf+jZYniyQCkU7KY17QSSc7SrLbct8lueFXNkmfMwVXIafFC4GIDcZvdFs7hbhS9e+82PA4PK03Mw+TeA4bz0sru3Gi+SV2e/gQcVO5pzXgIvedcIwqhx0SkKEdyAqXsNjSCYVI8gqk+N4gI2zOJkRHb3KIQQ==\"}";
+		String key = "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJXvQX5cym1RFoY3TlcOr8tO1MX9pAmCe3gq6QAy2/+lqaxzzu+Sb1xMrlqm8ByLwI9qPNZv212RsFeB0G0eAs2Lg3Mxj1L5KDNBmanWOXHfptvGWY7tEQxPmFVkKnmFgEnTfY2lL7OlWMXXulaKhHC7+7XgfbXvvCvc+qcLItURAgMBAAECgYAC9dxnMFHW4rj9IW5sjZ+u9fmBh0aL4YWqk75SAK8TVyQIgajeVhfYzLLMm2s2RRQiQMi+uyqrqErBD5Eb9nG7sVFbILaugQ5VQc2D6QAjHJq3FIug13IZiI3E0zVYRI0m5Kmka+ZYnpZayYZ6kHnWN8egPbAXwkcm2BXQsuusIQJBAPKvFd8Sk0AVHK+I1q2BcFDyM2sn6YB0m2f/EFovDu/kCtVUY0SOUIZdGVxfuDn8RgLbjKwN+rY/s5uj9jFH+30CQQCeKViPdWLKttWnR3wCVEZB+sEIwQA3Z592+FqbjlB9ltEIV6PwgNbeS7kqyDOwmdW6vT0fy6MD4+n20mGSKiwlAkEA3hdtJKCTMWogvmO0U0TJzMpaVA5KmswcDKhYBrBaMx+26lQ4JF05RD2fH/JZXneIesUBj9ObRX42mJEMlT0S4QJAJeJddi919HyK/MCgsaYiFhfMOR6RwLOvfL3MZkpWeV6wv0DO4gLadPDKb7b4uqp+iXZet8j9SSdEmXyXJn23iQJBAKDTkhFPXPQeNlA3kgy3/Bjn8ljyZf8jmx+JN7ZnwlAnKjIv6A5cyasV1VQJPSt6kQ7yCUONzL/ZXpgFOm4MBOc=\n";
+		String rsaSign = RSAEncryptUtil.sign(json.getBytes(), key);
+
+
+		/*****调用方(请求方)*****/
+
+		/*****接收方(自己的系统)*****/
+		//参数判空(略)
+		//appId校验(略)
+		//本条请求的合法性校验《唯一不重复请求;时间合理》(略)
+		//验签
+		Map<String, Object> paramMap2 = RSAEncryptUtil.bean2Map(jsonRequest);
+		paramMap2.remove("sign");
+		//参数排序
+		Map<String, Object> sortedMap2 = RSAEncryptUtil.sort(paramMap2);
+		//拼接参数:key1Value1key2Value2
+		String urlParams2 = RSAEncryptUtil.groupStringParam(sortedMap2);
+		//签名验证
+		boolean verify = RSAEncryptUtil.verify(urlParams2.getBytes(), publicKey, jsonRequest.getSign());
+		if (!verify) {
+			throw new RuntimeException("签名验证失败");
+		}
+		if (!StringUtils.isEmpty(jsonRequest.getBody())) {
+			// 解密请求报文
+			String requestBody = "";
+			try {
+				requestBody = AESEncryptUtil.decrypt(jsonRequest.getBody());
+			} catch (Exception e) {
+				throw new RuntimeException("请求参数解密异常");
+			}
+			System.out.println("业务参数解密结果:" + requestBody);
+		}
+		/*****接收方(自己的系统)*****/
+	}
+}

+ 42 - 0
ldt-core/src/main/java/org/springblade/common/utils/encrypt/RequestUtil.java

@@ -0,0 +1,42 @@
+package org.springblade.common.utils.encrypt;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName RequestUtil.java
+ * @Description 请求工具类
+ * @createTime 2021年11月02日 16:01:09
+ */
+public class RequestUtil {
+
+
+    public static String ReadAsChars2(HttpServletRequest request) {
+        InputStream is = null;
+        StringBuilder sb = new StringBuilder();
+        try {
+            is = request.getInputStream();
+
+            byte[] b = new byte[4096];
+            for (int n; (n = is.read(b)) != -1; ) {
+                sb.append(new String(b, 0, n));
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (null != is) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        return sb.toString();
+
+    }
+
+}

+ 55 - 0
ldt-core/src/main/java/org/springblade/common/utils/encrypt/ResponseUtil.java

@@ -0,0 +1,55 @@
+package org.springblade.common.utils.encrypt;
+
+
+import com.alibaba.fastjson.JSONObject;
+import org.springblade.common.config.ApiEncryptConfig;
+import org.springblade.core.tool.api.R;
+import org.springframework.util.ObjectUtils;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName ResponseUtil.java
+ * @Description 响应工具类
+ * @createTime 2021年11月02日 16:01:09
+ */
+public class ResponseUtil {
+
+	//失败: result必须为null
+	public static void toResponseEncryptWriter(HttpServletResponse response, R apiResponse, Object result) {
+		if (response == null) {
+			return;
+		}
+		response.setContentType("application/json;charset=UTF-8");
+		try {
+			PrintWriter writer = response.getWriter();
+			//需要加密
+			if (ApiEncryptConfig.responseStatic) {
+				if (ObjectUtils.isEmpty(result)) {
+					writer.write(JSONObject.toJSONString(apiResponse));
+				} else {
+					String json = JSONObject.toJSONString(result);
+					System.out.println("返回值加密前===" + json);
+					String encrypt1 = AESEncryptUtil.encrypt(json).replaceAll(System.getProperty("line.separator"), "");
+					apiResponse.setData(encrypt1);
+
+					writer.write(JSONObject.toJSONString(apiResponse));
+				}
+
+			} else {
+				apiResponse.setData(result);
+				writer.write(JSONObject.toJSONString(apiResponse));
+			}
+			writer.flush();
+			writer.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+
+	}
+
+}

+ 22 - 10
ldt-core/src/main/java/org/springblade/gateway/client_gateway/controller/ClientTradeController.java

@@ -17,6 +17,7 @@ import org.springblade.ldt.user.service.ILoginUserService;
 import org.springblade.payment.entity.SuccessParams;
 import org.springblade.payment.handle.Trade;
 import org.springblade.payment.handle.entity.Order;
+import org.springblade.payment.handle.handler.CancelTradeHandle;
 import org.springframework.util.Assert;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
@@ -41,13 +42,16 @@ public class ClientTradeController {
 	private IBillsService billsService;
 	private Trade trade;
 	private IAppShopService appShopService;
+	private CancelTradeHandle cancelTradeHandle;
 
 	@ApiOperation("用户扫商家二维码下单")
 	@PostMapping("/scanPay")
-	public R scanPay(@RequestBody ClientTradeDto clientTradeDto){
+	public R scanPay(@RequestBody ClientTradeDto clientTradeDto) {
 
 		LoginUser loginUser = loginUserService.getById(clientTradeDto.getUserId());
-		Assert.notNull(loginUser,() -> {throw  new ServiceException(ResCode.USER_NOT_FOUNT);});
+		Assert.notNull(loginUser, () -> {
+			throw new ServiceException(ResCode.USER_NOT_FOUNT);
+		});
 
 		//处理订单
 		Order order = Order.builder()
@@ -61,20 +65,27 @@ public class ClientTradeController {
 			.openId(loginUser.getOpenid())
 			.build();
 
-		SuccessParams successParams =null;
+		SuccessParams successParams = null;
 
 		try {
-		 successParams= trade.tradeForScanPay(order,clientTradeDto.getChannelId());
+			successParams = trade.tradeForScanPay(order, clientTradeDto.getChannelId());
 		} catch (Exception e) {
 			e.printStackTrace();
-			throw  new ServiceException(ResCode.TRADE_ERROR);
+			throw new ServiceException(ResCode.TRADE_ERROR);
 		}
 		return R.data(successParams);
 	}
 
+	@ApiOperation("取消支付交易")
+	@PostMapping("/cancelTrade")
+	public R cancelTrade(@RequestBody SuccessParams successParams) {
+		cancelTradeHandle.handle(successParams);
+		return R.status(true);
+	}
+
 	@ApiOperation("用户扫商家二维码微信下单")
 	@PostMapping("/scanPayByWeChat")
-	public R scanPayByWeChat(@RequestBody ClientTradeDto clientTradeDto){
+	public R scanPayByWeChat(@RequestBody ClientTradeDto clientTradeDto) {
 		String tenantId = appShopService.getTenantId(clientTradeDto.getShopId());
 
 		Bills bills = new Bills();
@@ -95,11 +106,12 @@ public class ClientTradeController {
 		bills.setTenantId(tenantId);
 
 
-
-		Assert.isTrue(billsService.saveOrUpdate(bills),()->{throw new ServiceException("订单异常!");});
+		Assert.isTrue(billsService.saveOrUpdate(bills), () -> {
+			throw new ServiceException("订单异常!");
+		});
 
 		//支付参数
-		SuccessParams successParams  = SuccessParams.builder()
+		SuccessParams successParams = SuccessParams.builder()
 			.orderType(OrderType.USER_PAY.name())
 			.status(AppConstant.BillPayStatus.待付款.name())
 			.totalPrice(bills.getTotalPrice())
@@ -108,7 +120,7 @@ public class ClientTradeController {
 			.tenantId(bills.getTenantId())
 			.build();
 
-		PaymentCache.putSuccessParams(Convert.toStr(successParams.getBills().getId()),successParams);
+		PaymentCache.putSuccessParams(Convert.toStr(successParams.getBills().getId()), successParams);
 		return R.data(bills);
 	}
 

+ 3 - 0
ldt-core/src/main/java/org/springblade/gateway/web_gateway/controller/PressureTestController.java

@@ -6,6 +6,8 @@ import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
+import org.springblade.common.aop.core.DecryptRequestBody;
+import org.springblade.common.aop.core.EncryptResponse;
 import org.springblade.common.cache.PaymentCache;
 import org.springblade.common.enums.*;
 import org.springblade.core.log.exception.ServiceException;
@@ -113,6 +115,7 @@ public class PressureTestController {
 		return R.data(res);
 	}
 
+	//@EncryptResponse
 	@PostMapping("/scanPay")
 	public R scanPay(@RequestBody ClientTradeDto clientTradeDto) {
 		/*ClientTradeDto clientTradeDto = new ClientTradeDto();

+ 32 - 24
ldt-core/src/main/java/org/springblade/ldt/bills/entity/FrozenRec.java

@@ -16,13 +16,16 @@
  */
 package org.springblade.ldt.bills.entity;
 
-import java.math.BigDecimal;
 import com.baomidou.mybatisplus.annotation.TableName;
-import org.springblade.core.mp.base.BaseEntity;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import org.springblade.common.enums.FrozenSatus;
+import org.springblade.common.enums.FrozenType;
+import org.springblade.core.mp.base.BaseEntity;
+
+import java.math.BigDecimal;
 
 /**
  * 实体类
@@ -39,30 +42,35 @@ public class FrozenRec extends BaseEntity {
 	private static final long serialVersionUID = 1L;
 
 	/**
-	* 冻结账户类型
-	*/
-		@ApiModelProperty(value = "冻结账户类型")
-		private String frozenType;
+	 * 冻结账户类型
+	 */
+	@ApiModelProperty(value = "冻结账户类型")
+	private FrozenType frozenType;
 	/**
-	* 冻结金额
-	*/
-		@ApiModelProperty(value = "冻结金额")
-		private BigDecimal frozenNum;
+	 * 冻结金额
+	 */
+	@ApiModelProperty(value = "冻结金额")
+	private BigDecimal frozenNum;
 	/**
-	* 冻结前金额
-	*/
-		@ApiModelProperty(value = "冻结前金额")
-		private BigDecimal beforeNum;
+	 * 冻结前金额
+	 */
+	@ApiModelProperty(value = "冻结前金额")
+	private BigDecimal beforeNum;
 	/**
-	* 订单号
-	*/
-		@ApiModelProperty(value = "订单号")
-		private Long tradeNo;
+	 * 订单号
+	 */
+	@ApiModelProperty(value = "订单号")
+	private Long tradeNo;
 	/**
-	* 冻结用户id
-	*/
-		@ApiModelProperty(value = "冻结用户id")
-		private Long userId;
+	 * 冻结用户id
+	 */
+	@ApiModelProperty(value = "冻结用户id")
+	private Long userId;
 
+	/**
+	 * 冻结状态
+	 */
+	@ApiModelProperty(value = "冻结状态")
+	private FrozenSatus frozenSatus;
 
 }

+ 2 - 1
ldt-core/src/main/java/org/springblade/ldt/bills/service/IFrozenRecService.java

@@ -16,6 +16,7 @@
  */
 package org.springblade.ldt.bills.service;
 
+import org.springblade.common.enums.FrozenType;
 import org.springblade.ldt.bills.entity.Bills;
 import org.springblade.ldt.bills.entity.FrozenRec;
 import org.springblade.ldt.bills.vo.FrozenRecVO;
@@ -43,5 +44,5 @@ public interface IFrozenRecService extends BaseService<FrozenRec> {
 	 */
 	IPage<FrozenRecVO> selectFrozenRecPage(IPage<FrozenRecVO> page, FrozenRecVO frozenRec);
 
-	boolean addFrozenRec(BigDecimal handlePrice, Bills bills, LoginUser user, String type, UserChannelPoint channelPoint);
+	boolean addFrozenRec(BigDecimal handlePrice, Bills bills, LoginUser user, FrozenType type, UserChannelPoint channelPoint);
 }

+ 6 - 3
ldt-core/src/main/java/org/springblade/ldt/bills/service/impl/FrozenRecServiceImpl.java

@@ -17,6 +17,8 @@
 package org.springblade.ldt.bills.service.impl;
 
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import org.springblade.common.enums.FrozenSatus;
+import org.springblade.common.enums.FrozenType;
 import org.springblade.core.mp.base.BaseServiceImpl;
 import org.springblade.ldt.bills.entity.Bills;
 import org.springblade.ldt.bills.entity.FrozenRec;
@@ -45,7 +47,7 @@ public class FrozenRecServiceImpl extends BaseServiceImpl<FrozenRecMapper, Froze
 	}
 
 	@Override
-	public boolean addFrozenRec(BigDecimal handlePrice, Bills bills, LoginUser user, String type, UserChannelPoint userChannelPoint) {
+	public boolean addFrozenRec(BigDecimal handlePrice, Bills bills, LoginUser user, FrozenType type, UserChannelPoint userChannelPoint) {
 
 		FrozenRec frozenRec = new FrozenRec();
 		frozenRec.setBeforeNum(user.getBalance());
@@ -54,13 +56,14 @@ public class FrozenRecServiceImpl extends BaseServiceImpl<FrozenRecMapper, Froze
 		frozenRec.setTradeNo(bills.getId());
 		frozenRec.setFrozenType(type);
 		frozenRec.setStatus(1);
+		frozenRec.setFrozenSatus(FrozenSatus.SUCCEED_FROZEN);
 
-		if (Objects.equals(type, "point")) {
+		if (Objects.equals(type, FrozenType.POINT)) {
 			user.setChannelPoint(user.getChannelPoint().subtract(handlePrice));
 			user.setFrozenPoint(user.getFrozenPoint().add(handlePrice));
 			userChannelPoint.setAvailable(userChannelPoint.getAvailable().subtract(handlePrice));
 		}
-		if (Objects.equals(type, "balance")) {
+		if (Objects.equals(type, FrozenType.BALANCE)) {
 			user.setBalance(user.getBalance().subtract(handlePrice));
 			user.setFrozenBalance(user.getFrozenBalance().add(handlePrice));
 		}

+ 1 - 2
ldt-core/src/main/java/org/springblade/payment/callback/trade/UserPayCS.java

@@ -138,8 +138,7 @@ public class UserPayCS {
 		} catch (Exception e) {
 			log.error("清算回调异常:{}", e.getMessage());
 			e.printStackTrace();
-			//throw e;
-			throw new TradeException(ResCode.TRADE_ERROR, successParams);
+			throw e;
 		}
 	}
 

+ 65 - 51
ldt-core/src/main/java/org/springblade/payment/handle/Trade.java

@@ -2,6 +2,7 @@ package org.springblade.payment.handle;
 
 import cn.hutool.core.convert.Convert;
 import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.StringUtils;
 import org.springblade.common.cache.PaymentCache;
 import org.springblade.common.enums.AppConstant;
 import org.springblade.common.enums.OrderType;
@@ -17,6 +18,7 @@ import org.springblade.payment.event.UserPayCsEvent;
 import org.springblade.payment.handle.entity.HandleData;
 import org.springblade.payment.handle.entity.Order;
 import org.springblade.payment.handle.handler.*;
+import org.springblade.payment.listener.sender.OrderSend;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
@@ -43,6 +45,7 @@ public class Trade {
 	private WxPayHandle wxPayHandle;
 	private IAppShopService appShopService;
 	private ApplicationEventPublisher applicationEventPublisher;
+	private OrderSend orderSend;
 
 	//
 //	@Transactional
@@ -100,65 +103,76 @@ public class Trade {
 //	 	return true;
 //	}
 	@Transactional(rollbackFor = Exception.class)
-	public SuccessParams tradeForScanPay(Order order, long channelId) throws Exception {
-		/**
-		 * 	交易处理链,每个handle处理负责一个业务节点
-		 * 	1、处理各种商家折扣,计算实际交易金额
-		 * 	2、用户渠道积分用以抵消交易金额
-		 * 	3、用户账户余额抵消交易金额
-		 * 	4、保存订单信息
-		 * */
-		List<BaseHandle> chain = new ArrayList() {{
-			add(discountHandle);
-			add(channelPointHandle);
-			add(balanceHandle);
-			add(wxPayHandle);
-		}};
+	public SuccessParams tradeForScanPay(Order order, long channelId) {
+		SuccessParams successParams = SuccessParams.builder().build();
+		try {
+			/**
+			 * 	交易处理链,每个handle处理负责一个业务节点
+			 * 	1、处理各种商家折扣,计算实际交易金额
+			 * 	2、用户渠道积分用以抵消交易金额
+			 * 	3、用户账户余额抵消交易金额
+			 * 	4、保存订单信息
+			 * */
+			List<BaseHandle> chain = new ArrayList() {{
+				add(discountHandle);
+				add(channelPointHandle);
+				add(balanceHandle);
+				add(wxPayHandle);
+			}};
 
-		BigDecimal remain = order.getMoney();
+			BigDecimal remain = order.getMoney();
 
-		//获取tenantId
-		String tenantId = appShopService.getTenantId(order.getShopId());
-		Bills bills = initBill(order, channelId, tenantId);
+			//获取tenantId
+			String tenantId = appShopService.getTenantId(order.getShopId());
+			Bills bills = initBill(order, channelId, tenantId);
 
-		synchronized (order.getLoginUser().getId().toString().intern()) {
-			//支付参数
-			SuccessParams successParams = SuccessParams.builder()
-				.orderType(OrderType.USER_PAY.name())
-				.status(AppConstant.BillPayStatus.待付款.name())
-				.userId(order.getLoginUser().getId())
-				.totalPrice(order.getMoney())
-				.bills(bills)
-				.shopId(order.getShopId())
-				.tenantId(tenantId)
-				.channelId(channelId)
-				.build();
+			synchronized (order.getLoginUser().getId().toString().intern()) {
+				//支付参数
+				successParams = SuccessParams.builder()
+					.orderType(OrderType.USER_PAY.name())
+					.status(AppConstant.BillPayStatus.待付款.name())
+					.userId(order.getLoginUser().getId())
+					.totalPrice(order.getMoney())
+					.bills(bills)
+					.shopId(order.getShopId())
+					.tenantId(tenantId)
+					.channelId(channelId)
+					.build();
 
-			//处理各个节点
-			for (BaseHandle node : chain) {
-				HandleData res = node.handle(remain, order, successParams);
-				Assert.isTrue(res.isSuccess(), () -> {
-					throw new TradeException(ResCode.TRADE_ERROR, res.getSuccessParams());
+				//处理各个节点
+				for (BaseHandle node : chain) {
+					HandleData res = node.handle(remain, order, successParams);
+					Assert.isTrue(res.isSuccess(), () -> {
+						throw new TradeException(ResCode.TRADE_ERROR, res.getSuccessParams());
+					});
+					remain = res.getRemain();
+					successParams = res.getSuccessParams();
+
+					if (remain.compareTo(BigDecimal.ZERO) == 0) {
+						successParams.setStatus(AppConstant.BillPayStatus.付款成功.name());
+						bills.setPayStatus(AppConstant.BillPayStatus.付款成功.name());
+						dataHandle.saveOrUpdateEntity(billsService, successParams.getBills());
+						applicationEventPublisher.publishEvent(new UserPayCsEvent(successParams));
+						break;
+					}
+				}
+
+				//新建事务,保存订单信息,
+				SuccessParams finalSuccessParams = successParams;
+				Assert.isTrue(dataHandle.saveOrUpdateEntity(billsService, successParams.getBills()), () -> {
+					throw new TradeException(ResCode.TRADE_ERROR, finalSuccessParams);
 				});
-				remain = res.getRemain();
-				successParams = res.getSuccessParams();
+				PaymentCache.putSuccessParams(Convert.toStr(successParams.getBills().getId()), successParams);
 
-				if (remain.compareTo(BigDecimal.ZERO) == 0) {
-					successParams.setStatus(AppConstant.BillPayStatus.付款成功.name());
-					bills.setPayStatus(AppConstant.BillPayStatus.付款成功.name());
-					dataHandle.saveOrUpdateEntity(billsService, successParams.getBills());
-					applicationEventPublisher.publishEvent(new UserPayCsEvent(successParams));
-					break;
+				//判断是否是待付款
+				if(StringUtils.equals(successParams.getStatus(),AppConstant.BillPayStatus.待付款.name())){
+					//发送消息队列
+					orderSend.sendOrderMessage(successParams);
 				}
+				return successParams;
 			}
-
-			//新建事务,保存订单信息,
-			SuccessParams finalSuccessParams = successParams;
-			Assert.isTrue(dataHandle.saveOrUpdateEntity(billsService, successParams.getBills()), () -> {
-				throw new TradeException(ResCode.TRADE_ERROR, finalSuccessParams);
-			});
-			PaymentCache.putSuccessParams(Convert.toStr(successParams.getBills().getId()), successParams);
-			return successParams;
+		} catch (Exception e) {
+			throw new TradeException(ResCode.TRADE_ERROR, successParams);
 		}
 
 	}

+ 3 - 3
ldt-core/src/main/java/org/springblade/payment/handle/handler/BalanceHandle.java

@@ -2,10 +2,10 @@ package org.springblade.payment.handle.handler;
 
 import lombok.AllArgsConstructor;
 import org.springblade.common.enums.AppConstant;
+import org.springblade.common.enums.FrozenType;
 import org.springblade.common.enums.OrderType;
 import org.springblade.common.enums.ResCode;
 import org.springblade.common.utils.TradeException;
-import org.springblade.core.log.exception.ServiceException;
 import org.springblade.ldt.bills.entity.BalanceBills;
 import org.springblade.ldt.bills.service.IBalanceBillsService;
 import org.springblade.ldt.bills.service.IFrozenRecService;
@@ -46,7 +46,7 @@ public class BalanceHandle implements BaseHandle {
 	}
 
 	@Override
-	public HandleData handle(BigDecimal remain, Order order, SuccessParams successParams) throws ServiceException {
+	public HandleData handle(BigDecimal remain, Order order, SuccessParams successParams) throws TradeException {
 
 		LoginUser user = order.getLoginUser();
 		BigDecimal myBalance = user.getBalance();
@@ -69,7 +69,7 @@ public class BalanceHandle implements BaseHandle {
 			handlePrice = myBalance;
 		}
 
-		Assert.isTrue(frozenRecService.addFrozenRec(handlePrice, successParams.getBills(), user, "balance", null), () -> {
+		Assert.isTrue(frozenRecService.addFrozenRec(handlePrice, successParams.getBills(), user, FrozenType.BALANCE, null), () -> {
 			throw new TradeException(ResCode.TRADE_ERROR, successParams);
 		});
 		BalanceBills balanceBills = buildBills(order, user, handlePrice, successParams, BigDecimal.ZERO);

+ 88 - 0
ldt-core/src/main/java/org/springblade/payment/handle/handler/CancelTradeHandle.java

@@ -0,0 +1,88 @@
+package org.springblade.payment.handle.handler;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.ObjectUtils;
+import org.springblade.common.enums.AppConstant;
+import org.springblade.common.enums.FrozenSatus;
+import org.springblade.common.enums.FrozenType;
+import org.springblade.core.log.exception.ServiceException;
+import org.springblade.ldt.bills.entity.BalanceBills;
+import org.springblade.ldt.bills.entity.Bills;
+import org.springblade.ldt.bills.entity.FrozenRec;
+import org.springblade.ldt.bills.entity.PointBills;
+import org.springblade.ldt.bills.service.IBalanceBillsService;
+import org.springblade.ldt.bills.service.IBillsService;
+import org.springblade.ldt.bills.service.IFrozenRecService;
+import org.springblade.ldt.bills.service.IPointBillsService;
+import org.springblade.ldt.user.entity.LoginUser;
+import org.springblade.ldt.user.entity.UserChannelPoint;
+import org.springblade.ldt.user.service.ILoginUserService;
+import org.springblade.ldt.user.service.IUserChannelPointService;
+import org.springblade.payment.entity.SuccessParams;
+import org.springframework.stereotype.Component;
+
+import java.util.Objects;
+
+/**
+ * @author July
+ * @version 1.0.0
+ * @ClassName CancelTradeHandle.java
+ * @Description 取消交易处理
+ * @createTime 2021年11月01日 15:14:00
+ */
+@Component
+@AllArgsConstructor
+public class CancelTradeHandle {
+
+	private ILoginUserService loginUserService;
+	private IBillsService billsService;
+	private IPointBillsService pointBillsService;
+	private IBalanceBillsService balanceBillsService;
+	private IFrozenRecService frozenRecService;
+	private IUserChannelPointService userChannelPointService;
+
+	public void handle(SuccessParams successParams) throws ServiceException {
+		Bills bills = successParams.getBills();
+		synchronized (bills.getId().toString().intern()) {
+			FrozenRec frozenRec = frozenRecService.getOne(Wrappers.<FrozenRec>lambdaQuery().eq(FrozenRec::getTradeNo, bills.getId()));
+			UserChannelPoint channelPoint = userChannelPointService.getById(successParams.getChannelId());
+
+			//修改用户冻结余额或积分
+			LoginUser user = loginUserService.getById(bills.getPayId());
+			if (ObjectUtils.isNotEmpty(user) && ObjectUtils.isNotEmpty(frozenRec)) {
+				frozenRec.setFrozenSatus(FrozenSatus.RETURNED_FROZEN);
+				if (Objects.equals(frozenRec.getFrozenType(), FrozenType.POINT)) {
+					user.setChannelPoint(user.getChannelPoint().add(frozenRec.getFrozenNum()));
+					user.setFrozenPoint(user.getFrozenPoint().subtract(frozenRec.getFrozenNum()));
+					if (ObjectUtils.isNotEmpty(channelPoint)) {
+						channelPoint.setAvailable(channelPoint.getAvailable().add(frozenRec.getFrozenNum()));
+						userChannelPointService.saveOrUpdate(channelPoint);
+					}
+				} else if (Objects.equals(frozenRec.getFrozenType(), FrozenType.BALANCE)) {
+					user.setBalance(user.getBalance().add(frozenRec.getFrozenNum()));
+					user.setFrozenBalance(user.getFrozenBalance().subtract(frozenRec.getFrozenNum()));
+				}
+			}
+
+			if (ObjectUtils.isNotEmpty(frozenRec)) {
+				frozenRecService.saveOrUpdate(frozenRec);
+			}
+			loginUserService.saveOrUpdate(user);
+		}
+		bills.setPayStatus(AppConstant.BillPayStatus.取消付款.name());
+		billsService.saveOrUpdate(bills);
+
+		PointBills pointBills = pointBillsService.getById(successParams.getPointBillsId());
+		if (ObjectUtils.isNotEmpty(pointBills)) {
+			pointBills.setPayStatus(AppConstant.BillPayStatus.取消付款.name());
+			pointBillsService.saveOrUpdate(pointBills);
+		}
+
+		BalanceBills balanceBills = balanceBillsService.getById(successParams.getBalanceBillsId());
+		if (ObjectUtils.isNotEmpty(balanceBills)) {
+			balanceBills.setPayStatus(AppConstant.BillPayStatus.取消付款.name());
+			balanceBillsService.saveOrUpdate(balanceBills);
+		}
+	}
+}

+ 3 - 3
ldt-core/src/main/java/org/springblade/payment/handle/handler/ChannelPointHandle.java

@@ -3,10 +3,10 @@ package org.springblade.payment.handle.handler;
 import lombok.AllArgsConstructor;
 import org.springblade.common.cache.PlatformCache;
 import org.springblade.common.enums.AppConstant;
+import org.springblade.common.enums.FrozenType;
 import org.springblade.common.enums.OrderType;
 import org.springblade.common.enums.ResCode;
 import org.springblade.common.utils.TradeException;
-import org.springblade.core.log.exception.ServiceException;
 import org.springblade.ldt.bills.entity.PointBills;
 import org.springblade.ldt.bills.service.IFrozenRecService;
 import org.springblade.ldt.bills.service.IPointBillsService;
@@ -45,7 +45,7 @@ public class ChannelPointHandle implements BaseHandle {
 	 * 不足抵消,remain将会流转到下一个节点处理
 	 */
 	@Override
-	public HandleData handle(BigDecimal remain, Order order, SuccessParams successParams) throws ServiceException {
+	public HandleData handle(BigDecimal remain, Order order, SuccessParams successParams) throws TradeException {
 
 		BigDecimal pointRate = BigDecimal.valueOf(PlatformCache.platFormConfig().getPointFee());
 
@@ -77,7 +77,7 @@ public class ChannelPointHandle implements BaseHandle {
 			throw new TradeException(ResCode.TRADE_ERROR, successParams);
 		});
 
-		Assert.isTrue(frozenRecService.addFrozenRec(handlePrice, successParams.getBills(), user, "point", channelPoint), () -> {
+		Assert.isTrue(frozenRecService.addFrozenRec(handlePrice, successParams.getBills(), user, FrozenType.POINT, channelPoint), () -> {
 			throw new TradeException(ResCode.TRADE_ERROR, successParams);
 		});
 		Assert.isTrue(dataHandle.saveOrUpdateEntity(loginUserService, user), () -> {

+ 2 - 1
ldt-core/src/main/java/org/springblade/payment/handle/handler/DiscountHandle.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import lombok.AllArgsConstructor;
 import org.springblade.common.cache.PlatformCache;
 import org.springblade.common.config.entity.PlatformConfig;
+import org.springblade.common.utils.TradeException;
 import org.springblade.core.log.exception.ServiceException;
 import org.springblade.ldt.shop.entity.Shop;
 import org.springblade.ldt.shop.service.IShopService;
@@ -33,7 +34,7 @@ public class DiscountHandle implements BaseHandle {
 	private IJoinRecordService joinRecordService;
 
 	@Override
-	public HandleData handle(BigDecimal remain, Order order, SuccessParams successParams) throws ServiceException {
+	public HandleData handle(BigDecimal remain, Order order, SuccessParams successParams) throws TradeException {
 		PlatformConfig platformConfig = PlatformCache.platFormConfig();
 
 		//获取相关商家、活动、参与情况

+ 65 - 0
ldt-core/src/main/java/org/springblade/payment/listener/consumer/OrderListener.java

@@ -0,0 +1,65 @@
+package org.springblade.payment.listener.consumer;
+
+import com.alibaba.fastjson.JSONObject;
+import com.rabbitmq.client.Channel;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springblade.common.constant.OrderConstant;
+import org.springblade.common.enums.AppConstant;
+import org.springblade.ldt.bills.entity.Bills;
+import org.springblade.ldt.bills.service.IBillsService;
+import org.springblade.payment.entity.SuccessParams;
+import org.springblade.payment.handle.handler.CancelTradeHandle;
+import org.springframework.amqp.core.Message;
+import org.springframework.amqp.rabbit.annotation.RabbitHandler;
+import org.springframework.amqp.rabbit.annotation.RabbitListener;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.io.IOException;
+
+/**
+ * @Author: Silent
+ * @Description
+ * @Date: Created in 16:15 2021/11/1
+ * @Modified By:
+ */
+@Component
+@Slf4j
+public class OrderListener {
+	@Autowired
+	private CancelTradeHandle cancelTradeHandle;
+
+	@Autowired
+	private IBillsService billsService;
+
+	/**
+	 * 接收消息
+	 */
+	@RabbitHandler
+	@RabbitListener(queues = OrderConstant.DLX_ORDER_QUEUE)
+	public void listen(String successParamsJSON,Message message, Channel channel) throws IOException {
+		log.info("person 接收到消息=[{}]", message.toString());
+		try {
+			//解析参数
+			SuccessParams successParams = JSONObject.parseObject(successParamsJSON, SuccessParams.class);
+			//获取账单信息
+			Bills bills = billsService.getById(successParams.getBills().getId());
+			//判断支付状态
+			if(ObjectUtils.isNotEmpty(bills) && StringUtils.equals(bills.getPayStatus(), AppConstant.BillPayStatus.待付款.name())){
+				//发送取消信息队列
+				cancelTradeHandle.handle(successParams);
+			}
+			channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
+		} catch (IOException e) {
+			//失败后重新放回队列  不推荐,如果服务因为某种暂时无法恢复原因失败,那么会一直循环回归队列--->消费失败
+			// channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
+			// 丢弃消息
+			//channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
+			//失败后消息被确认,
+			channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
+			log.error("订单消息错误:{}", e.getMessage());
+		}
+	}
+}

+ 60 - 0
ldt-core/src/main/java/org/springblade/payment/listener/sender/OrderSend.java

@@ -0,0 +1,60 @@
+package org.springblade.payment.listener.sender;
+
+import cn.hutool.core.util.IdUtil;
+import com.alibaba.fastjson.JSONObject;
+import lombok.extern.slf4j.Slf4j;
+import org.springblade.common.constant.OrderConstant;
+import org.springblade.payment.entity.SuccessParams;
+import org.springframework.amqp.core.MessageProperties;
+import org.springframework.amqp.rabbit.connection.CorrelationData;
+import org.springframework.amqp.rabbit.core.RabbitTemplate;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * @Author: Silent
+ * @Description
+ * @Date: Created in 16:36 2021/11/1
+ * @Modified By:
+ */
+@Component
+@Slf4j
+public class OrderSend implements RabbitTemplate.ConfirmCallback {
+	@Autowired
+	private RabbitTemplate rabbitTemplate;
+
+	/**
+	 * 发送订单消息
+	 * @param successParams
+	 * @return
+	 */
+	public boolean sendOrderMessage(SuccessParams successParams){
+		//发送超时队列消息
+		rabbitTemplate.convertAndSend(OrderConstant.TTL_ORDER_EXCHANGE, OrderConstant.TTL_ROUTE_KEY, JSONObject.toJSONString(successParams), message->{
+			MessageProperties messageProperties = message.getMessageProperties();
+			//设置消息延迟发送时间
+			//给消息设置延迟毫秒值
+			messageProperties.setExpiration(String.valueOf(OrderConstant.ORDER_DELAY_TIME_OUT));
+			messageProperties.setDelay((int) OrderConstant.ORDER_DELAY_TIME_OUT);
+			return message;
+		},new CorrelationData(IdUtil.randomUUID())); //全局唯一ID
+		log.info("订单消息发送:{}",successParams);
+		return true;
+	}
+
+	/**
+	 * 确认回调
+	 * @param correlationData
+	 * @param ack
+	 * @param cause
+	 */
+	@Override
+	public void confirm(CorrelationData correlationData, boolean ack, String cause) {
+		System.out.println("消息id:" + correlationData.getId());
+		if (ack) {
+			log.info("消息发送确认成功");
+		} else {
+			log.error("消息发送确认失败:{}",cause);
+		}
+	}
+}

+ 21 - 1
ldt-core/src/main/java/org/springblade/payment/plugin/YeePayPlugin.java

@@ -1,15 +1,22 @@
 package org.springblade.payment.plugin;
 
 import cn.hutool.core.convert.Convert;
+import cn.hutool.core.date.DateField;
+import cn.hutool.core.date.DateTime;
 import cn.hutool.core.lang.Assert;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.yeepay.yop.sdk.service.common.response.YopResponse;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.springblade.common.cache.PaymentCache;
+import org.springblade.common.enums.AppConstant;
 import org.springblade.common.enums.OrderType;
 import org.springblade.common.enums.PaymentType;
+import org.springblade.common.enums.ResCode;
+import org.springblade.common.utils.TradeException;
 import org.springblade.core.log.logger.BladeLogger;
 import org.springblade.core.tool.api.R;
 import org.springblade.core.tool.utils.DateUtil;
@@ -61,7 +68,6 @@ public class YeePayPlugin implements Payment {
 	IAgentLeagueRecordService agentLeagueRecordService;
 	BladeLogger bladeLogger;
 
-
 	@Override
 	public R nativePay(PayParam payParam) {
 		Bills bills = billsService.getById(payParam.getOrderId());
@@ -131,6 +137,20 @@ public class YeePayPlugin implements Payment {
 
 	@Override
 	public R miniProgram(PayParam payParam) {
+		Bills bills = billsService.getById(payParam.getOrderId());
+		//判断订单是否超时(默认90s)
+		if(ObjectUtils.isEmpty(bills)){
+			//判断支付状态
+			if(StringUtils.equals(bills.getPayStatus(), AppConstant.BillPayStatus.待付款.name())){
+				//判断超时时间
+				if(DateTime.now().getTime() >
+					DateTime.of(bills.getCreateTime()).offset(DateField.SECOND,90).getTime()){
+					SuccessParams successParams = PaymentCache.getSuccessParams(payParam.getOrderId());
+					throw new TradeException(ResCode.TRADE_TIME_OUT, successParams);
+				}
+			}
+		}
+
 
 		InitOrderDto initOrderDto = InitOrderDto.builder()
 			.payWay(YeepayApiConstant.payWayType.MINI_PROGRAM.name())

+ 21 - 1
ldt-core/src/main/resources/application-dev.yml

@@ -4,6 +4,27 @@ spring:
     host: 192.168.1.218
     port: 5672
     virtual-host: ldt
+    connection-timeout: 15000
+    # 发送确认
+    publisher-confirm-type: simple
+    # 路由失败回调
+    publisher-returns: true
+    template:
+      # 必须设置成true 消息路由失败通知监听者,而不是将消息丢弃
+      mandatory: true
+    listener:
+      simple:
+        # 每次从RabbitMQ获取的消息数量
+        prefetch: 1
+        default-requeue-rejected: false
+        # 每个队列启动的消费者数量
+        concurrency: 1
+        # 每个队列最大的消费者数量
+        max-concurrency: 1
+        # 手动签收ACK
+        acknowledge-mode: manual
+    username: guest
+    password: guest
   redis:
     ##redis 单机环境配置
     host: 192.168.1.218
@@ -59,4 +80,3 @@ blade:
     remote-mode: true
     upload-domain: http://localhost:8999
     remote-path: /usr/share/nginx/html
-

+ 1 - 4
ldt-core/src/main/resources/application.yml

@@ -157,7 +157,7 @@ social:
       client-secret: 233************************************
       redirect-uri: ${social.domain}/oauth/redirect/dingtalk
 
-#blade配置
+  #blade配置
 
   #token配置
   token:
@@ -252,6 +252,3 @@ social:
       - blade_dict_biz
       - ldt_activity
       - ldt_join_record
-
-
-