diff --git a/payment-base-app/pom.xml b/payment-base-app/pom.xml
index c8e4ea1..004e5c8 100644
--- a/payment-base-app/pom.xml
+++ b/payment-base-app/pom.xml
@@ -73,11 +73,18 @@
2.9.2
+
+
+ com.google.code.gson
+ gson
+ 2.2.4
+
+
com.github.wechatpay-apiv3
wechatpay-apache-httpclient
- 0.4.2
+ 0.2.2
@@ -93,6 +100,22 @@
spring-boot-starter-amqp
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+ org.redisson
+ redisson-spring-boot-starter
+ 3.15.1
+
+
+ com.alibaba
+ fastjson
+ 1.2.47
+
+
org.apache.httpcomponents
httpclient
diff --git a/payment-base-app/src/main/java/com/base/springcloud/aspect/annotation/BusinessException.java b/payment-base-app/src/main/java/com/base/springcloud/aspect/annotation/BusinessException.java
new file mode 100644
index 0000000..f5dff7a
--- /dev/null
+++ b/payment-base-app/src/main/java/com/base/springcloud/aspect/annotation/BusinessException.java
@@ -0,0 +1,29 @@
+package com.base.springcloud.aspect.annotation;
+
+import com.base.springcloud.constant.ErrorConstant;
+import lombok.Getter;
+
+@Getter
+public class BusinessException extends RuntimeException{
+
+ private String code;
+
+ /**
+ * 使用已有的错误类型
+ * @param type 枚举类中的错误类型
+ */
+ public BusinessException(ErrorConstant type){
+ super(type.getMessage());
+ this.code = type.getCode();
+ }
+
+ /**
+ * 自定义错误类型
+ * @param code 自定义的错误码
+ * @param msg 自定义的错误提示
+ */
+ public BusinessException(String code, String msg){
+ super(msg);
+ this.code = code;
+ }
+}
diff --git a/payment-base-app/src/main/java/com/base/springcloud/aspect/annotation/Rlock.java b/payment-base-app/src/main/java/com/base/springcloud/aspect/annotation/Rlock.java
new file mode 100644
index 0000000..7980e9c
--- /dev/null
+++ b/payment-base-app/src/main/java/com/base/springcloud/aspect/annotation/Rlock.java
@@ -0,0 +1,42 @@
+package com.base.springcloud.aspect.annotation;
+
+import java.lang.annotation.*;
+import java.util.concurrent.TimeUnit;
+
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface Rlock {
+
+ /**
+ * 分布式锁的key前缀
+ */
+ String prefix() default "redisLockPrefix::";
+
+ /**
+ * 分布式锁的key动态参数
+ */
+ String suffix() default "lockDynamicKey";
+
+ /**
+ * 等待时间 默认五秒
+ *
+ * @return
+ */
+ long waitTime() default 5;
+
+ /**
+ * 锁释放时间 默认十秒
+ *
+ * @return
+ */
+ long leaseTime() default 10;
+
+ /**
+ * 时间格式 默认:秒
+ *
+ * @return
+ */
+ TimeUnit timeUnit() default TimeUnit.SECONDS;
+}
\ No newline at end of file
diff --git a/payment-base-app/src/main/java/com/base/springcloud/aspect/aspect/BusinessExceptionHandler.java b/payment-base-app/src/main/java/com/base/springcloud/aspect/aspect/BusinessExceptionHandler.java
new file mode 100644
index 0000000..9e0c885
--- /dev/null
+++ b/payment-base-app/src/main/java/com/base/springcloud/aspect/aspect/BusinessExceptionHandler.java
@@ -0,0 +1,24 @@
+package com.base.springcloud.aspect.aspect;
+
+import com.base.springcloud.aspect.annotation.BusinessException;
+import com.base.springcloud.base.CommonResult;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@ControllerAdvice
+public class BusinessExceptionHandler {
+
+ /**
+ * @ExceptionHandler相当于controller的@RequestMapping
+ * 如果抛出的的是BusinessException,则调用该方法
+ * @param se 业务异常
+ * @return
+ */
+ @ExceptionHandler(BusinessException.class)
+ @ResponseBody
+ public CommonResult handle(BusinessException se){
+ return CommonResult.error(se.getCode(),se.getMessage());
+ }
+}
+
diff --git a/payment-base-app/src/main/java/com/base/springcloud/aspect/aspect/RlockAspect.java b/payment-base-app/src/main/java/com/base/springcloud/aspect/aspect/RlockAspect.java
new file mode 100644
index 0000000..9279a75
--- /dev/null
+++ b/payment-base-app/src/main/java/com/base/springcloud/aspect/aspect/RlockAspect.java
@@ -0,0 +1,118 @@
+package com.base.springcloud.aspect.aspect;
+
+import com.base.springcloud.aspect.annotation.Rlock;
+import com.base.springcloud.handle.RBSQueueHandler;
+import lombok.extern.slf4j.Slf4j;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.core.DefaultParameterNameDiscoverer;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.Expression;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.stereotype.Component;
+
+import java.lang.reflect.Method;
+
+@Aspect
+@Component
+@Slf4j
+public class RlockAspect {
+
+ Logger logger = LoggerFactory.getLogger(RBSQueueHandler.class);
+
+ @Autowired
+ private RedissonClient redissonClient;
+
+ /**
+ * 用于SpEL表达式解析.
+ */
+ private SpelExpressionParser parser = new SpelExpressionParser();
+ /**
+ * 用于获取方法参数定义名字.
+ */
+ private DefaultParameterNameDiscoverer nameDiscoverer = new DefaultParameterNameDiscoverer();
+
+
+
+ @Pointcut("@annotation(com.base.springcloud.aspect.annotation.Rlock)")
+ public void RlockAspect() {
+ }
+
+ @Around("RlockAspect()")
+ public Object arround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
+ Object object = null;
+ RLock lock = null;
+ try {
+ // 获取注解信息
+ Rlock rlockInfo = getRlockInfo(proceedingJoinPoint);
+ String key = getLocalKey(proceedingJoinPoint, rlockInfo);
+ // 根据名字获取锁实例
+ lock = redissonClient.getLock(key);
+ if (lock != null) {
+ final boolean status = lock.tryLock(rlockInfo.waitTime(), rlockInfo.leaseTime(), rlockInfo.timeUnit());
+ if (status) {
+ logger.info("锁获取成功,加锁成功 : {}........",key);
+ object = proceedingJoinPoint.proceed();
+ } else {
+ logger.info("锁被占用,加锁失败 : {} .........",key);
+ throw new RuntimeException("加锁失败");
+ }
+ }
+ } finally {
+ // 方法执行完成释放锁
+ if (lock != null && lock.isHeldByCurrentThread()) {
+ lock.unlock();
+ }
+ }
+ return object;
+ }
+
+ public Rlock getRlockInfo(ProceedingJoinPoint proceedingJoinPoint) {
+ MethodSignature methodSignature = (MethodSignature) proceedingJoinPoint.getSignature();
+ return methodSignature.getMethod().getAnnotation(Rlock.class);
+ }
+
+ /**
+ * @param proceedingJoinPoint
+ * @return
+ */
+ public String getLocalKey(ProceedingJoinPoint proceedingJoinPoint, Rlock rlockInfo) {
+ StringBuilder localKey = new StringBuilder();
+ //获取注解参数值
+ String prefix = rlockInfo.prefix();
+ String spELString = rlockInfo.suffix();
+ String key = generateKeyBySpEL(spELString,proceedingJoinPoint);
+ localKey.append(prefix).append(key);
+ logger.info("加锁 key :{}",localKey.toString());
+ return localKey.toString();
+ }
+
+ public String generateKeyBySpEL(String spELString, ProceedingJoinPoint joinPoint) {
+ // 通过joinPoint获取被注解方法
+ MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
+ Method method = methodSignature.getMethod();
+ // 使用spring的DefaultParameterNameDiscoverer获取方法形参名数组
+ String[] paramNames = nameDiscoverer.getParameterNames(method);
+ // 解析过后的Spring表达式对象
+ Expression expression = parser.parseExpression(spELString);
+ // spring的表达式上下文对象
+ EvaluationContext context = new StandardEvaluationContext();
+ // 通过joinPoint获取被注解方法的形参
+ Object[] args = joinPoint.getArgs();
+ // 给上下文赋值
+ for(int i = 0 ; i < args.length ; i++) {
+ context.setVariable(paramNames[i], args[i]);
+ }
+ // 表达式从上下文中计算出实际参数值
+ return expression.getValue(context).toString();
+ }
+}
\ No newline at end of file
diff --git a/payment-base-app/src/main/java/com/base/springcloud/base/CommonResult.java b/payment-base-app/src/main/java/com/base/springcloud/base/CommonResult.java
index e74764d..7344f08 100644
--- a/payment-base-app/src/main/java/com/base/springcloud/base/CommonResult.java
+++ b/payment-base-app/src/main/java/com/base/springcloud/base/CommonResult.java
@@ -23,4 +23,12 @@ public class CommonResult {
public CommonResult(String code, String message) {
this(code,message,null);
}
+
+ public static CommonResult error(String code, String msg){
+ CommonResult res = new CommonResult();
+ res.setCode(code);
+ res.setMessage(msg);
+ return res;
+ }
+
}
\ No newline at end of file
diff --git a/payment-base-app/src/main/java/com/base/springcloud/constant/ErrorConstant.java b/payment-base-app/src/main/java/com/base/springcloud/constant/ErrorConstant.java
index 0ecf6d1..d2d40fa 100644
--- a/payment-base-app/src/main/java/com/base/springcloud/constant/ErrorConstant.java
+++ b/payment-base-app/src/main/java/com/base/springcloud/constant/ErrorConstant.java
@@ -8,7 +8,7 @@ public enum ErrorConstant {
private String code;
private String message;
- private ErrorConstant(String code, String message) {
+ ErrorConstant(String code, String message) {
this.setCode(code);
this.setMessage(message);
}
@@ -28,12 +28,4 @@ public enum ErrorConstant {
public void setMessage(String message) {
this.message = message;
}
-
- @Override
- public String toString() {
- return "ErrorConstant{" +
- "code='" + code + '\'' +
- ", message='" + message + '\'' +
- '}';
- }
}
diff --git a/payment-base-app/src/main/java/com/base/springcloud/constant/FastJson2JsonRedisSerializer.java b/payment-base-app/src/main/java/com/base/springcloud/constant/FastJson2JsonRedisSerializer.java
new file mode 100644
index 0000000..1da92d8
--- /dev/null
+++ b/payment-base-app/src/main/java/com/base/springcloud/constant/FastJson2JsonRedisSerializer.java
@@ -0,0 +1,59 @@
+package com.base.springcloud.constant;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.parser.ParserConfig;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.TypeFactory;
+import org.springframework.data.redis.serializer.RedisSerializer;
+import org.springframework.data.redis.serializer.SerializationException;
+import org.springframework.util.Assert;
+
+import java.nio.charset.Charset;
+
+/**
+ * FastJson2JsonRedisSerializer
+ * Redis使用FastJson序列化
+ */
+public class FastJson2JsonRedisSerializer implements RedisSerializer {
+ private ObjectMapper objectMapper = new ObjectMapper();
+ public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
+
+ private Class clazz;
+
+ static {
+ ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
+ }
+
+ public FastJson2JsonRedisSerializer(Class clazz) {
+ super();
+ this.clazz = clazz;
+ }
+
+ public byte[] serialize(T t) throws SerializationException {
+ if (t == null) {
+ return new byte[0];
+ }
+ return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
+ }
+
+ public T deserialize(byte[] bytes) throws SerializationException {
+ if (bytes == null || bytes.length <= 0) {
+ return null;
+ }
+ String str = new String(bytes, DEFAULT_CHARSET);
+
+ return JSON.parseObject(str, clazz);
+ }
+
+ public void setObjectMapper(ObjectMapper objectMapper) {
+ Assert.notNull(objectMapper, "'objectMapper' must not be null");
+ this.objectMapper = objectMapper;
+ }
+
+ protected JavaType getJavaType(Class> clazz) {
+ return TypeFactory.defaultInstance().constructType(clazz);
+ }
+
+}
\ No newline at end of file
diff --git a/payment-base-app/src/main/java/com/base/springcloud/constant/PaymentConstant.java b/payment-base-app/src/main/java/com/base/springcloud/constant/PaymentConstant.java
index e9c52b2..b756fb1 100644
--- a/payment-base-app/src/main/java/com/base/springcloud/constant/PaymentConstant.java
+++ b/payment-base-app/src/main/java/com/base/springcloud/constant/PaymentConstant.java
@@ -21,5 +21,10 @@ public interface PaymentConstant {
String CP = "CP"; // 云闪付
String QRCCP = "QRCCP";
+ String NATIVE = "Native"; // Native支付
+ String H5 = "H5"; // H5支付
+ String JSAPI = "Jsapi"; // JSAPI支付
+
String ORDER_QUEUE = "order_queue"; // 订单队列
+ String ORDER_LOCK_PREFIX = "ORDER_KEY:"; // 订单锁前缀
}
diff --git a/payment-base-app/src/main/java/com/base/springcloud/constant/RedisConfig.java b/payment-base-app/src/main/java/com/base/springcloud/constant/RedisConfig.java
new file mode 100644
index 0000000..da09b29
--- /dev/null
+++ b/payment-base-app/src/main/java/com/base/springcloud/constant/RedisConfig.java
@@ -0,0 +1,58 @@
+package com.base.springcloud.constant;
+
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.redisson.Redisson;
+import org.redisson.config.Config;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+@Configuration
+@EnableCaching
+public class RedisConfig extends CachingConfigurerSupport {
+
+ @Value("${spring.redis.host}")
+ private String host;
+
+ @Value("${spring.redis.port}")
+ private String port;
+
+ @Value("${spring.redis.password}")
+ private String password;
+
+ @Bean
+ @SuppressWarnings(value = {"unchecked", "rawtypes"})
+ public RedisTemplate
-
- mysql
- mysql-connector-java
- 5.1.38
-
-
org.mybatis.generator