Commit 222daa5366eb8169097a9546000cd1acbcf05cee

Authored by YunaiV
1 parent a51579a7

pay 缓存,使用 guava 替代 job 扫描,目的:提升启动速度,加快缓存失效

Showing 14 changed files with 146 additions and 162 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
... ... @@ -48,7 +48,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen
48 48 */
49 49 public final void init() {
50 50 doInit();
51   - log.info("[init][客户端({}) 初始化完成]", getId());
  51 + log.debug("[init][客户端({}) 初始化完成]", getId());
52 52 }
53 53  
54 54 /**
... ...
yudao-module-infra/yudao-module-infra-biz/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImplTest.java
... ... @@ -233,14 +233,38 @@ public class FileConfigServiceImplTest extends BaseDbUnitTest {
233 233  
234 234 @Test
235 235 public void testGetFileClient() {
  236 + // mock 数据
  237 + FileConfigDO fileConfig = randomFileConfigDO().setMaster(false);
  238 + fileConfigMapper.insert(fileConfig);
236 239 // 准备参数
237   - Long id = randomLongId();
  240 + Long id = fileConfig.getId();
238 241 // mock 获得 Client
239 242 FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig());
240 243 when(fileClientFactory.getFileClient(eq(id))).thenReturn(fileClient);
241 244  
242 245 // 调用,并断言
243 246 assertSame(fileClient, fileConfigService.getFileClient(id));
  247 + // 断言缓存
  248 + verify(fileClientFactory).createOrUpdateFileClient(eq(id), eq(fileConfig.getStorage()),
  249 + eq(fileConfig.getConfig()));
  250 + }
  251 +
  252 + @Test
  253 + public void testGetMasterFileClient() {
  254 + // mock 数据
  255 + FileConfigDO fileConfig = randomFileConfigDO().setMaster(true);
  256 + fileConfigMapper.insert(fileConfig);
  257 + // 准备参数
  258 + Long id = fileConfig.getId();
  259 + // mock 获得 Client
  260 + FileClient fileClient = new LocalFileClient(id, new LocalFileClientConfig());
  261 + when(fileClientFactory.getFileClient(eq(0L))).thenReturn(fileClient);
  262 +
  263 + // 调用,并断言
  264 + assertSame(fileClient, fileConfigService.getMasterFileClient());
  265 + // 断言缓存
  266 + verify(fileClientFactory).createOrUpdateFileClient(eq(0L), eq(fileConfig.getStorage()),
  267 + eq(fileConfig.getConfig()));
244 268 }
245 269  
246 270 private FileConfigDO randomFileConfigDO() {
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
... ... @@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
7 7 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
8   -import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
9 8 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
10 9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
11 10 import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskDetailRespVO;
... ... @@ -16,6 +15,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO;
16 15 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO;
17 16 import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO;
18 17 import cn.iocoder.yudao.module.pay.service.app.PayAppService;
  18 +import cn.iocoder.yudao.module.pay.service.channel.PayChannelService;
19 19 import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService;
20 20 import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
21 21 import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
... ... @@ -53,9 +53,8 @@ public class PayNotifyController {
53 53 private PayNotifyService notifyService;
54 54 @Resource
55 55 private PayAppService appService;
56   -
57 56 @Resource
58   - private PayClientFactory payClientFactory;
  57 + private PayChannelService channelService;
59 58  
60 59 @PostMapping(value = "/order/{channelId}")
61 60 @Operation(summary = "支付渠道的统一【支付】回调")
... ... @@ -66,7 +65,7 @@ public class PayNotifyController {
66 65 @RequestBody(required = false) String body) {
67 66 log.info("[notifyOrder][channelId({}) 回调数据({}/{})]", channelId, params, body);
68 67 // 1. 校验支付渠道是否存在
69   - PayClient payClient = payClientFactory.getPayClient(channelId);
  68 + PayClient payClient = channelService.getPayClient(channelId);
70 69 if (payClient == null) {
71 70 log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId);
72 71 throw exception(CHANNEL_NOT_FOUND);
... ... @@ -87,7 +86,7 @@ public class PayNotifyController {
87 86 @RequestBody(required = false) String body) {
88 87 log.info("[notifyRefund][channelId({}) 回调数据({}/{})]", channelId, params, body);
89 88 // 1. 校验支付渠道是否存在
90   - PayClient payClient = payClientFactory.getPayClient(channelId);
  89 + PayClient payClient = channelService.getPayClient(channelId);
91 90 if (payClient == null) {
92 91 log.error("[notifyCallback][渠道编号({}) 找不到对应的支付客户端]", channelId);
93 92 throw exception(CHANNEL_NOT_FOUND);
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/channel/PayChannelMapper.java
... ... @@ -28,7 +28,4 @@ public interface PayChannelMapper extends BaseMapperX<PayChannelDO> {
28 28 .eq(PayChannelDO::getStatus, status));
29 29 }
30 30  
31   - @Select("SELECT COUNT(*) FROM pay_channel WHERE update_time > #{maxUpdateTime}")
32   - Long selectCountByUpdateTimeGt(LocalDateTime maxUpdateTime);
33   -
34 31 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/job/notify/PayNotifyJob.java
... ... @@ -15,7 +15,6 @@ import javax.annotation.Resource;
15 15 * @author 芋道源码
16 16 */
17 17 @Component
18   -@TenantJob // 多租户
19 18 @Slf4j
20 19 public class PayNotifyJob implements JobHandler {
21 20  
... ... @@ -23,6 +22,7 @@ public class PayNotifyJob implements JobHandler {
23 22 private PayNotifyService payNotifyService;
24 23  
25 24 @Override
  25 + @TenantJob
26 26 public String execute(String param) throws Exception {
27 27 int notifyCount = payNotifyService.executeNotify();
28 28 return String.format("执行支付通知 %s 个", notifyCount);
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/job/order/PayOrderExpireJob.java
... ... @@ -16,13 +16,13 @@ import javax.annotation.Resource;
16 16 * @author 芋道源码
17 17 */
18 18 @Component
19   -@TenantJob
20 19 public class PayOrderExpireJob implements JobHandler {
21 20  
22 21 @Resource
23 22 private PayOrderService orderService;
24 23  
25 24 @Override
  25 + @TenantJob
26 26 public String execute(String param) {
27 27 int count = orderService.expireOrder();
28 28 return StrUtil.format("支付过期 {} 个", count);
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/job/order/PayOrderSyncJob.java
... ... @@ -18,7 +18,6 @@ import java.time.LocalDateTime;
18 18 * @author 芋道源码
19 19 */
20 20 @Component
21   -@TenantJob
22 21 public class PayOrderSyncJob implements JobHandler {
23 22  
24 23 /**
... ... @@ -34,6 +33,7 @@ public class PayOrderSyncJob implements JobHandler {
34 33 private PayOrderService orderService;
35 34  
36 35 @Override
  36 + @TenantJob
37 37 public String execute(String param) {
38 38 LocalDateTime minCreateTime = LocalDateTime.now().minus(CREATE_TIME_DURATION_BEFORE);
39 39 int count = orderService.syncOrder(minCreateTime);
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/job/refund/PayRefundSyncJob.java
... ... @@ -16,13 +16,13 @@ import javax.annotation.Resource;
16 16 * @author 芋道源码
17 17 */
18 18 @Component
19   -@TenantJob
20 19 public class PayRefundSyncJob implements JobHandler {
21 20  
22 21 @Resource
23 22 private PayRefundService refundService;
24 23  
25 24 @Override
  25 + @TenantJob
26 26 public String execute(String param) {
27 27 int count = refundService.syncRefund();
28 28 return StrUtil.format("同步退款订单 {} 个", count);
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelService.java
1 1 package cn.iocoder.yudao.module.pay.service.channel;
2 2  
3 3 import cn.iocoder.yudao.framework.common.exception.ServiceException;
  4 +import cn.iocoder.yudao.framework.pay.core.client.PayClient;
4 5 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
5 6 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
6 7 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
... ... @@ -92,4 +93,12 @@ public interface PayChannelService {
92 93 */
93 94 List<PayChannelDO> getEnableChannelList(Long appId);
94 95  
  96 + /**
  97 + * 获得指定编号的支付客户端
  98 + *
  99 + * @param id 编号
  100 + * @return 支付客户端
  101 + */
  102 + PayClient getPayClient(Long id);
  103 +
95 104 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java
1 1 package cn.iocoder.yudao.module.pay.service.channel;
2 2  
3   -import cn.hutool.core.collection.CollUtil;
4 3 import cn.hutool.core.lang.Assert;
5 4 import cn.hutool.core.util.ObjectUtil;
6 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
7   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 6 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  7 +import cn.iocoder.yudao.framework.pay.core.client.PayClient;
9 8 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
10 9 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
11 10 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
12   -import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
13 11 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelCreateReqVO;
14 12 import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateReqVO;
15 13 import cn.iocoder.yudao.module.pay.convert.channel.PayChannelConvert;
16 14 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
17 15 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
18   -import cn.iocoder.yudao.module.pay.framework.pay.wallet.WalletPayClient;
  16 +import com.google.common.cache.CacheLoader;
  17 +import com.google.common.cache.LoadingCache;
19 18 import lombok.Getter;
20   -import lombok.Setter;
21 19 import lombok.extern.slf4j.Slf4j;
22   -import org.springframework.scheduling.annotation.Scheduled;
23 20 import org.springframework.stereotype.Service;
24 21 import org.springframework.validation.annotation.Validated;
25 22  
26   -import javax.annotation.PostConstruct;
27 23 import javax.annotation.Resource;
28 24 import javax.validation.Validator;
29   -import java.time.LocalDateTime;
  25 +import java.time.Duration;
30 26 import java.util.Collection;
31   -import java.util.Collections;
32 27 import java.util.List;
33   -import java.util.concurrent.TimeUnit;
34 28  
35 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  30 +import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
36 31 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
37 32  
38 33 /**
... ... @@ -45,71 +40,34 @@ import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
45 40 @Validated
46 41 public class PayChannelServiceImpl implements PayChannelService {
47 42  
48   - @Getter // 为了方便测试,这里提供 getter 方法
49   - @Setter
50   - private volatile List<PayChannelDO> channelCache;
  43 + /**
  44 + * {@link PayClient} 缓存,通过它异步清空 smsClientFactory
  45 + */
  46 + @Getter
  47 + private final LoadingCache<Long, PayClient> clientCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
  48 + new CacheLoader<Long, PayClient>() {
  49 +
  50 + @Override
  51 + public PayClient load(Long id) {
  52 + // 查询,然后尝试清空
  53 + PayChannelDO channel = payChannelMapper.selectById(id);
  54 + if (channel != null) {
  55 + payClientFactory.createOrUpdatePayClient(channel.getId(), channel.getCode(), channel.getConfig());
  56 + }
  57 + return payClientFactory.getPayClient(id);
  58 + }
  59 +
  60 + });
51 61  
52 62 @Resource
53 63 private PayClientFactory payClientFactory;
54 64  
55 65 @Resource
56   - private PayChannelMapper channelMapper;
  66 + private PayChannelMapper payChannelMapper;
57 67  
58 68 @Resource
59 69 private Validator validator;
60 70  
61   - /**
62   - * 初始化 {@link #payClientFactory} 缓存
63   - */
64   - @PostConstruct
65   - public void initLocalCache() {
66   - // 注册钱包支付 Class
67   - payClientFactory.registerPayClientClass(PayChannelEnum.WALLET, WalletPayClient.class);
68   -
69   - // 注意:忽略自动多租户,因为要全局初始化缓存
70   - TenantUtils.executeIgnore(() -> {
71   - // 第一步:查询数据
72   - List<PayChannelDO> channels = Collections.emptyList();
73   - try {
74   - channels = channelMapper.selectList();
75   - } catch (Throwable ex) {
76   - if (!ex.getMessage().contains("doesn't exist")) {
77   - throw ex;
78   - }
79   - log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://doc.iocoder.cn/pay/build/ 开启]");
80   - }
81   - log.info("[initLocalCache][缓存支付渠道,数量为:{}]", channels.size());
82   -
83   - // 第二步:构建缓存:创建或更新支付 Client
84   - channels.forEach(payChannel -> payClientFactory.createOrUpdatePayClient(payChannel.getId(),
85   - payChannel.getCode(), payChannel.getConfig()));
86   - this.channelCache = channels;
87   - });
88   - }
89   -
90   - /**
91   - * 通过定时任务轮询,刷新缓存
92   - *
93   - * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新
94   - */
95   - @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
96   - public void refreshLocalCache() {
97   - // 注意:忽略自动多租户,因为要全局初始化缓存
98   - TenantUtils.executeIgnore(() -> {
99   - // 情况一:如果缓存里没有数据,则直接刷新缓存
100   - if (CollUtil.isEmpty(channelCache)) {
101   - initLocalCache();
102   - return;
103   - }
104   -
105   - // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
106   - LocalDateTime maxTime = CollectionUtils.getMaxValue(channelCache, PayChannelDO::getUpdateTime);
107   - if (channelMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
108   - initLocalCache();
109   - }
110   - });
111   - }
112   -
113 71 @Override
114 72 public Long createChannel(PayChannelCreateReqVO reqVO) {
115 73 // 断言是否有重复的
... ... @@ -121,10 +79,7 @@ public class PayChannelServiceImpl implements PayChannelService {
121 79 // 新增渠道
122 80 PayChannelDO channel = PayChannelConvert.INSTANCE.convert(reqVO)
123 81 .setConfig(parseConfig(reqVO.getCode(), reqVO.getConfig()));
124   - channelMapper.insert(channel);
125   -
126   - // 刷新缓存
127   - initLocalCache();
  82 + payChannelMapper.insert(channel);
128 83 return channel.getId();
129 84 }
130 85  
... ... @@ -136,10 +91,10 @@ public class PayChannelServiceImpl implements PayChannelService {
136 91 // 更新
137 92 PayChannelDO channel = PayChannelConvert.INSTANCE.convert(updateReqVO)
138 93 .setConfig(parseConfig(dbChannel.getCode(), updateReqVO.getConfig()));
139   - channelMapper.updateById(channel);
  94 + payChannelMapper.updateById(channel);
140 95  
141   - // 刷新缓存
142   - initLocalCache();
  96 + // 清空缓存
  97 + clearCache(channel.getId());
143 98 }
144 99  
145 100 /**
... ... @@ -169,14 +124,23 @@ public class PayChannelServiceImpl implements PayChannelService {
169 124 validateChannelExists(id);
170 125  
171 126 // 删除
172   - channelMapper.deleteById(id);
  127 + payChannelMapper.deleteById(id);
173 128  
174   - // 刷新缓存
175   - initLocalCache();
  129 + // 清空缓存
  130 + clearCache(id);
  131 + }
  132 +
  133 + /**
  134 + * 删除缓存
  135 + *
  136 + * @param id 渠道编号
  137 + */
  138 + private void clearCache(Long id) {
  139 + clientCache.invalidate(id);
176 140 }
177 141  
178 142 private PayChannelDO validateChannelExists(Long id) {
179   - PayChannelDO channel = channelMapper.selectById(id);
  143 + PayChannelDO channel = payChannelMapper.selectById(id);
180 144 if (channel == null) {
181 145 throw exception(CHANNEL_NOT_FOUND);
182 146 }
... ... @@ -185,29 +149,29 @@ public class PayChannelServiceImpl implements PayChannelService {
185 149  
186 150 @Override
187 151 public PayChannelDO getChannel(Long id) {
188   - return channelMapper.selectById(id);
  152 + return payChannelMapper.selectById(id);
189 153 }
190 154  
191 155 @Override
192 156 public List<PayChannelDO> getChannelListByAppIds(Collection<Long> appIds) {
193   - return channelMapper.selectListByAppIds(appIds);
  157 + return payChannelMapper.selectListByAppIds(appIds);
194 158 }
195 159  
196 160 @Override
197 161 public PayChannelDO getChannelByAppIdAndCode(Long appId, String code) {
198   - return channelMapper.selectByAppIdAndCode(appId, code);
  162 + return payChannelMapper.selectByAppIdAndCode(appId, code);
199 163 }
200 164  
201 165 @Override
202 166 public PayChannelDO validPayChannel(Long id) {
203   - PayChannelDO channel = channelMapper.selectById(id);
  167 + PayChannelDO channel = payChannelMapper.selectById(id);
204 168 validPayChannel(channel);
205 169 return channel;
206 170 }
207 171  
208 172 @Override
209 173 public PayChannelDO validPayChannel(Long appId, String code) {
210   - PayChannelDO channel = channelMapper.selectByAppIdAndCode(appId, code);
  174 + PayChannelDO channel = payChannelMapper.selectByAppIdAndCode(appId, code);
211 175 validPayChannel(channel);
212 176 return channel;
213 177 }
... ... @@ -223,7 +187,12 @@ public class PayChannelServiceImpl implements PayChannelService {
223 187  
224 188 @Override
225 189 public List<PayChannelDO> getEnableChannelList(Long appId) {
226   - return channelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus());
  190 + return payChannelMapper.selectListByAppId(appId, CommonStatusEnum.ENABLE.getStatus());
  191 + }
  192 +
  193 + @Override
  194 + public PayClient getPayClient(Long id) {
  195 + return clientCache.getUnchecked(id);
227 196 }
228 197  
229 198 }
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
... ... @@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
8 8 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
9 9 import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
10 10 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
11   -import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
12 11 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderRespDTO;
13 12 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
14 13 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
... ... @@ -61,9 +60,6 @@ public class PayOrderServiceImpl implements PayOrderService {
61 60 private PayProperties payProperties;
62 61  
63 62 @Resource
64   - private PayClientFactory payClientFactory;
65   -
66   - @Resource
67 63 private PayOrderMapper orderMapper;
68 64 @Resource
69 65 private PayOrderExtensionMapper orderExtensionMapper;
... ... @@ -134,7 +130,7 @@ public class PayOrderServiceImpl implements PayOrderService {
134 130 PayOrderDO order = validateOrderCanSubmit(reqVO.getId());
135 131 // 1.32 校验支付渠道是否有效
136 132 PayChannelDO channel = validateChannelCanSubmit(order.getAppId(), reqVO.getChannelCode());
137   - PayClient client = payClientFactory.getPayClient(channel.getId());
  133 + PayClient client = channelService.getPayClient(channel.getId());
138 134  
139 135 // 2. 插入 PayOrderExtensionDO
140 136 String no = noRedisDAO.generate(payProperties.getOrderNoPrefix());
... ... @@ -205,7 +201,7 @@ public class PayOrderServiceImpl implements PayOrderService {
205 201 throw exception(ORDER_EXTENSION_IS_PAID);
206 202 }
207 203 // 情况二:调用三方接口,查询支付单状态,是不是已支付
208   - PayClient payClient = payClientFactory.getPayClient(orderExtension.getChannelId());
  204 + PayClient payClient = channelService.getPayClient(orderExtension.getChannelId());
209 205 if (payClient == null) {
210 206 log.error("[validateOrderCanSubmit][渠道编号({}) 找不到对应的支付客户端]", orderExtension.getChannelId());
211 207 return;
... ... @@ -224,7 +220,7 @@ public class PayOrderServiceImpl implements PayOrderService {
224 220 appService.validPayApp(appId);
225 221 // 校验支付渠道是否有效
226 222 PayChannelDO channel = channelService.validPayChannel(appId, channelCode);
227   - PayClient client = payClientFactory.getPayClient(channel.getId());
  223 + PayClient client = channelService.getPayClient(channel.getId());
228 224 if (client == null) {
229 225 log.error("[validatePayChannelCanSubmit][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
230 226 throw exception(CHANNEL_NOT_FOUND);
... ... @@ -458,7 +454,7 @@ public class PayOrderServiceImpl implements PayOrderService {
458 454 private boolean syncOrder(PayOrderExtensionDO orderExtension) {
459 455 try {
460 456 // 1.1 查询支付订单信息
461   - PayClient payClient = payClientFactory.getPayClient(orderExtension.getChannelId());
  457 + PayClient payClient = channelService.getPayClient(orderExtension.getChannelId());
462 458 if (payClient == null) {
463 459 log.error("[syncOrder][渠道编号({}) 找不到对应的支付客户端]", orderExtension.getChannelId());
464 460 return false;
... ... @@ -513,7 +509,7 @@ public class PayOrderServiceImpl implements PayOrderService {
513 509 return false;
514 510 }
515 511 // 情况二:调用三方接口,查询支付单状态,是不是已支付/已退款
516   - PayClient payClient = payClientFactory.getPayClient(orderExtension.getChannelId());
  512 + PayClient payClient = channelService.getPayClient(orderExtension.getChannelId());
517 513 if (payClient == null) {
518 514 log.error("[expireOrder][渠道编号({}) 找不到对应的支付客户端]", orderExtension.getChannelId());
519 515 return false;
... ...
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/refund/PayRefundServiceImpl.java
... ... @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.extra.spring.SpringUtil;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 6 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
7   -import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
8 7 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO;
9 8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
10 9 import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum;
... ... @@ -53,9 +52,6 @@ public class PayRefundServiceImpl implements PayRefundService {
53 52 private PayProperties payProperties;
54 53  
55 54 @Resource
56   - private PayClientFactory payClientFactory;
57   -
58   - @Resource
59 55 private PayRefundMapper refundMapper;
60 56 @Resource
61 57 private PayNoRedisDAO noRedisDAO;
... ... @@ -102,7 +98,7 @@ public class PayRefundServiceImpl implements PayRefundService {
102 98 PayOrderDO order = validatePayOrderCanRefund(reqDTO);
103 99 // 1.3 校验支付渠道是否有效
104 100 PayChannelDO channel = channelService.validPayChannel(order.getChannelId());
105   - PayClient client = payClientFactory.getPayClient(channel.getId());
  101 + PayClient client = channelService.getPayClient(channel.getId());
106 102 if (client == null) {
107 103 log.error("[refund][渠道编号({}) 找不到对应的支付客户端]", channel.getId());
108 104 throw exception(CHANNEL_NOT_FOUND);
... ... @@ -305,7 +301,7 @@ public class PayRefundServiceImpl implements PayRefundService {
305 301 private boolean syncRefund(PayRefundDO refund) {
306 302 try {
307 303 // 1.1 查询退款订单信息
308   - PayClient payClient = payClientFactory.getPayClient(refund.getChannelId());
  304 + PayClient payClient = channelService.getPayClient(refund.getChannelId());
309 305 if (payClient == null) {
310 306 log.error("[syncRefund][渠道编号({}) 找不到对应的支付客户端]", refund.getChannelId());
311 307 return false;
... ...
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceTest.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.pay.service.channel;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  5 +import cn.iocoder.yudao.framework.pay.core.client.PayClient;
5 6 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
6 7 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
7 8 import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig;
... ... @@ -12,30 +13,26 @@ import cn.iocoder.yudao.module.pay.controller.admin.channel.vo.PayChannelUpdateR
12 13 import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO;
13 14 import cn.iocoder.yudao.module.pay.dal.mysql.channel.PayChannelMapper;
14 15 import com.alibaba.fastjson.JSON;
15   -import org.junit.jupiter.api.BeforeEach;
16 16 import org.junit.jupiter.api.Test;
17 17 import org.springframework.boot.test.mock.mockito.MockBean;
18 18 import org.springframework.context.annotation.Import;
19 19  
20 20 import javax.annotation.Resource;
21 21 import javax.validation.Validator;
22   -
23   -import java.time.Duration;
24 22 import java.util.Collections;
25 23 import java.util.List;
26 24  
27   -import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.addTime;
28 25 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
29 26 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
30 27 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
31 28 import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*;
32 29 import static org.junit.jupiter.api.Assertions.*;
  30 +import static org.mockito.ArgumentMatchers.eq;
  31 +import static org.mockito.Mockito.*;
33 32  
34 33 @Import({PayChannelServiceImpl.class})
35 34 public class PayChannelServiceTest extends BaseDbUnitTest {
36 35  
37   - private static final String ALIPAY_SERVER_URL = "https://openapi.alipay.com/gateway.do";
38   -
39 36 @Resource
40 37 private PayChannelServiceImpl channelService;
41 38  
... ... @@ -47,45 +44,6 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
47 44 @MockBean
48 45 private Validator validator;
49 46  
50   - @BeforeEach
51   - public void setUp() {
52   - channelService.setChannelCache(null);
53   - }
54   -
55   - @Test
56   - public void testInitLocalCache() {
57   - // mock 数据
58   - PayChannelDO dbChannel = randomPojo(PayChannelDO.class,
59   - o -> o.setConfig(randomWxPayClientConfig()));
60   - channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
61   -
62   - // 调用
63   - channelService.initLocalCache();
64   - // 校验缓存
65   - assertEquals(1, channelService.getChannelCache().size());
66   - assertEquals(dbChannel, channelService.getChannelCache().get(0));
67   - }
68   -
69   - @Test
70   - public void testRefreshLocalCache() {
71   - // mock 数据 01
72   - PayChannelDO dbChannel = randomPojo(PayChannelDO.class,
73   - o -> o.setConfig(randomWxPayClientConfig()).setUpdateTime(addTime(Duration.ofMinutes(-2))));
74   - channelMapper.insert(dbChannel);// @Sql: 先插入出一条存在的数据
75   - channelService.initLocalCache();
76   - // mock 数据 02
77   - PayChannelDO dbChannel02 = randomPojo(PayChannelDO.class,
78   - o -> o.setConfig(randomWxPayClientConfig()));
79   - channelMapper.insert(dbChannel02);// @Sql: 先插入出一条存在的数据
80   -
81   - // 调用
82   - channelService.refreshLocalCache();
83   - // 校验缓存
84   - assertEquals(2, channelService.getChannelCache().size());
85   - assertEquals(dbChannel, channelService.getChannelCache().get(0));
86   - assertEquals(dbChannel02, channelService.getChannelCache().get(1));
87   - }
88   -
89 47 @Test
90 48 public void testCreateChannel_success() {
91 49 // 准备参数
... ... @@ -103,8 +61,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
103 61 assertPojoEquals(reqVO, channel, "config");
104 62 assertPojoEquals(config, channel.getConfig());
105 63 // 校验缓存
106   - assertEquals(1, channelService.getChannelCache().size());
107   - assertEquals(channel, channelService.getChannelCache().get(0));
  64 + assertNull(channelService.getClientCache().getIfPresent(channelId));
108 65 }
109 66  
110 67 @Test
... ... @@ -146,8 +103,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
146 103 assertPojoEquals(reqVO, channel, "config");
147 104 assertPojoEquals(config, channel.getConfig());
148 105 // 校验缓存
149   - assertEquals(1, channelService.getChannelCache().size());
150   - assertEquals(channel, channelService.getChannelCache().get(0));
  106 + assertNull(channelService.getClientCache().getIfPresent(channel.getId()));
151 107 }
152 108  
153 109 @Test
... ... @@ -179,7 +135,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
179 135 // 校验数据不存在了
180 136 assertNull(channelMapper.selectById(id));
181 137 // 校验缓存
182   - assertEquals(0, channelService.getChannelCache().size());
  138 + assertNull(channelService.getClientCache().getIfPresent(id));
183 139 }
184 140  
185 141 @Test
... ... @@ -344,6 +300,28 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
344 300 assertPojoEquals(channel, dbChannel03);
345 301 }
346 302  
  303 + @Test
  304 + public void testGetPayClient() {
  305 + // mock 数据
  306 + PayChannelDO channel = randomPojo(PayChannelDO.class, o -> {
  307 + o.setCode(PayChannelEnum.ALIPAY_APP.getCode());
  308 + o.setConfig(randomAlipayPayClientConfig());
  309 + });
  310 + channelMapper.insert(channel);
  311 + // mock 参数
  312 + Long id = channel.getId();
  313 + // mock 方法
  314 + PayClient mockClient = mock(PayClient.class);
  315 + when(payClientFactory.getPayClient(eq(id))).thenReturn(mockClient);
  316 +
  317 + // 调用
  318 + PayClient client = channelService.getPayClient(id);
  319 + // 断言
  320 + assertSame(client, mockClient);
  321 + verify(payClientFactory).createOrUpdatePayClient(eq(id), eq(channel.getCode()),
  322 + eq(channel.getConfig()));
  323 + }
  324 +
347 325 public WxPayClientConfig randomWxPayClientConfig() {
348 326 return new WxPayClientConfig()
349 327 .setAppId(randomString())
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sensitiveword/SensitiveWordServiceImpl.java
... ... @@ -18,6 +18,7 @@ import lombok.Getter;
18 18 import lombok.extern.slf4j.Slf4j;
19 19 import org.springframework.scheduling.annotation.Scheduled;
20 20 import org.springframework.stereotype.Service;
  21 +import org.springframework.util.Assert;
21 22 import org.springframework.validation.annotation.Validated;
22 23  
23 24 import javax.annotation.PostConstruct;
... ... @@ -43,6 +44,11 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SENSITIVE_
43 44 public class SensitiveWordServiceImpl implements SensitiveWordService {
44 45  
45 46 /**
  47 + * 是否开启敏感词功能
  48 + */
  49 + private static final Boolean ENABLED = false;
  50 +
  51 + /**
46 52 * 敏感词列表缓存
47 53 */
48 54 @Getter
... ... @@ -75,6 +81,10 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
75 81 */
76 82 @PostConstruct
77 83 public void initLocalCache() {
  84 + if (!ENABLED) {
  85 + return;
  86 + }
  87 +
78 88 // 第一步:查询数据
79 89 List<SensitiveWordDO> sensitiveWords = sensitiveWordMapper.selectList();
80 90 log.info("[initLocalCache][缓存敏感词,数量为:{}]", sensitiveWords.size());
... ... @@ -216,6 +226,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
216 226  
217 227 @Override
218 228 public List<String> validateText(String text, List<String> tags) {
  229 + Assert.isTrue(ENABLED, "敏感词功能未开启,请将 ENABLED 设置为 true");
  230 +
  231 + // 无标签时,默认所有
219 232 if (CollUtil.isEmpty(tags)) {
220 233 return defaultSensitiveWordTrie.validate(text);
221 234 }
... ... @@ -233,6 +246,9 @@ public class SensitiveWordServiceImpl implements SensitiveWordService {
233 246  
234 247 @Override
235 248 public boolean isTextValid(String text, List<String> tags) {
  249 + Assert.isTrue(ENABLED, "敏感词功能未开启,请将 ENABLED 设置为 true");
  250 +
  251 + // 无标签时,默认所有
236 252 if (CollUtil.isEmpty(tags)) {
237 253 return defaultSensitiveWordTrie.isValid(text);
238 254 }
... ...