Jelajahi Sumber

Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/ruoyi-vue-pro

# Conflicts:
#	yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
#	yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java
YunaiV 10 bulan lalu
induk
melakukan
c660833697
12 mengubah file dengan 285 tambahan dan 308 penghapusan
  1. 92 127
      sql/mysql/quartz.sql
  2. 141 87
      sql/oracle/quartz.sql
  3. 1 1
      yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java
  4. 13 2
      yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java
  5. 2 2
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/job/order/PayOrderSyncJob.java
  6. 5 43
      yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
  7. 3 9
      yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java
  8. 3 2
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java
  9. 3 2
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
  10. 10 14
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
  11. 2 1
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java
  12. 10 18
      yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java

File diff ditekan karena terlalu besar
+ 92 - 127
sql/mysql/quartz.sql


File diff ditekan karena terlalu besar
+ 141 - 87
sql/oracle/quartz.sql


+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-data-permission/src/main/java/cn/iocoder/yudao/framework/datapermission/core/rule/dept/DeptDataPermissionRule.java

@@ -157,7 +157,7 @@ public class DeptDataPermissionRule implements DataPermissionRule {
         // 拼接条件
         return new InExpression(MyBatisUtils.buildColumn(tableName, tableAlias, columnName),
                 // Parenthesis 的目的,是提供 (1,2,3) 的 () 左右括号
-                new Parenthesis(new ExpressionList<>(CollectionUtils.convertList(deptIds, LongValue::new))));
+                new Parenthesis(new ExpressionList<LongValue>(CollectionUtils.convertList(deptIds, LongValue::new))));
     }
 
     private Expression buildUserExpression(String tableName, Alias tableAlias, Boolean self, Long userId) {

+ 13 - 2
yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/web/core/handler/GlobalExceptionHandler.java

@@ -2,9 +2,11 @@ package cn.iocoder.yudao.framework.web.core.handler;
 
 import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.apilog.core.service.ApiErrorLogFrameworkService;
 import cn.iocoder.yudao.framework.common.exception.ServiceException;
+import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.collection.SetUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
@@ -209,8 +211,17 @@ public class GlobalExceptionHandler {
         // 不包含的时候,才进行打印,避免 ex 堆栈过多
         if (!IGNORE_ERROR_MESSAGES.contains(ex.getMessage())) {
             // 即使打印,也只打印第一层 StackTraceElement,并且使用 warn 在控制台输出,更容易看到
-            StackTraceElement[] stackTrace = ex.getStackTrace();
-            log.warn("[serviceExceptionHandler]\n\t{}", stackTrace[0]);
+            try {
+                StackTraceElement[] stackTraces = ex.getStackTrace();
+                for (StackTraceElement stackTrace : stackTraces) {
+                    if (ObjUtil.notEqual(stackTrace.getClassName(), ServiceExceptionUtil.class.getName())) {
+                        log.warn("[serviceExceptionHandler]\n\t{}", stackTrace);
+                        break;
+                    }
+                }
+            } catch (Exception ignored) {
+                // 忽略日志,避免影响主流程
+            }
         }
         return CommonResult.error(ex.getCode(), ex.getMessage());
     }

+ 2 - 2
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/job/order/PayOrderSyncJob.java

@@ -21,9 +21,9 @@ import java.time.LocalDateTime;
 public class PayOrderSyncJob implements JobHandler {
 
     /**
-     * 同步创建时间在 N 分钟之的订单
+     * 同步创建时间在 N 分钟之的订单
      *
-     * 为什么同步 10 分钟之的订单?
+     * 为什么同步 10 分钟之的订单?
      *  因为一个订单发起支付,到支付成功,大多数在 10 分钟内,需要保证轮询到。
      *  如果设置为 30、60 或者更大时间范围,会导致轮询的订单太多,影响性能。当然,你也可以根据自己的业务情况来处理。
      */

+ 5 - 43
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java

@@ -14,22 +14,17 @@ import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert;
 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
 import cn.iocoder.yudao.module.pay.framework.pay.core.WalletPayClient;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import lombok.Getter;
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.Resource;
+import jakarta.validation.Validator;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
-import javax.annotation.PostConstruct;
-import javax.annotation.Resource;
-import javax.validation.Validator;
-import java.time.Duration;
 import java.util.Collection;
 import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
 
 /**
@@ -42,25 +37,6 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
 @Validated
 public class PayChannelServiceImpl implements PayChannelService {
 
-    /**
-     * {@link PayClient} 缓存,通过它异步清空 smsClientFactory
-     */
-    @Getter
-    private final LoadingCache<Long, PayClient> clientCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
-            new CacheLoader<Long, PayClient>() {
-
-                @Override
-                public PayClient load(Long id) {
-                    // 查询,然后尝试清空
-                    PayChannelDO channel = payChannelMapper.selectById(id);
-                    if (channel != null) {
-                        payClientFactory.createOrUpdatePayClient(channel.getId(), channel.getCode(), channel.getConfig());
-                    }
-                    return payClientFactory.getPayClient(id);
-                }
-
-            });
-
     @Resource
     private PayClientFactory payClientFactory;
 
@@ -102,9 +78,6 @@ public class PayChannelServiceImpl implements PayChannelService {
         PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO)
                 .setConfig(parseConfig(dbChannel.getCode(), updateReqVO.getConfig()));
         payChannelMapper.updateById(channel);
-
-        // 清空缓存
-        clearCache(channel.getId());
     }
 
     /**
@@ -135,18 +108,6 @@ public class PayChannelServiceImpl implements PayChannelService {
 
         // 删除
         payChannelMapper.deleteById(id);
-
-        // 清空缓存
-        clearCache(id);
-    }
-
-    /**
-     * 删除缓存
-     *
-     * @param id 渠道编号
-     */
-    private void clearCache(Long id) {
-        clientCache.invalidate(id);
     }
 
     private PayChannelDO validateChannelExists(Long id) {
@@ -202,7 +163,8 @@ public class PayChannelServiceImpl implements PayChannelService {
 
     @Override
     public PayClient getPayClient(Long id) {
-        return clientCache.getUnchecked(id);
+        PayChannelDO channel = validPayChannel(id);
+        return payClientFactory.createOrUpdatePayClient(id, channel.getCode(), channel.getConfig());
     }
 
 }

+ 3 - 9
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java

@@ -60,8 +60,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
         PayChannelDO channel = channelMapper.selectById(channelId);
         assertPojoEquals(reqVO, channel, "config");
         assertPojoEquals(config, channel.getConfig());
-        // 校验缓存
-        assertNull(channelService.getClientCache().getIfPresent(channelId));
     }
 
     @Test
