Commit 48fd4972da171f10c6ac2012f05fea58a5e86562

Authored by YunaiV
1 parent ab800aa8

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

yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/convert/sms/SmsChannelConvert.java
... ... @@ -32,8 +32,8 @@ public interface SmsChannelConvert {
32 32  
33 33 PageResult<SmsChannelRespVO> convertPage(PageResult<SmsChannelDO> page);
34 34  
35   - List<SmsChannelProperties> convertList02(List<SmsChannelDO> list);
36   -
37 35 List<SmsChannelSimpleRespVO> convertList03(List<SmsChannelDO> list);
38 36  
  37 + SmsChannelProperties convert02(SmsChannelDO channel);
  38 +
39 39 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsChannelMapper.java
... ... @@ -6,9 +6,6 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 6 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
7 7 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9   -import org.apache.ibatis.annotations.Select;
10   -
11   -import java.time.LocalDateTime;
12 9  
13 10 @Mapper
14 11 public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
... ... @@ -21,7 +18,8 @@ public interface SmsChannelMapper extends BaseMapperX&lt;SmsChannelDO&gt; {
21 18 .orderByDesc(SmsChannelDO::getId));
22 19 }
23 20  
24   - @Select("SELECT COUNT(*) FROM system_sms_channel WHERE update_time > #{maxUpdateTime}")
25   - Long selectCountByUpdateTimeGt(LocalDateTime maxTime);
  21 + default SmsChannelDO selectByCode(String code) {
  22 + return selectOne(SmsChannelDO::getCode, code);
  23 + }
26 24  
27 25 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelService.java
1 1 package cn.iocoder.yudao.module.system.service.sms;
2 2  
3 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
4 5 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
5 6 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
6 7 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
... ... @@ -13,16 +14,11 @@ import java.util.List;
13 14 * 短信渠道 Service 接口
14 15 *
15 16 * @author zzf
16   - * @date 2021/1/25 9:24
  17 + * @since 2021/1/25 9:24
