Browse Source

Merge remote-tracking branch 'origin/feature-1.1-bnyer-order' into feature-1.1-dev

feature-1.1
Penny 3 years ago
parent
commit
a145878e2c
  1. 9
      bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/R.java
  2. 23
      bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/ResponseEnum.java
  3. 140
      bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/MD5Util.java
  4. 39
      bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/UserVipRecordController.java
  5. 3
      bnyer-services/bnyer-img/src/main/java/com/bnyer/img/mapper/UserVipRecordMapper.java
  6. 26
      bnyer-services/bnyer-img/src/main/java/com/bnyer/img/query/UserVipRecordQuery.java
  7. 9
      bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipRecordService.java
  8. 31
      bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java
  9. 56
      bnyer-services/bnyer-img/src/main/java/com/bnyer/img/vo/UserVipRecordVo.java
  10. 30
      bnyer-services/bnyer-img/src/main/resources/com/bnyer/img/mapper/UserVipRecordMapper.xml
  11. 63
      bnyer-services/bnyer-order/src/main/java/com/bnyer/order/annotation/LimitRepeatRequest.java
  12. 150
      bnyer-services/bnyer-order/src/main/java/com/bnyer/order/aop/LimitRepeatRequestAspect.java
  13. 2
      bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/VipOrderController.java
  14. 63
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/annotation/LimitRepeatRequest.java
  15. 150
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/aop/LimitRepeatRequestAspect.java
  16. 16
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/bean/dto/RefundDto.java
  17. 3
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/bean/vo/UnifiedOrderVo.java
  18. 18
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/UnifiedPayController.java
  19. 4
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/factory/PayFactory.java
  20. 10
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/WxPayStrategy.java
  21. 20
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumVerificationKey.java
  22. 8
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/UnifiedPayService.java
  23. 10
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/UnifiedPayServiceImpl.java
  24. 39
      bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/PaymentRefundUtil.java
  25. 2
      pom.xml

9
bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/domain/R.java

@ -77,6 +77,15 @@ public class R<T> implements Serializable
return restResult(null,responseEnum.getCode(), responseEnum.getMsg()); return restResult(null,responseEnum.getCode(), responseEnum.getMsg());
} }
/**
* 构建重复提交错误消息
* @param time
* @return
*/
public R<T> buildRepeatRequest(int time){
return restResult(null,ResponseEnum.REPEAT_REQUEST_ERROR.getCode(),time+"分钟内"+ResponseEnum.REPEAT_REQUEST_ERROR.getMsg());
}
private static <T> R<T> restResult(T data, int code, String msg) private static <T> R<T> restResult(T data, int code, String msg)
{ {
R<T> apiResult = new R<>(); R<T> apiResult = new R<>();

23
bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/enums/ResponseEnum.java

@ -10,27 +10,20 @@ public enum ResponseEnum {
//======================系统异常========================, //======================系统异常========================,
SERVER_ERROR(500, "系统繁忙,请稍候重试!"), SERVER_ERROR(500, "系统繁忙,请稍候重试!"),
NOT_AUTH(500, "未登录!"), NOT_AUTH(500, "未登录!"),
REPEAT_REQUEST_ERROR(500, "请勿重复请求"),
PARAM_ERROR(400, "参数异常!"), PARAM_ERROR(400, "参数异常!"),
NOT_EXIST(110001, "查询为空"),
PAY_CONFIG_ERROR(110002, "支付配置未启用或未配置!"),
//======================订单异常========================
//======================业务异常========================
NOT_EXIST(110001, "查询为空"),
PAY_CONFIG_ERROR(110002, "支付配置未启用或未配置!"),
/** /**
* 订单已过期当前端看到该状态码的时候提示订单信息已过期请重新确认后提交此时用户点击确定前端刷新页面 * 订单已过期当前端看到该状态码的时候提示订单信息已过期请重新确认后提交此时用户点击确定前端刷新页面
*/ */
ORDER_EXPIRED(210001, "订单已过期"), ORDER_EXPIRED(110003, "订单已过期"),
ORDER_CANCEL(110004, "该订单已取消,请重新下单!"),
/** ORDER_REPEAT_PAY(110005, "该订单已支付,请勿重复支付!"),
* 请勿重复提交订单 REFUND_SING_ERROR(110006, "签名错误!"),
* 1.当前端遇到该异常时说明前端防多次点击没做好
* 2.提示用户 订单已发生改变请勿重复下单
*/
REPEAT_ORDER(210002,"请勿重复提交订单"),
ORDER_CANCEL(210003, "该订单已取消,请重新下单!"),
ORDER_REPEAT_PAY(210004, "该订单已支付,请勿重复支付!"),

140
bnyer-common/bnyer-common-core/src/main/java/com/bnyer/common/core/utils/MD5Util.java

@ -0,0 +1,140 @@
package com.bnyer.common.core.utils;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* @author :WXC
* @Date :2023/05/12
* @description :
*/
public class MD5Util {
/**
* 默认的密码字符串组合用来将字节转换成 16 进制表示的字符,apache校验下载的文件的正确性用的就是默认的这个组合
*/
protected static char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
protected static MessageDigest messagedigest = null;
static {
try {
messagedigest = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsaex) {
System.err.println(MD5Util.class.getName()
+ "初始化失败,MessageDigest不支持MD5Util。");
nsaex.printStackTrace();
}
}
/**
* 生成字符串的md5校验值
*
* @param s
* @return
*/
public static String getMD5String(String s) {
return getMD5String(s.getBytes());
}
public static String getStaffMD5String(String staffId,String s) {
return getMD5String((staffId+s).getBytes());
}
public static String getUserMD5String(String userId,String s) {
return getMD5String(s.getBytes());
}
/**
* 判断字符串的md5校验码是否与一个已知的md5码相匹配
*
* @param password 要校验的字符串
* @param md5PwdStr 已知的md5校验码
* @return
*/
public static boolean checkPassword(String password, String md5PwdStr) {
String s = getMD5String(password);
return s.equals(md5PwdStr);
}
/**
* 生成文件的md5校验值
*
* @param file
* @return
* @throws IOException
*/
public static String getFileMD5String(File file) throws IOException {
InputStream fis;
fis = new FileInputStream(file);
byte[] buffer = new byte[1024];
int numRead = 0;
while ((numRead = fis.read(buffer)) > 0) {
messagedigest.update(buffer, 0, numRead);
}
fis.close();
return bufferToHex(messagedigest.digest());
}
public static String getFileMD5String(InputStream fis) throws IOException {
byte[] buffer = new byte[1024];
int numRead = 0;
while ((numRead = fis.read(buffer)) > 0) {
messagedigest.update(buffer, 0, numRead);
}
fis.close();
return bufferToHex(messagedigest.digest());
}
public static String getMD5String(byte[] bytes) {
messagedigest.update(bytes);
return bufferToHex(messagedigest.digest());
}
private static String bufferToHex(byte bytes[]) {
return bufferToHex(bytes, 0, bytes.length);
}
private static String bufferToHex(byte bytes[], int m, int n) {
StringBuffer stringbuffer = new StringBuffer(2 * n);
int k = m + n;
for (int l = m; l < k; l++) {
appendHexPair(bytes[l], stringbuffer);
}
return stringbuffer.toString();
}
private static void appendHexPair(byte bt, StringBuffer stringbuffer) {
char c0 = hexDigits[(bt & 0xf0) >> 4];// 取字节中高 4 位的数字转换, >>> 为逻辑右移,将符号位一起右移,此处未发现两种符号有何不同
char c1 = hexDigits[bt & 0xf];// 取字节中低 4 位的数字转换
stringbuffer.append(c0);
stringbuffer.append(c1);
}
/**
* MD5方法
*
* @param text 明文
* @param key 密钥
* @return 密文
* @throws Exception
*/
public static String md5(String text, String key) throws Exception {
//加密后的字符串
System.out.println("text + key "+text + key);
String encodeStr= DigestUtils.md5Hex(text + key);
System.out.println("MD5加密后的字符串为:encodeStr="+encodeStr);
return encodeStr;
}
public static void main(String[] args) throws IOException {
System.out.println(MD5Util.getMD5String("RVWU202305121022211042vip"));
}
}

39
bnyer-services/bnyer-img/src/main/java/com/bnyer/img/controller/UserVipRecordController.java

@ -0,0 +1,39 @@
package com.bnyer.img.controller;
import com.bnyer.common.core.domain.R;
import com.bnyer.img.query.UserVipQuery;
import com.bnyer.img.query.UserVipRecordQuery;
import com.bnyer.img.service.UserVipRecordService;
import com.bnyer.img.service.UserVipService;
import com.bnyer.img.vo.UserVipRecordVo;
import com.bnyer.img.vo.UserVipVo;
import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
/**
* @author :WXC
* @Date :2023/05/10
* @description :
*/
@Api(value = "用户会员记录相关接口",tags = "用户会员记录相关接口")
@RestController
@RequestMapping("/img/mini/vipRecord")
@Slf4j
public class UserVipRecordController {
@Resource
private UserVipRecordService userVipRecordService;
@Operation(summary="获取用户会员记录",description = "获取用户会员记录")
@GetMapping(value = "/queryUserVipRecord")
public R<UserVipRecordVo> queryUserVipRecord(UserVipRecordQuery query){
return R.ok(userVipRecordService.queryUserVipRecord(query));
}
}

3
bnyer-services/bnyer-img/src/main/java/com/bnyer/img/mapper/UserVipRecordMapper.java

@ -2,8 +2,11 @@ package com.bnyer.img.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.bnyer.common.core.domain.UserVipRecord; import com.bnyer.common.core.domain.UserVipRecord;
import com.bnyer.img.query.UserVipRecordQuery;
import com.bnyer.img.vo.UserVipRecordVo;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
@Mapper @Mapper
public interface UserVipRecordMapper extends BaseMapper<UserVipRecord> { public interface UserVipRecordMapper extends BaseMapper<UserVipRecord> {
UserVipRecordVo queryUserVipRecord(UserVipRecordQuery query);
} }

26
bnyer-services/bnyer-img/src/main/java/com/bnyer/img/query/UserVipRecordQuery.java

@ -0,0 +1,26 @@
package com.bnyer.img.query;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
/**
* @author :WXC
* @Date :2023/03/31
* @description :
*/
@Getter
@Setter
@ApiModel("用户会员记录查询请求对象")
public class UserVipRecordQuery {
@ApiModelProperty(value = "id")
private Long id;
@ApiModelProperty(value = "用户id")
private Long userId;
/**
* 用户客户端类型
*/
private Integer userClientType;
}

9
bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/UserVipRecordService.java

@ -2,6 +2,9 @@ package com.bnyer.img.service;
import com.bnyer.common.core.dto.AddUserVipRecordDto; import com.bnyer.common.core.dto.AddUserVipRecordDto;
import com.bnyer.common.core.dto.PayUserVipDto; import com.bnyer.common.core.dto.PayUserVipDto;
import com.bnyer.img.query.UserVipRecordQuery;
import com.bnyer.img.vo.UserVipRecordVo;
import com.bnyer.img.vo.UserVipVo;
public interface UserVipRecordService { public interface UserVipRecordService {
@ -20,4 +23,10 @@ public interface UserVipRecordService {
*/ */
void addUserVipRecord(AddUserVipRecordDto dto); void addUserVipRecord(AddUserVipRecordDto dto);
/**
* 查询用户会员记录
* @param query
* @return
*/
UserVipRecordVo queryUserVipRecord(UserVipRecordQuery query);
} }

31
bnyer-services/bnyer-img/src/main/java/com/bnyer/img/service/impl/UserVipServiceRecordImpl.java

@ -6,13 +6,20 @@ import com.bnyer.common.core.domain.UserVipRecord;
import com.bnyer.common.core.domain.VipOrder; import com.bnyer.common.core.domain.VipOrder;
import com.bnyer.common.core.dto.AddUserVipRecordDto; import com.bnyer.common.core.dto.AddUserVipRecordDto;
import com.bnyer.common.core.dto.PayUserVipDto; import com.bnyer.common.core.dto.PayUserVipDto;
import com.bnyer.common.core.enums.ResponseEnum;
import com.bnyer.common.core.exception.ServiceException; import com.bnyer.common.core.exception.ServiceException;
import com.bnyer.common.core.utils.StringUtils;
import com.bnyer.common.core.utils.bean.EntityConvertUtil; import com.bnyer.common.core.utils.bean.EntityConvertUtil;
import com.bnyer.common.core.vo.UserInfoVo;
import com.bnyer.common.redis.service.RedissonService; import com.bnyer.common.redis.service.RedissonService;
import com.bnyer.common.security.utils.SecurityUtils;
import com.bnyer.img.constants.UserVipOrderStatusConstant; import com.bnyer.img.constants.UserVipOrderStatusConstant;
import com.bnyer.img.enums.EnumUserVipRecordStatus; import com.bnyer.img.enums.EnumUserVipRecordStatus;
import com.bnyer.img.mapper.UserVipRecordMapper; import com.bnyer.img.mapper.UserVipRecordMapper;
import com.bnyer.img.query.UserVipRecordQuery;
import com.bnyer.img.service.UserVipRecordService; import com.bnyer.img.service.UserVipRecordService;
import com.bnyer.img.vo.UserVipRecordVo;
import com.bnyer.img.vo.UserVipVo;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock; import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -20,6 +27,7 @@ import org.springframework.stereotype.Service;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.util.Date; import java.util.Date;
import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@Service @Service
@ -91,4 +99,27 @@ public class UserVipServiceRecordImpl implements UserVipRecordService {
userVipRecord.setStatus(EnumUserVipRecordStatus.VALID.getStatus()); userVipRecord.setStatus(EnumUserVipRecordStatus.VALID.getStatus());
userVipRecordMapper.insert(userVipRecord); userVipRecordMapper.insert(userVipRecord);
} }
/**
* 获取用户会员记录
* @param query
* @return
*/
@Override
public UserVipRecordVo queryUserVipRecord(UserVipRecordQuery query) {
UserInfoVo userInfo = SecurityUtils.getUserInfo();
if (Objects.isNull(query.getId())){
query.setUserId(userInfo.getId());
query.setUserClientType(userInfo.getUserClientType());
}
UserVipRecordVo userVipRecordVo = userVipRecordMapper.queryUserVipRecord(query);
if (Objects.isNull(userVipRecordVo)){
userVipRecordVo = new UserVipRecordVo();
userVipRecordVo.setIsVip("0");
}else {
// TODO: 2023/05/12 同步会员到期状态
userVipRecordVo.setIsVip("1");
}
return userVipRecordVo;
}
} }

56
bnyer-services/bnyer-img/src/main/java/com/bnyer/img/vo/UserVipRecordVo.java

@ -0,0 +1,56 @@
package com.bnyer.img.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.Date;
@Getter
@Setter
@NoArgsConstructor
@ApiModel(value = "用户会员记录查询响应对象")
public class UserVipRecordVo {
@ApiModelProperty(value="id")
private Long id;
@ApiModelProperty(value="订单号")
private String orderNo;
@ApiModelProperty(value="用户id")
private Long userId;
@ApiModelProperty(value="用户手机号")
private String phone;
@ApiModelProperty(value="vip表id")
private Long vipId;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value="开始时间")
private Date startTime;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@ApiModelProperty(value="到期时间")
private Date endTime;
@ApiModelProperty(value="状态(0->已到期;1->已生效;)")
private Integer status;
@ApiModelProperty(value="vip名称")
private String vipName;
@ApiModelProperty(value = "vip类型名称")
private String vipTypeName;
@ApiModelProperty(value = "用户客户端类型:10用户-抖音 20用户-快手 30用户-微信 40艺术家-微信")
private Integer userClientType;
@ApiModelProperty(value = "是否开通会员:0 未开通 1已开通")
private String isVip;
}

30
bnyer-services/bnyer-img/src/main/resources/com/bnyer/img/mapper/UserVipRecordMapper.xml

@ -22,7 +22,33 @@
</resultMap> </resultMap>
<sql id="Base_Column_List"> <sql id="Base_Column_List">
<!--@mbg.generated--> <!--@mbg.generated-->
id,order_no,user_id , phone,vip_name,vip_type_name,user_client_type,status, vip_id, start_time, end_time, is_show, create_time, update_time, a.id,
sort a.order_no,
a.user_id ,
a.phone,vip_name,
a.vip_type_name,
a.user_client_type,
a.`status`, vip_id,
a.start_time, end_time,
a.is_show,
a.create_time,
a.update_time,
a.sort
</sql> </sql>
<select id="queryUserVipRecord" resultMap="BaseResultMap">
select
<include refid="Base_Column_List"/>
FROM img_user_vip_record a
WHERE
a.status = 1
<if test="id != null">
and a.id = #{id}
</if>
<if test="userId != null">
and a.user_id = #{userId}
</if>
<if test="userClientType != null">
and a.user_client_type = #{userClientType}
</if>
</select>
</mapper> </mapper>

63
bnyer-services/bnyer-order/src/main/java/com/bnyer/order/annotation/LimitRepeatRequest.java

@ -0,0 +1,63 @@
package com.bnyer.order.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
/**
* @author :WXC
* @Date :2023/05/12
* @description : 防重复提交
*/
@Target({ METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LimitRepeatRequest {
/**
* 限制当前用户同一个api 不能重复提交
*/
String SELF="self";
/**
* 限制所有用户同一个参数不能重复提交如我新增了用户 张三 那么其他人不能再并发情况下重复添加张三
*/
String ALL_USER="all";
/**
* 当前时间内 api 只能请求一次单位秒
* @return
*/
int time() default 5;
/**
* 对部分参数做重复请求限制
* @return
*/
String[] bodyParam() default {};
/**
* 是否对全部参数做重复请求限制
* @return
*/
boolean bodyAllParam() default false;
/**
* 重复请求限制的用户范围
* LimitRepeatRequest.SELF针对当前登录用户
* LimitRepeatRequest.ALL_USER针对所有用户
*
* @return
*/
String userRange() default SELF;
/**
* 错误提示信息
* @return
*/
String message() default "";
}

150
bnyer-services/bnyer-order/src/main/java/com/bnyer/order/aop/LimitRepeatRequestAspect.java

@ -0,0 +1,150 @@
package com.bnyer.order.aop;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bnyer.common.core.domain.R;
import com.bnyer.common.core.utils.MD5Util;
import com.bnyer.common.core.utils.StringUtils;
import com.bnyer.common.core.vo.UserInfoVo;
import com.bnyer.common.redis.service.RedisService;
import com.bnyer.common.security.utils.SecurityUtils;
import com.bnyer.order.annotation.LimitRepeatRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @author :WXC
* @Date :2023/05/12
* @description :重复提交限制aop
*/
@Slf4j
@Aspect
@Component
public class LimitRepeatRequestAspect {
@Autowired
private RedisService redisService;
@Around("@annotation(limitRepeatRequest)")
public Object around(ProceedingJoinPoint joinPoint, LimitRepeatRequest limitRepeatRequest) throws Throwable{
String key = getKey(joinPoint,limitRepeatRequest);
Object cacheObject = redisService.getCacheObject(key);
Object message;
if (Objects.nonNull(cacheObject)){
if (StringUtils.isNotBlank(limitRepeatRequest.message())){
message = limitRepeatRequest.message();
}else {
message = new R().buildRepeatRequest(limitRepeatRequest.time());
}
}else {
redisService.setCacheObject(key,"1",(long)limitRepeatRequest.time(), TimeUnit.SECONDS);
message = joinPoint.proceed();
}
return message;
}
/**
* 获取rediskey
*
* @param joinPoint
* @param limitRepeatRequest
* @return
*/
private String getKey(ProceedingJoinPoint joinPoint, LimitRepeatRequest limitRepeatRequest) {
UserInfoVo userInfo = SecurityUtils.getUserInfo();
Method currentMethod = getCurrentMethod(joinPoint);
//最后拼接好的key
StringBuilder key = new StringBuilder("LimitRepeatRequestAspect#" + currentMethod.getName());
//限制范围
String userRange = limitRepeatRequest.userRange();
if (LimitRepeatRequest.SELF.equals(userRange)){
key.append("#");
key.append(userInfo.getUserClientType());
key.append("#");
key.append(userInfo.getId());
}
//获取请求参数
JSONObject requestParams = getRequestParams(joinPoint);
//部分参数做重复请求限制
if (Objects.nonNull(requestParams)){
String[] bodyParam = limitRepeatRequest.bodyParam();
if (bodyParam != null){
for (String param : bodyParam) {
key.append("#");
Object obj = requestParams.get(param);
if (obj instanceof JSONArray){
JSONArray jsonArray = requestParams.getJSONArray(param);
if (CollUtil.isNotEmpty(jsonArray)){
for (Object o : jsonArray) {
if (o instanceof String){
key.append(o);
}
}
}
}else if(obj instanceof String){
String value = requestParams.containsKey(param)?requestParams.getString(param):"";
key.append(value);
}else if(obj instanceof Integer){
String value = requestParams.containsKey(param)?requestParams.getString(param):"";
key.append(value);
}
}
}
}
//全部参数做重复请求限制
if (limitRepeatRequest.bodyAllParam()){
String jsonStr = JSON.toJSONString(requestParams);
key.append("#");
key.append(jsonStr);
}
return MD5Util.getMD5String(key.toString());
}
/**
* 获取请求参数
* @param joinPoint
* @return
*/
private JSONObject getRequestParams(ProceedingJoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
if (args != null && args.length != 0){
String jsonString = JSON.toJSONString(args);
JSONArray parseArray = JSON.parseArray(jsonString);
return parseArray.getJSONObject(0);
}
return null;
}
/**
* 获取当前方法
* @param joinPoint
* @return
*/
private Method getCurrentMethod(ProceedingJoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
if (!(signature instanceof MethodSignature)){
throw new IllegalArgumentException("该注解只能作用于方法上面");
}
MethodSignature methodSignature = (MethodSignature)signature;
Object target = joinPoint.getTarget();
try {
Method method = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
return method;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}

2
bnyer-services/bnyer-order/src/main/java/com/bnyer/order/controller/VipOrderController.java

@ -2,6 +2,7 @@ package com.bnyer.order.controller;
import com.bnyer.common.core.domain.R; import com.bnyer.common.core.domain.R;
import com.bnyer.common.core.web.controller.BaseController; import com.bnyer.common.core.web.controller.BaseController;
import com.bnyer.order.annotation.LimitRepeatRequest;
import com.bnyer.order.bean.dto.AddVipOrderDto; import com.bnyer.order.bean.dto.AddVipOrderDto;
import com.bnyer.order.bean.query.VipOrderExtQuery; import com.bnyer.order.bean.query.VipOrderExtQuery;
import com.bnyer.order.bean.query.VipOrderQuery; import com.bnyer.order.bean.query.VipOrderQuery;
@ -38,6 +39,7 @@ public class VipOrderController extends BaseController {
/** /**
* 提交订单 * 提交订单
*/ */
@LimitRepeatRequest(bodyAllParam = true, message = "您的订单已提交,请勿频繁操作")
@PostMapping("/addVipOrder") @PostMapping("/addVipOrder")
@Operation(summary = "生成会员订单,返回订单号" , description = "生成会员订单,返回订单号,通过订单号调用支付接口") @Operation(summary = "生成会员订单,返回订单号" , description = "生成会员订单,返回订单号,通过订单号调用支付接口")
public R<String> addVipOrder(@Valid @RequestBody AddVipOrderDto addVipOrderDto) { public R<String> addVipOrder(@Valid @RequestBody AddVipOrderDto addVipOrderDto) {

63
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/annotation/LimitRepeatRequest.java

@ -0,0 +1,63 @@
package com.bnyer.pay.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.METHOD;
/**
* @author :WXC
* @Date :2023/05/12
* @description : 防重复提交
*/
@Target({ METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LimitRepeatRequest {
/**
* 限制当前用户同一个api 不能重复提交
*/
String SELF="self";
/**
* 限制所有用户同一个参数不能重复提交如我新增了用户 张三 那么其他人不能再并发情况下重复添加张三
*/
String ALL_USER="all";
/**
* 当前时间内 api 只能请求一次单位秒
* @return
*/
int time() default 5;
/**
* 对部分参数做重复请求限制
* @return
*/
String[] bodyParam() default {};
/**
* 是否对全部参数做重复请求限制
* @return
*/
boolean bodyAllParam() default false;
/**
* 重复请求限制的用户范围
* LimitRepeatRequest.SELF针对当前登录用户
* LimitRepeatRequest.ALL_USER针对所有用户
*
* @return
*/
String userRange() default SELF;
/**
* 错误提示信息
* @return
*/
String message() default "";
}

150
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/aop/LimitRepeatRequestAspect.java

@ -0,0 +1,150 @@
package com.bnyer.pay.aop;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bnyer.common.core.domain.R;
import com.bnyer.common.core.utils.MD5Util;
import com.bnyer.common.core.utils.StringUtils;
import com.bnyer.common.core.vo.UserInfoVo;
import com.bnyer.common.redis.service.RedisService;
import com.bnyer.common.security.utils.SecurityUtils;
import com.bnyer.pay.annotation.LimitRepeatRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
* @author :WXC
* @Date :2023/05/12
* @description :重复提交限制aop
*/
@Slf4j
@Aspect
@Component
public class LimitRepeatRequestAspect {
@Autowired
private RedisService redisService;
@Around("@annotation(limitRepeatRequest)")
public Object around(ProceedingJoinPoint joinPoint, LimitRepeatRequest limitRepeatRequest) throws Throwable{
String key = getKey(joinPoint,limitRepeatRequest);
Object cacheObject = redisService.getCacheObject(key);
Object message;
if (Objects.nonNull(cacheObject)){
if (StringUtils.isNotBlank(limitRepeatRequest.message())){
message = limitRepeatRequest.message();
}else {
message = new R().buildRepeatRequest(limitRepeatRequest.time());
}
}else {
redisService.setCacheObject(key,"1",(long)limitRepeatRequest.time(), TimeUnit.SECONDS);
message = joinPoint.proceed();
}
return message;
}
/**
* 获取rediskey
*
* @param joinPoint
* @param limitRepeatRequest
* @return
*/
private String getKey(ProceedingJoinPoint joinPoint, LimitRepeatRequest limitRepeatRequest) {
UserInfoVo userInfo = SecurityUtils.getUserInfo();
Method currentMethod = getCurrentMethod(joinPoint);
//最后拼接好的key
StringBuilder key = new StringBuilder("LimitRepeatRequestAspect#" + currentMethod.getName());
//限制范围
String userRange = limitRepeatRequest.userRange();
if (LimitRepeatRequest.SELF.equals(userRange)){
key.append("#");
key.append(userInfo.getUserClientType());
key.append("#");
key.append(userInfo.getId());
}
//获取请求参数
JSONObject requestParams = getRequestParams(joinPoint);
//部分参数做重复请求限制
if (Objects.nonNull(requestParams)){
String[] bodyParam = limitRepeatRequest.bodyParam();
if (bodyParam != null){
for (String param : bodyParam) {
key.append("#");
Object obj = requestParams.get(param);
if (obj instanceof JSONArray){
JSONArray jsonArray = requestParams.getJSONArray(param);
if (CollUtil.isNotEmpty(jsonArray)){
for (Object o : jsonArray) {
if (o instanceof String){
key.append(o);
}
}
}
}else if(obj instanceof String){
String value = requestParams.containsKey(param)?requestParams.getString(param):"";
key.append(value);
}else if(obj instanceof Integer){
String value = requestParams.containsKey(param)?requestParams.getString(param):"";
key.append(value);
}
}
}
}
//全部参数做重复请求限制
if (limitRepeatRequest.bodyAllParam()){
String jsonStr = JSON.toJSONString(requestParams);
key.append("#");
key.append(jsonStr);
}
return MD5Util.getMD5String(key.toString());
}
/**
* 获取请求参数
* @param joinPoint
* @return
*/
private JSONObject getRequestParams(ProceedingJoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
if (args != null && args.length != 0){
String jsonString = JSON.toJSONString(args);
JSONArray parseArray = JSON.parseArray(jsonString);
return parseArray.getJSONObject(0);
}
return null;
}
/**
* 获取当前方法
* @param joinPoint
* @return
*/
private Method getCurrentMethod(ProceedingJoinPoint joinPoint) {
Signature signature = joinPoint.getSignature();
if (!(signature instanceof MethodSignature)){
throw new IllegalArgumentException("该注解只能作用于方法上面");
}
MethodSignature methodSignature = (MethodSignature)signature;
Object target = joinPoint.getTarget();
try {
Method method = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes());
return method;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}

16
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/bean/dto/RefundDto.java

@ -1,9 +1,13 @@
package com.bnyer.pay.bean.dto; package com.bnyer.pay.bean.dto;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;
/** /**
* @author :WXC * @author :WXC
* @Date :2023/05/08 * @Date :2023/05/08
@ -14,4 +18,16 @@ import lombok.Setter;
@NoArgsConstructor @NoArgsConstructor
public class RefundDto { public class RefundDto {
@NotBlank(message = "支付订单号不能为空")
@ApiModelProperty(value = "支付订单号")
private String payId;
@ApiModelProperty(value = " 退款金额(元) ,如果不传则全额退款", example = "0.01")
private BigDecimal refundAmount;
@NotBlank(message = "签名不能为空")
@ApiModelProperty(value = "签名")
private String sign;
} }

3
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/bean/vo/UnifiedOrderVo.java

@ -13,9 +13,6 @@ import lombok.NoArgsConstructor;
@NoArgsConstructor @NoArgsConstructor
public class UnifiedOrderVo { public class UnifiedOrderVo {
@ApiModelProperty(value = "应用id")
private String appId;
@ApiModelProperty(value = "内部系统支付单号/开发者测单号") @ApiModelProperty(value = "内部系统支付单号/开发者测单号")
private String outOrderNo; private String outOrderNo;

18
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/controller/UnifiedPayController.java

@ -1,11 +1,17 @@
package com.bnyer.pay.controller; package com.bnyer.pay.controller;
import com.bnyer.common.core.domain.R; import com.bnyer.common.core.domain.R;
import com.bnyer.common.core.enums.ResponseEnum;
import com.bnyer.common.core.exception.ServiceException;
import com.bnyer.pay.annotation.LimitRepeatRequest;
import com.bnyer.pay.bean.dto.RefundDto;
import com.bnyer.pay.bean.dto.UnifiedOrderDto; import com.bnyer.pay.bean.dto.UnifiedOrderDto;
import com.bnyer.pay.bean.dto.QueryOrderDto; import com.bnyer.pay.bean.dto.QueryOrderDto;
import com.bnyer.pay.bean.vo.ThirdRefundVo;
import com.bnyer.pay.service.UnifiedPayService; import com.bnyer.pay.service.UnifiedPayService;
import com.bnyer.pay.bean.vo.UnifiedOrderVo; import com.bnyer.pay.bean.vo.UnifiedOrderVo;
import com.bnyer.pay.bean.vo.QueryOrderVo; import com.bnyer.pay.bean.vo.QueryOrderVo;
import com.bnyer.pay.utils.PaymentRefundUtil;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -32,6 +38,7 @@ public class UnifiedPayController {
@Autowired @Autowired
private UnifiedPayService unifiedPayService; private UnifiedPayService unifiedPayService;
@LimitRepeatRequest(bodyAllParam = true, message = "您的订单已提交,请勿频繁操作")
@PostMapping("/unifiedOrder") @PostMapping("/unifiedOrder")
@Operation(summary = "统一下单,并生成支付订单" , description = "生成支付订单,返回前端支付所需参数") @Operation(summary = "统一下单,并生成支付订单" , description = "生成支付订单,返回前端支付所需参数")
public R<UnifiedOrderVo> unifiedOrder(@Valid @RequestBody UnifiedOrderDto dto, HttpServletRequest request){ public R<UnifiedOrderVo> unifiedOrder(@Valid @RequestBody UnifiedOrderDto dto, HttpServletRequest request){
@ -46,4 +53,15 @@ public class UnifiedPayController {
return R.ok(queryOrderVo); return R.ok(queryOrderVo);
} }
@LimitRepeatRequest(time = 10, message = "请勿频繁操作")
@PostMapping("/refund")
@Operation(summary = "统一退款" , description = "统一退款")
public R<?> refund(@Valid @RequestBody RefundDto dto){
if (!PaymentRefundUtil.checkSign(dto)){
throw new ServiceException(ResponseEnum.REFUND_SING_ERROR);
}
unifiedPayService.refund(dto);
return R.ok();
}
} }

4
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/factory/PayFactory.java

@ -27,11 +27,11 @@ public class PayFactory {
} }
public static class SingletonHolder{ public static class SingletonHolder{
public static PayFactory payStrategy = new PayFactory(); public static PayFactory payFactory = new PayFactory();
} }
public static PayFactory getInstance(){ public static PayFactory getInstance(){
return SingletonHolder.payStrategy; return SingletonHolder.payFactory;
} }
public IPayStrategy getConcreteStrategy(String payType){ public IPayStrategy getConcreteStrategy(String payType){

10
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/design/strategy/WxPayStrategy.java

@ -102,14 +102,18 @@ public class WxPayStrategy extends AbstractPayStrategy {
amount.setTotal(BaseWxPayRequest.yuanToFen(bo.getPayAmount())); amount.setTotal(BaseWxPayRequest.yuanToFen(bo.getPayAmount()));
orderRequest.setAmount(amount); orderRequest.setAmount(amount);
//调用微信支付接口 //调用微信支付接口
WxPayUnifiedOrderV3Result wxPayUnifiedOrderV3Result = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, orderRequest); WxPayUnifiedOrderV3Result wxPayUnifiedOrderV3Result = wxPayService.unifiedOrderV3(TradeTypeEnum.JSAPI, orderRequest);
WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = wxPayUnifiedOrderV3Result.getPayInfo(TradeTypeEnum.JSAPI, wxPayConfig.getAppid(), wxPayConfig.getMchid(), wxPayService.getConfig().getPrivateKey()); WxPayUnifiedOrderV3Result.JsapiResult jsapiResult = wxPayUnifiedOrderV3Result.getPayInfo(TradeTypeEnum.JSAPI, wxPayConfig.getAppid(), wxPayConfig.getMchid(), wxPayService.getConfig().getPrivateKey());
thirdUnifiedOrderVo.setAppId(wxPayConfig.getAppid());
ThirdUnifiedOrderVo.WxThirdInOrderVo wxThirdInOrderVo = new ThirdUnifiedOrderVo.WxThirdInOrderVo(); ThirdUnifiedOrderVo.WxThirdInOrderVo wxThirdInOrderVo = new ThirdUnifiedOrderVo.WxThirdInOrderVo();
//微信返回信息封装
wxThirdInOrderVo.setAppId(jsapiResult.getAppId());
wxThirdInOrderVo.setPackageValue(jsapiResult.getPackageValue()); wxThirdInOrderVo.setPackageValue(jsapiResult.getPackageValue());
wxThirdInOrderVo.setTimeStamp(jsapiResult.getTimeStamp()); wxThirdInOrderVo.setTimeStamp(jsapiResult.getTimeStamp());
wxThirdInOrderVo.setNonceStr(jsapiResult.getNonceStr()); wxThirdInOrderVo.setNonceStr(jsapiResult.getNonceStr());
wxThirdInOrderVo.setPaySign(jsapiResult.getPaySign()); wxThirdInOrderVo.setPaySign(jsapiResult.getPaySign());
wxThirdInOrderVo.setSignType(jsapiResult.getSignType());
thirdUnifiedOrderVo.setAppId(wxPayConfig.getAppid());
thirdUnifiedOrderVo.setOutOrderNo(bo.getPayId()); thirdUnifiedOrderVo.setOutOrderNo(bo.getPayId());
thirdUnifiedOrderVo.setAppId(jsapiResult.getAppId()); thirdUnifiedOrderVo.setAppId(jsapiResult.getAppId());
@ -117,6 +121,8 @@ public class WxPayStrategy extends AbstractPayStrategy {
thirdUnifiedOrderVo.setThirdCode("0"); thirdUnifiedOrderVo.setThirdCode("0");
thirdUnifiedOrderVo.setThirdMsg("ok"); thirdUnifiedOrderVo.setThirdMsg("ok");
thirdUnifiedOrderVo.setThirdNo(wxPayUnifiedOrderV3Result.getPrepayId()); thirdUnifiedOrderVo.setThirdNo(wxPayUnifiedOrderV3Result.getPrepayId());
thirdUnifiedOrderVo.setWxThirdInOrderVo(wxThirdInOrderVo);
return thirdUnifiedOrderVo; return thirdUnifiedOrderVo;
} catch (WxPayException e) { } catch (WxPayException e) {
log.error("微信支付:统一下单接口调用失败,payId:{},error{}", bo.getPayId(), e.getMessage()); log.error("微信支付:统一下单接口调用失败,payId:{},error{}", bo.getPayId(), e.getMessage());

20
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/enums/EnumVerificationKey.java

@ -0,0 +1,20 @@
package com.bnyer.pay.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
/**
* @author :WXC
* @Date :2023/05/12
* @description :
*/
@Getter
@NoArgsConstructor
@AllArgsConstructor
public enum EnumVerificationKey {
VIP("vip", "VipInOrder20230512Key"),
;
private String key;
private String value;
}

8
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/UnifiedPayService.java

@ -1,7 +1,9 @@
package com.bnyer.pay.service; package com.bnyer.pay.service;
import com.bnyer.pay.bean.dto.RefundDto;
import com.bnyer.pay.bean.dto.UnifiedOrderDto; import com.bnyer.pay.bean.dto.UnifiedOrderDto;
import com.bnyer.pay.bean.dto.QueryOrderDto; import com.bnyer.pay.bean.dto.QueryOrderDto;
import com.bnyer.pay.bean.vo.ThirdRefundVo;
import com.bnyer.pay.bean.vo.UnifiedOrderVo; import com.bnyer.pay.bean.vo.UnifiedOrderVo;
import com.bnyer.pay.bean.vo.QueryOrderVo; import com.bnyer.pay.bean.vo.QueryOrderVo;
@ -28,4 +30,10 @@ public interface UnifiedPayService {
* @return * @return
*/ */
QueryOrderVo queryOrder(QueryOrderDto dto); QueryOrderVo queryOrder(QueryOrderDto dto);
/**
* 统一退款
* @param dto
*/
void refund(RefundDto dto);
} }

10
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/service/impl/UnifiedPayServiceImpl.java

@ -18,6 +18,7 @@ import com.bnyer.pay.bean.bo.QueryOrderBo;
import com.bnyer.pay.bean.bo.UnifiedOrderBo; import com.bnyer.pay.bean.bo.UnifiedOrderBo;
import com.bnyer.pay.bean.dto.AddPayInfoDto; import com.bnyer.pay.bean.dto.AddPayInfoDto;
import com.bnyer.pay.bean.dto.QueryOrderDto; import com.bnyer.pay.bean.dto.QueryOrderDto;
import com.bnyer.pay.bean.dto.RefundDto;
import com.bnyer.pay.bean.dto.UnifiedOrderDto; import com.bnyer.pay.bean.dto.UnifiedOrderDto;
import com.bnyer.pay.bean.vo.*; import com.bnyer.pay.bean.vo.*;
import com.bnyer.pay.constant.KSPayConstants; import com.bnyer.pay.constant.KSPayConstants;
@ -121,8 +122,8 @@ public class UnifiedPayServiceImpl implements UnifiedPayService {
* @param dto * @param dto
*/ */
private void checkData(UnifiedOrderDto dto) { private void checkData(UnifiedOrderDto dto) {
PayInfo payInfo = payInfoMapper.selectOne(new LambdaQueryWrapper<PayInfo>().eq(PayInfo::getOrderNo,dto.getOrderNo())); PayInfo payInfo = payInfoMapper.selectOne(new LambdaQueryWrapper<PayInfo>().eq(PayInfo::getOrderNo,dto.getOrderNo()).eq(PayInfo::getPayStatus,EnumPayStatus.SUCCESS.getStatus()));
if (Objects.nonNull(payInfo) && EnumPayStatus.SUCCESS.getStatus() == payInfo.getPayStatus()){ if (Objects.nonNull(payInfo)){
throw new ServiceException(ResponseEnum.ORDER_REPEAT_PAY); throw new ServiceException(ResponseEnum.ORDER_REPEAT_PAY);
} }
} }
@ -248,4 +249,9 @@ public class UnifiedPayServiceImpl implements UnifiedPayService {
} }
} }
@Override
public void refund(RefundDto dto) {
}
} }

39
bnyer-services/bnyer-pay/src/main/java/com/bnyer/pay/utils/PaymentRefundUtil.java

@ -0,0 +1,39 @@
package com.bnyer.pay.utils;
import com.bnyer.common.core.utils.MD5Util;
import com.bnyer.pay.bean.dto.RefundDto;
import com.bnyer.pay.enums.EnumVerificationKey;
/**
* @author :WXC
* @Date :2023/05/12
* @description : 退款工具类
*/
public class PaymentRefundUtil {
/**
* 获取签名
* @param dto
* @return
*/
public static String getSign(RefundDto dto) {
return MD5Util.getMD5String(dto.getPayId() + EnumVerificationKey.VIP.getValue());
}
/**
* 校验签名
* @param dto
* @return
*/
public static boolean checkSign(RefundDto dto) {
return MD5Util.getMD5String(dto.getPayId() + EnumVerificationKey.VIP.getValue()).equals(dto.getSign());
}
public static void main(String[] args) {
String sign = MD5Util.getMD5String("RVWU202305121022211042" + EnumVerificationKey.VIP.getValue());
System.out.println(sign);
System.out.println(MD5Util.getMD5String("RVWU202305121022211042" + EnumVerificationKey.VIP.getValue()).equals(sign));
}
}

2
pom.xml

@ -44,7 +44,7 @@
<hutool.version>5.8.0.M3</hutool.version> <hutool.version>5.8.0.M3</hutool.version>
<redisson.version>3.16.2</redisson.version> <redisson.version>3.16.2</redisson.version>
<aliPay.version>4.23.21.ALL</aliPay.version> <aliPay.version>4.23.21.ALL</aliPay.version>
<wePay.version>4.4.0</wePay.version> <wePay.version>4.5.0</wePay.version>
<qiniu.version>7.2.18</qiniu.version> <qiniu.version>7.2.18</qiniu.version>
<rocketmq-springboot.version>2.2.2</rocketmq-springboot.version> <rocketmq-springboot.version>2.2.2</rocketmq-springboot.version>
</properties> </properties>

Loading…
Cancel
Save