@@ -102,8 +100,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
         PayChannelDO channel = channelMapper.selectById(reqVO.getId()); // 获取最新的
         assertPojoEquals(reqVO, channel, "config");
         assertPojoEquals(config, channel.getConfig());
-        // 校验缓存
-        assertNull(channelService.getClientCache().getIfPresent(channel.getId()));
     }
 
     @Test
@@ -134,8 +130,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
         channelService.deleteChannel(id);
         // 校验数据不存在了
         assertNull(channelMapper.selectById(id));
-        // 校验缓存
-        assertNull(channelService.getClientCache().getIfPresent(id));
     }
 
     @Test
@@ -306,20 +300,20 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
         PayChannelDO channel = randomPojo(PayChannelDO.class, o -> {
             o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
             o.setConfig(randomAlipayPayClientConfig());
+            o.setStatus(CommonStatusEnum.ENABLE.getStatus());
         });
         channelMapper.insert(channel);
         // mock 参数
         Long id = channel.getId();
         // mock 方法
         PayClient mockClient = mock(PayClient.class);
-        when(payClientFactory.getPayClient(eq(id))).thenReturn(mockClient);
+        when(payClientFactory.createOrUpdatePayClient(eq(id), eq(channel.getCode()), eq(channel.getConfig())))
+                .thenReturn(mockClient);
 
         // 调用
         PayClient client = channelService.getPayClient(id);
         // 断言
         assertSame(client, mockClient);