17 18 */
18 19 public interface SmsChannelService {
19 20  
20 21 /**
21   - * 初始化短信客户端
22   - */
23   - void initLocalCache();
24   -
25   - /**
26 22 * 创建短信渠道
27 23 *
28 24 * @param createReqVO 创建信息
... ... @@ -67,4 +63,20 @@ public interface SmsChannelService {
67 63 */
68 64 PageResult<SmsChannelDO> getSmsChannelPage(SmsChannelPageReqVO pageReqVO);
69 65  
  66 + /**
  67 + * 获得短信客户端
  68 + *
  69 + * @param id 编号
  70 + * @return 短信客户端
  71 + */
  72 + SmsClient getSmsClient(Long id);
  73 +
  74 + /**
  75 + * 获得短信客户端
  76 + *
  77 + * @param code 编码
  78 + * @return 短信客户端
  79 + */
  80 + SmsClient getSmsClient(String code);
  81 +
70 82 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.service.sms;
2 2  
3   -import cn.hutool.core.collection.CollUtil;
  3 +import cn.hutool.core.util.StrUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
  6 +import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
5 7 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
6 8 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
7 9 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
... ... @@ -10,20 +12,18 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannel
10 12 import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert;
11 13 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
12 14 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
  15 +import com.google.common.cache.CacheLoader;
  16 +import com.google.common.cache.LoadingCache;
13 17 import lombok.Getter;
  18 +import lombok.SneakyThrows;
14 19 import lombok.extern.slf4j.Slf4j;
15   -import org.springframework.scheduling.annotation.Scheduled;
16 20 import org.springframework.stereotype.Service;
17 21  
18   -import javax.annotation.PostConstruct;
19 22 import javax.annotation.Resource;
20   -import java.time.LocalDateTime;
21   -import java.util.Collections;
  23 +import java.time.Duration;
22 24 import java.util.List;
23   -import java.util.concurrent.TimeUnit;
24 25  
25 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
26   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
27 27 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
28 28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
29 29  
... ... @@ -37,10 +37,44 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNE
37 37 public class SmsChannelServiceImpl implements SmsChannelService {
38 38  
39 39 /**
40   - * 短信渠道列表的缓存
  40 + * {@link SmsClient} 缓存,通过它异步刷新 smsClientFactory
41 41 */
42 42 @Getter
43   - private volatile List<SmsChannelDO> channelCache = Collections.emptyList();
  43 + private final LoadingCache<Long, SmsClient> idClientCache = CacheUtils.buildAsyncReloadingCache(Duration.ofSeconds(10L),
  44 + new CacheLoader<Long, SmsClient>() {
  45 +
  46 + @Override
  47 + public SmsClient load(Long id) {
  48 + // 查询,然后尝试刷新
  49 + SmsChannelDO channel = smsChannelMapper.selectById(id);
  50 + if (channel != null) {
  51 + SmsChannelProperties properties = SmsChannelConvert.INSTANCE.convert02(channel);
  52 + smsClientFactory.createOrUpdateSmsClient(properties);
  53 + }
  54 + return smsClientFactory.getSmsClient(id);
  55 + }
  56 +
  57 + });
  58 +
  59 + /**
  60 + * {@link SmsClient} 缓存,通过它异步刷新 smsClientFactory
  61 + */
  62 + @Getter
  63 + private final LoadingCache<String, SmsClient> codeClientCache = CacheUtils.buildAsyncReloadingCache(Duration.ofSeconds(60L),
  64 + new CacheLoader<String, SmsClient>() {
  65 +
  66 + @Override
  67 + public SmsClient load(String code) {
  68 + // 查询,然后尝试刷新
  69 + SmsChannelDO channel = smsChannelMapper.selectByCode(code);
  70 + if (channel != null) {
  71 + SmsChannelProperties properties = SmsChannelConvert.INSTANCE.convert02(channel);
  72 + smsClientFactory.createOrUpdateSmsClient(properties);
  73 + }
  74 + return smsClientFactory.getSmsClient(code);
  75 + }
  76 +
  77 + });
44 78  
45 79 @Resource
46 80 private SmsClientFactory smsClientFactory;
... ... @@ -52,65 +86,32 @@ public class SmsChannelServiceImpl implements SmsChannelService {
52 86 private SmsTemplateService smsTemplateService;
53 87  
54 88 @Override
55   - @PostConstruct
56   - public void initLocalCache() {
57   - // 第一步:查询数据
58   - List<SmsChannelDO> channels = smsChannelMapper.selectList();
59   - log.info("[initLocalCache][缓存短信渠道,数量为:{}]", channels.size());
60   -
61   - // 第二步:构建缓存:创建或更新短信 Client
62   - List<SmsChannelProperties> propertiesList = SmsChannelConvert.INSTANCE.convertList02(channels);
63   - propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
64   - this.channelCache = channels;
65   - }
66   -
67   - /**
68   - * 通过定时任务轮询,刷新缓存
69   - *
70   - * 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新
71   - */
72   - @Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
73   - public void refreshLocalCache() {
74   - // 情况一:如果缓存里没有数据,则直接刷新缓存
75   - if (CollUtil.isEmpty(channelCache)) {
76   - initLocalCache();
77   - return;
78   - }
79   -
80   - // 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
81   - LocalDateTime maxTime = getMaxValue(channelCache, SmsChannelDO::getUpdateTime);
82   - if (smsChannelMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
83   - initLocalCache();
84   - }
85   - }
86   -
87   - @Override
88 89 public Long createSmsChannel(SmsChannelCreateReqVO createReqVO) {
89 90 // 插入
90   - SmsChannelDO smsChannel = SmsChannelConvert.INSTANCE.convert(createReqVO);
91   - smsChannelMapper.insert(smsChannel);
  91 + SmsChannelDO channel = SmsChannelConvert.INSTANCE.convert(createReqVO);
  92 + smsChannelMapper.insert(channel);
92 93  
93   - // 刷新缓存
94   - initLocalCache();
95   - return smsChannel.getId();
  94 + // 清空缓存
  95 + clearCache(channel.getId(), null);
  96 + return channel.getId();
96 97 }
97 98  
98 99 @Override
99 100 public void updateSmsChannel(SmsChannelUpdateReqVO updateReqVO) {
100 101 // 校验存在
101   - validateSmsChannelExists(updateReqVO.getId());
  102 + SmsChannelDO channel = validateSmsChannelExists(updateReqVO.getId());
102 103 // 更新
103 104 SmsChannelDO updateObj = SmsChannelConvert.INSTANCE.convert(updateReqVO);
104 105 smsChannelMapper.updateById(updateObj);
105 106  
106   - // 刷新缓存
107   - initLocalCache();
  107 + // 清空缓存
  108 + clearCache(updateReqVO.getId(), channel.getCode());
108 109 }
109 110  
110 111 @Override
111 112 public void deleteSmsChannel(Long id) {
112 113 // 校验存在
113   - validateSmsChannelExists(id);
  114 + SmsChannelDO channel = validateSmsChannelExists(id);
114 115 // 校验是否有在使用该账号的模版
115 116 if (smsTemplateService.countByChannelId(id) > 0) {
116 117 throw exception(SMS_CHANNEL_HAS_CHILDREN);
... ... @@ -118,14 +119,28 @@ public class SmsChannelServiceImpl implements SmsChannelService {
118 119 // 删除
119 120 smsChannelMapper.deleteById(id);
120 121  
121   - // 刷新缓存
122   - initLocalCache();
  122 + // 清空缓存
  123 + clearCache(id, channel.getCode());
  124 + }
  125 +
  126 + /**
  127 + * 清空指定渠道编号的缓存
  128 + *
  129 + * @param id 渠道编号
  130 + */
  131 + private void clearCache(Long id, String code) {
  132 + idClientCache.invalidate(id);
  133 + if (StrUtil.isNotEmpty(code)) {
  134 + codeClientCache.invalidate(code);
  135 + }
123 136 }
124 137  
125   - private void validateSmsChannelExists(Long id) {
126   - if (smsChannelMapper.selectById(id) == null) {
  138 + private SmsChannelDO validateSmsChannelExists(Long id) {
  139 + SmsChannelDO channel = smsChannelMapper.selectById(id);
  140 + if (channel == null) {
127 141 throw exception(SMS_CHANNEL_NOT_EXISTS);
128 142 }
  143 + return channel;
129 144 }
130 145  
131 146 @Override
... ... @@ -143,4 +158,14 @@ public class SmsChannelServiceImpl implements SmsChannelService {
143 158 return smsChannelMapper.selectPage(pageReqVO);
144 159 }
145 160  
  161 + @Override
  162 + public SmsClient getSmsClient(Long id) {
  163 + return idClientCache.getUnchecked(id);
  164 + }
  165 +
  166 + @Override
  167 + public SmsClient getSmsClient(String code) {
  168 + return codeClientCache.getUnchecked(code);
  169 + }
  170 +
146 171 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java
... ... @@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
8 8 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
9 9 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
10 10 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
11   -import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
12 11 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
13 12 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
14 13 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
... ... @@ -50,9 +49,6 @@ public class SmsSendServiceImpl implements SmsSendService {
50 49 private SmsLogService smsLogService;
51 50  
52 51 @Resource
53   - private SmsClientFactory smsClientFactory;
54   -
55   - @Resource
56 52 private SmsProducer smsProducer;
57 53  
58 54 @Override
... ... @@ -95,7 +91,6 @@ public class SmsSendServiceImpl implements SmsSendService {
95 91 // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
96 92 Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
97 93 && CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
98   - ;
99 94 String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
100 95 Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams);
101 96  
... ... @@ -132,7 +127,7 @@ public class SmsSendServiceImpl implements SmsSendService {
132 127 /**
133 128 * 将参数模板,处理成有序的 KeyValue 数组
134 129 * <p>
135   - * 原因是,部分短信平台并不是使用 key 作为参数,而是数组下标,例如说腾讯云 https://cloud.tencent.com/document/product/382/39023
  130 + * 原因是,部分短信平台并不是使用 key 作为参数,而是数组下标,例如说 <a href="https://cloud.tencent.com/document/product/382/39023">腾讯云</a>
136 131 *
137 132 * @param template 短信模板
138 133 * @param templateParams 原始参数
... ... @@ -160,7 +155,7 @@ public class SmsSendServiceImpl implements SmsSendService {
160 155 @Override
161 156 public void doSendSms(SmsSendMessage message) {
162 157 // 获得渠道对应的 SmsClient 客户端
163   - SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId());
  158 + SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId());
164 159 Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId());
165 160 // 发送短信
166 161 SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(),
... ... @@ -173,7 +168,7 @@ public class SmsSendServiceImpl implements SmsSendService {
173 168 @Override
174 169 public void receiveSmsStatus(String channelCode, String text) throws Throwable {
175 170 // 获得渠道对应的 SmsClient 客户端
176   - SmsClient smsClient = smsClientFactory.getSmsClient(channelCode);
  171 + SmsClient smsClient = smsChannelService.getSmsClient(channelCode);
177 172 Assert.notNull(smsClient, "短信客户端({}) 不存在", channelCode);
178 173 // 解析内容
179 174 List<SmsReceiveRespDTO> receiveResults = smsClient.parseSmsReceiveStatus(text);
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java
... ... @@ -2,11 +2,14 @@ package cn.iocoder.yudao.module.system.service.sms;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
5 6 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
  7 +import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
6 8 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
7 9 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
8 10 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
9 11 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
  12 +import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert;
10 13 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
11 14 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
12 15 import org.junit.jupiter.api.Test;
... ... @@ -19,7 +22,8 @@ import java.util.List;
19 22 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
20 23 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
21 24 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
22   -import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
  25 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
  26 +import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
23 27 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
24 28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
25 29 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
... ... @@ -42,27 +46,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
42 46 private SmsTemplateService smsTemplateService;
43 47  
44 48 @Test
45   - public void testInitLocalCache_success() {
46   - // mock 数据
47   - SmsChannelDO smsChannelDO01 = randomPojo(SmsChannelDO.class);
48   - smsChannelMapper.insert(smsChannelDO01);
49   - SmsChannelDO smsChannelDO02 = randomPojo(SmsChannelDO.class);
50   - smsChannelMapper.insert(smsChannelDO02);
51   -
52   - // 调用
53   - smsChannelService.initLocalCache();
54   - // 校验调用
55   - verify(smsClientFactory, times(1)).createOrUpdateSmsClient(
56   - argThat(properties -> isPojoEquals(smsChannelDO01, properties)));
57   - verify(smsClientFactory, times(1)).createOrUpdateSmsClient(
58   - argThat(properties -> isPojoEquals(smsChannelDO02, properties)));
59   - // 断言 channelCache 缓存
60   - assertEquals(2, smsChannelService.getChannelCache().size());
61   - assertPojoEquals(smsChannelDO01, smsChannelService.getChannelCache().get(0));
62   - assertPojoEquals(smsChannelDO02, smsChannelService.getChannelCache().get(1));
63   - }
64   -
65   - @Test
66 49 public void testCreateSmsChannel_success() {
67 50 // 准备参数
68 51 SmsChannelCreateReqVO reqVO = randomPojo(SmsChannelCreateReqVO.class, o -> o.setStatus(randomCommonStatus()));
... ... @@ -74,6 +57,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
74 57 // 校验记录的属性是否正确
75 58 SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId);
76 59 assertPojoEquals(reqVO, smsChannel);
  60 + // 断言 cache
  61 + assertNull(smsChannelService.getIdClientCache().getIfPresent(smsChannel.getId()));
  62 + assertNull(smsChannelService.getCodeClientCache().getIfPresent(smsChannel.getCode()));
77 63 }
78 64  
79 65 @Test
... ... @@ -93,6 +79,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
93 79 // 校验是否更新正确
94 80 SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的
95 81 assertPojoEquals(reqVO, smsChannel);
  82 + // 断言 cache
  83 + assertNull(smsChannelService.getIdClientCache().getIfPresent(smsChannel.getId()));
  84 + assertNull(smsChannelService.getCodeClientCache().getIfPresent(smsChannel.getCode()));
96 85 }
97 86  
98 87 @Test
... ... @@ -116,6 +105,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
116 105 smsChannelService.deleteSmsChannel(id);
117 106 // 校验数据不存在了
118 107 assertNull(smsChannelMapper.selectById(id));
  108 + // 断言 cache
  109 + assertNull(smsChannelService.getIdClientCache().getIfPresent(dbSmsChannel.getId()));
  110 + assertNull(smsChannelService.getCodeClientCache().getIfPresent(dbSmsChannel.getCode()));
119 111 }
120 112  
121 113 @Test
... ... @@ -199,4 +191,46 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
199 191 assertPojoEquals(dbSmsChannel, pageResult.getList().get(0));
200 192 }
201 193  
  194 + @Test
  195 + public void testGetSmsClient_id() {
  196 + // mock 数据
  197 + SmsChannelDO channel = randomPojo(SmsChannelDO.class);
  198 + smsChannelMapper.insert(channel);
  199 + // mock 参数
  200 + Long id = channel.getId();
  201 + // mock 方法
  202 + SmsClient mockClient = mock(SmsClient.class);
  203 + when(smsClientFactory.getSmsClient(eq(id))).thenReturn(mockClient);
  204 +
  205 + // 调用
  206 + SmsClient client = smsChannelService.getSmsClient(id);
  207 + // 断言
  208 + assertSame(client, mockClient);
  209 + verify(smsClientFactory).createOrUpdateSmsClient(argThat(arg -> {
  210 + SmsChannelProperties properties = SmsChannelConvert.INSTANCE.convert02(channel);
  211 + return properties.equals(arg);
  212 + }));
  213 + }
  214 +
  215 + @Test
  216 + public void testGetSmsClient_code() {
  217 + // mock 数据
  218 + SmsChannelDO channel = randomPojo(SmsChannelDO.class);
  219 + smsChannelMapper.insert(channel);
  220 + // mock 参数
  221 + String code = channel.getCode();
  222 + // mock 方法
  223 + SmsClient mockClient = mock(SmsClient.class);
  224 + when(smsClientFactory.getSmsClient(eq(code))).thenReturn(mockClient);
  225 +
  226 + // 调用
  227 + SmsClient client = smsChannelService.getSmsClient(code);
  228 + // 断言
  229 + assertSame(client, mockClient);
  230 + verify(smsClientFactory).createOrUpdateSmsClient(argThat(arg -> {
  231 + SmsChannelProperties properties = SmsChannelConvert.INSTANCE.convert02(channel);
  232 + return properties.equals(arg);
  233 + }));
  234 + }
  235 +
202 236 }
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java
... ... @@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
5 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 6 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
7 7 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
8   -import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
9 8 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
10 9 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
11 10 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
... ... @@ -52,9 +51,6 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
52 51 @Mock
53 52 private SmsProducer smsProducer;
54 53  
55   - @Mock
56   - private SmsClientFactory smsClientFactory;
57   -
58 54 @Test
59 55 public void testSendSingleSmsToAdmin() {
60 56 // 准备参数
... ... @@ -253,7 +249,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
253 249 SmsSendMessage message = randomPojo(SmsSendMessage.class);
254 250 // mock SmsClientFactory 的方法
255 251 SmsClient smsClient = spy(SmsClient.class);
256   - when(smsClientFactory.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
  252 + when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
257 253 // mock SmsClient 的方法
258 254 SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class);
259 255 sendResult.setData(randomPojo(SmsSendRespDTO.class));
... ... @@ -275,7 +271,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
275 271 String text = randomString();
276 272 // mock SmsClientFactory 的方法
277 273 SmsClient smsClient = spy(SmsClient.class);
278   - when(smsClientFactory.getSmsClient(eq(channelCode))).thenReturn(smsClient);
  274 + when(smsChannelService.getSmsClient(eq(channelCode))).thenReturn(smsClient);
279 275 // mock SmsClient 的方法
280 276 List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class);
281 277  
... ...