-        verify(payClientFactory).createOrUpdatePayClient(eq(id), eq(channel.getCode()),
-                eq(channel.getConfig()));
     }
 
     public WxPayClientConfig randomWxPayClientConfig() {

+ 3 - 2
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClientFactory.java

@@ -23,9 +23,10 @@ public interface PayClientFactory {
      * @param channelId 渠道编号
      * @param channelCode 渠道编码
      * @param config 支付配置
+     * @return 支付客户端
      */
-    <Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode,
-                                                                  Config config);
+    <Config extends PayClientConfig> PayClient createOrUpdatePayClient(Long channelId, String channelCode,
+                                                                       Config config);
 
     /**
      * 注册支付客户端 Class,用于模块中实现的 PayClient

+ 3 - 2
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java

@@ -71,8 +71,8 @@ public class PayClientFactoryImpl implements PayClientFactory {
 
     @Override
     @SuppressWarnings("unchecked")
-    public <Config extends PayClientConfig> void createOrUpdatePayClient(Long channelId, String channelCode,
-                                                                         Config config) {
+    public <Config extends PayClientConfig> PayClient createOrUpdatePayClient(Long channelId, String channelCode,
+                                                                              Config config) {
         AbstractPayClient<Config> client = (AbstractPayClient<Config>) clients.get(channelId);
         if (client == null) {
             client = this.createPayClient(channelId, channelCode, config);
@@ -81,6 +81,7 @@ public class PayClientFactoryImpl implements PayClientFactory {
         } else {
             client.refresh(config);
         }
+        return client;
     }
 
     @SuppressWarnings("unchecked")

+ 10 - 14
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java

@@ -36,6 +36,7 @@ import java.util.Objects;
 
 import static cn.hutool.core.date.DatePattern.*;
 import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig.API_VERSION_V2;
+import static cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig.API_VERSION_V3;
 
 /**
  * 微信支付抽象类,实现微信统一的接口、以及部分实现(退款)
@@ -59,19 +60,14 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
     protected void doInit(String tradeType) {
         // 创建 config 配置
         WxPayConfig payConfig = new WxPayConfig();
-        BeanUtil.copyProperties(config, payConfig, "keyContent", "privateKeyContent", "privateCertContent");
+        BeanUtil.copyProperties(config, payConfig, "keyContent", "privateKeyContent");
         payConfig.setTradeType(tradeType);
         // weixin-pay-java 无法设置内容,只允许读取文件,所以这里要创建临时文件来解决
-        if (Base64.isBase64(config.getKeyContent())) {
+        if (Objects.equals(config.getApiVersion(), API_VERSION_V2)) {
             payConfig.setKeyPath(FileUtils.createTempFile(Base64.decode(config.getKeyContent())).getPath());
-        }
-        if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {
+        } else if (Objects.equals(config.getApiVersion(), API_VERSION_V3)) {
             payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
         }
-        if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
-            payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
-        }
-//        payConfig.setCertSerialNo();
 
         // 创建 client 客户端
         client = new WxPayServiceImpl();
@@ -86,7 +82,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             switch (config.getApiVersion()) {
                 case API_VERSION_V2:
                     return doUnifiedOrderV2(reqDTO);
-                case WxPayClientConfig.API_VERSION_V3:
+                case API_VERSION_V3:
                     return doUnifiedOrderV3(reqDTO);
                 default:
                     throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
@@ -157,7 +153,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
         switch (config.getApiVersion()) {
             case API_VERSION_V2:
                 return doParseOrderNotifyV2(body);
-            case WxPayClientConfig.API_VERSION_V3:
+            case API_VERSION_V3:
                 return doParseOrderNotifyV3(body);
             default:
                 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
@@ -192,7 +188,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             switch (config.getApiVersion()) {
                 case API_VERSION_V2:
                     return doGetOrderV2(outTradeNo);
-                case WxPayClientConfig.API_VERSION_V3:
+                case API_VERSION_V3:
                     return doGetOrderV3(outTradeNo);
                 default:
                     throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
@@ -261,7 +257,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             switch (config.getApiVersion()) {
                 case API_VERSION_V2:
                     return doUnifiedRefundV2(reqDTO);
-                case WxPayClientConfig.API_VERSION_V3:
+                case API_VERSION_V3:
                     return doUnifiedRefundV3(reqDTO);
                 default:
                     throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
@@ -321,7 +317,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
         switch (config.getApiVersion()) {
             case API_VERSION_V2:
                 return doParseRefundNotifyV2(body);
-            case WxPayClientConfig.API_VERSION_V3:
+            case API_VERSION_V3:
                 return parseRefundNotifyV3(body);
             default:
                 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
@@ -358,7 +354,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             switch (config.getApiVersion()) {
                 case API_VERSION_V2:
                     return doGetRefundV2(outTradeNo, outRefundNo);
-                case WxPayClientConfig.API_VERSION_V3:
+                case API_VERSION_V3:
                     return doGetRefundV3(outTradeNo, outRefundNo);
                 default:
                     throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));

+ 2 - 1
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java

@@ -34,7 +34,8 @@ public class WxNativePayClient extends AbstractWxPayClient {
     @Override
     protected PayOrderRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
         // 构建 WxPayUnifiedOrderRequest 对象
-        WxPayUnifiedOrderRequest request = buildPayUnifiedOrderRequestV2(reqDTO);
+        WxPayUnifiedOrderRequest request = buildPayUnifiedOrderRequestV2(reqDTO)
+                .setProductId(reqDTO.getOutTradeNo()); // V2 必须传递 productId,无需在微信配置。该参数在 V3 简化,无需传递!
         // 执行请求
         WxPayNativeOrderResult response = client.createOrder(request);
 

+ 10 - 18
yudao-module-pay/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java

@@ -1,15 +1,11 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 
-import cn.hutool.core.io.IoUtil;
 import cn.iocoder.yudao.framework.common.util.validation.ValidationUtils;
 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
+import jakarta.validation.Validator;
+import jakarta.validation.constraints.NotBlank;
 import lombok.Data;
 
-import javax.validation.Validator;
-import javax.validation.constraints.NotBlank;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-
 /**
  * 微信支付的 PayClientConfig 实现类
  * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性
@@ -71,16 +67,19 @@ public class WxPayClientConfig implements PayClientConfig {
      */
     @NotBlank(message = "apiclient_key 不能为空", groups = V3.class)
     private String privateKeyContent;
-    /**
-     * apiclient_cert.pem 证书文件的对应的字符串
-     */
-    @NotBlank(message = "apiclient_cert 不能为空", groups = V3.class)
-    private String privateCertContent;
     /**
      * apiV3 密钥值
      */
     @NotBlank(message = "apiV3 密钥值不能为空", groups = V3.class)
     private String apiV3Key;
+    /**
+     * 证书序列号
+     */
+    @NotBlank(message = "证书序列号不能为空", groups = V3.class)
+    private String certSerialNo;
+
+    @Deprecated // TODO 芋艿:V2.3.0 进行移除
+    private String privateCertContent;
 
     /**
      * 分组校验 v2版本
@@ -100,11 +99,4 @@ public class WxPayClientConfig implements PayClientConfig {
                 API_VERSION_V2.equals(this.getApiVersion()) ? V2.class : V3.class);
     }
 
-    public static void main(String[] args) throws FileNotFoundException {
-        String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.p12";
-        /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_key.pem";
-        /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.pem";
-        System.out.println(IoUtil.readUtf8(new FileInputStream(path)));
-    }
-
 }

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini