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,8 +32,8 @@ public interface SmsChannelConvert {
32 32
33 PageResult<SmsChannelRespVO> convertPage(PageResult<SmsChannelDO> page); 33 PageResult<SmsChannelRespVO> convertPage(PageResult<SmsChannelDO> page);
34 34
35 - List<SmsChannelProperties> convertList02(List<SmsChannelDO> list);  
36 -  
37 List<SmsChannelSimpleRespVO> convertList03(List<SmsChannelDO> list); 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,9 +6,6 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
6 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
7 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; 7 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
8 import org.apache.ibatis.annotations.Mapper; 8 import org.apache.ibatis.annotations.Mapper;
9 -import org.apache.ibatis.annotations.Select;  
10 -  
11 -import java.time.LocalDateTime;  
12 9
13 @Mapper 10 @Mapper
14 public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> { 11 public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
@@ -21,7 +18,8 @@ public interface SmsChannelMapper extends BaseMapperX&lt;SmsChannelDO&gt; { @@ -21,7 +18,8 @@ public interface SmsChannelMapper extends BaseMapperX&lt;SmsChannelDO&gt; {
21 .orderByDesc(SmsChannelDO::getId)); 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 package cn.iocoder.yudao.module.system.service.sms; 1 package cn.iocoder.yudao.module.system.service.sms;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  4 +import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
4 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO; 5 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
5 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; 6 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
6 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO; 7 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
@@ -13,16 +14,11 @@ import java.util.List; @@ -13,16 +14,11 @@ import java.util.List;
13 * 短信渠道 Service 接口 14 * 短信渠道 Service 接口
14 * 15 *
15 * @author zzf 16 * @author zzf
16 - * @date 2021/1/25 9:24 17 + * @since 2021/1/25 9:24
17 */ 18 */
18 public interface SmsChannelService { 19 public interface SmsChannelService {
19 20
20 /** 21 /**
21 - * 初始化短信客户端  
22 - */  
23 - void initLocalCache();  
24 -  
25 - /**  
26 * 创建短信渠道 22 * 创建短信渠道
27 * 23 *
28 * @param createReqVO 创建信息 24 * @param createReqVO 创建信息
@@ -67,4 +63,20 @@ public interface SmsChannelService { @@ -67,4 +63,20 @@ public interface SmsChannelService {
67 */ 63 */
68 PageResult<SmsChannelDO> getSmsChannelPage(SmsChannelPageReqVO pageReqVO); 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 package cn.iocoder.yudao.module.system.service.sms; 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 import cn.iocoder.yudao.framework.common.pojo.PageResult; 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 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory; 7 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
6 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; 8 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
7 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO; 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,20 +12,18 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannel
10 import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert; 12 import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert;
11 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; 13 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
12 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; 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 import lombok.Getter; 17 import lombok.Getter;
  18 +import lombok.SneakyThrows;
14 import lombok.extern.slf4j.Slf4j; 19 import lombok.extern.slf4j.Slf4j;
15 -import org.springframework.scheduling.annotation.Scheduled;  
16 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
17 21
18 -import javax.annotation.PostConstruct;  
19 import javax.annotation.Resource; 22 import javax.annotation.Resource;
20 -import java.time.LocalDateTime;  
21 -import java.util.Collections; 23 +import java.time.Duration;
22 import java.util.List; 24 import java.util.List;
23 -import java.util.concurrent.TimeUnit;  
24 25
25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 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 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; 27 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; 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,10 +37,44 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNE
37 public class SmsChannelServiceImpl implements SmsChannelService { 37 public class SmsChannelServiceImpl implements SmsChannelService {
38 38
39 /** 39 /**
40 - * 短信渠道列表的缓存 40 + * {@link SmsClient} 缓存,通过它异步刷新 smsClientFactory
41 */ 41 */
42 @Getter 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 @Resource 79 @Resource
46 private SmsClientFactory smsClientFactory; 80 private SmsClientFactory smsClientFactory;
@@ -52,65 +86,32 @@ public class SmsChannelServiceImpl implements SmsChannelService { @@ -52,65 +86,32 @@ public class SmsChannelServiceImpl implements SmsChannelService {
52 private SmsTemplateService smsTemplateService; 86 private SmsTemplateService smsTemplateService;
53 87
54 @Override 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 public Long createSmsChannel(SmsChannelCreateReqVO createReqVO) { 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 @Override 99 @Override
99 public void updateSmsChannel(SmsChannelUpdateReqVO updateReqVO) { 100 public void updateSmsChannel(SmsChannelUpdateReqVO updateReqVO) {
100 // 校验存在 101 // 校验存在
101 - validateSmsChannelExists(updateReqVO.getId()); 102 + SmsChannelDO channel = validateSmsChannelExists(updateReqVO.getId());
102 // 更新 103 // 更新
103 SmsChannelDO updateObj = SmsChannelConvert.INSTANCE.convert(updateReqVO); 104 SmsChannelDO updateObj = SmsChannelConvert.INSTANCE.convert(updateReqVO);
104 smsChannelMapper.updateById(updateObj); 105 smsChannelMapper.updateById(updateObj);
105 106
106 - // 刷新缓存  
107 - initLocalCache(); 107 + // 清空缓存
  108 + clearCache(updateReqVO.getId(), channel.getCode());
108 } 109 }
109 110
110 @Override 111 @Override
111 public void deleteSmsChannel(Long id) { 112 public void deleteSmsChannel(Long id) {
112 // 校验存在 113 // 校验存在
113 - validateSmsChannelExists(id); 114 + SmsChannelDO channel = validateSmsChannelExists(id);
114 // 校验是否有在使用该账号的模版 115 // 校验是否有在使用该账号的模版
115 if (smsTemplateService.countByChannelId(id) > 0) { 116 if (smsTemplateService.countByChannelId(id) > 0) {
116 throw exception(SMS_CHANNEL_HAS_CHILDREN); 117 throw exception(SMS_CHANNEL_HAS_CHILDREN);
@@ -118,14 +119,28 @@ public class SmsChannelServiceImpl implements SmsChannelService { @@ -118,14 +119,28 @@ public class SmsChannelServiceImpl implements SmsChannelService {
118 // 删除 119 // 删除
119 smsChannelMapper.deleteById(id); 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 throw exception(SMS_CHANNEL_NOT_EXISTS); 141 throw exception(SMS_CHANNEL_NOT_EXISTS);
128 } 142 }
  143 + return channel;
129 } 144 }
130 145
131 @Override 146 @Override
@@ -143,4 +158,14 @@ public class SmsChannelServiceImpl implements SmsChannelService { @@ -143,4 +158,14 @@ public class SmsChannelServiceImpl implements SmsChannelService {
143 return smsChannelMapper.selectPage(pageReqVO); 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,7 +8,6 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
8 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 8 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
9 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; 9 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
10 import cn.iocoder.yudao.framework.sms.core.client.SmsClient; 10 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
11 -import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;  
12 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; 11 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
13 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; 12 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
14 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; 13 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
@@ -50,9 +49,6 @@ public class SmsSendServiceImpl implements SmsSendService { @@ -50,9 +49,6 @@ public class SmsSendServiceImpl implements SmsSendService {
50 private SmsLogService smsLogService; 49 private SmsLogService smsLogService;
51 50
52 @Resource 51 @Resource
53 - private SmsClientFactory smsClientFactory;  
54 -  
55 - @Resource  
56 private SmsProducer smsProducer; 52 private SmsProducer smsProducer;
57 53
58 @Override 54 @Override
@@ -95,7 +91,6 @@ public class SmsSendServiceImpl implements SmsSendService { @@ -95,7 +91,6 @@ public class SmsSendServiceImpl implements SmsSendService {
95 // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志 91 // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
96 Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()) 92 Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
97 && CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus()); 93 && CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
98 - ;  
99 String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams); 94 String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
100 Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams); 95 Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams);
101 96
@@ -132,7 +127,7 @@ public class SmsSendServiceImpl implements SmsSendService { @@ -132,7 +127,7 @@ public class SmsSendServiceImpl implements SmsSendService {
132 /** 127 /**
133 * 将参数模板,处理成有序的 KeyValue 数组 128 * 将参数模板,处理成有序的 KeyValue 数组
134 * <p> 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 * @param template 短信模板 132 * @param template 短信模板
138 * @param templateParams 原始参数 133 * @param templateParams 原始参数
@@ -160,7 +155,7 @@ public class SmsSendServiceImpl implements SmsSendService { @@ -160,7 +155,7 @@ public class SmsSendServiceImpl implements SmsSendService {
160 @Override 155 @Override
161 public void doSendSms(SmsSendMessage message) { 156 public void doSendSms(SmsSendMessage message) {
162 // 获得渠道对应的 SmsClient 客户端 157 // 获得渠道对应的 SmsClient 客户端
163 - SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId()); 158 + SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId());
164 Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId()); 159 Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId());
165 // 发送短信 160 // 发送短信
166 SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(), 161 SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(),
@@ -173,7 +168,7 @@ public class SmsSendServiceImpl implements SmsSendService { @@ -173,7 +168,7 @@ public class SmsSendServiceImpl implements SmsSendService {
173 @Override 168 @Override
174 public void receiveSmsStatus(String channelCode, String text) throws Throwable { 169 public void receiveSmsStatus(String channelCode, String text) throws Throwable {
175 // 获得渠道对应的 SmsClient 客户端 170 // 获得渠道对应的 SmsClient 客户端
176 - SmsClient smsClient = smsClientFactory.getSmsClient(channelCode); 171 + SmsClient smsClient = smsChannelService.getSmsClient(channelCode);
177 Assert.notNull(smsClient, "短信客户端({}) 不存在", channelCode); 172 Assert.notNull(smsClient, "短信客户端({}) 不存在", channelCode);
178 // 解析内容 173 // 解析内容
179 List<SmsReceiveRespDTO> receiveResults = smsClient.parseSmsReceiveStatus(text); 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,11 +2,14 @@ package cn.iocoder.yudao.module.system.service.sms;
2 2
3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
5 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory; 6 import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
  7 +import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
6 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 8 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
7 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO; 9 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
8 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO; 10 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
9 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO; 11 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
  12 +import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert;
10 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; 13 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
11 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper; 14 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
12 import org.junit.jupiter.api.Test; 15 import org.junit.jupiter.api.Test;
@@ -19,7 +22,8 @@ import java.util.List; @@ -19,7 +22,8 @@ import java.util.List;
19 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; 22 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
20 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime; 23 import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
21 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId; 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 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; 27 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
24 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN; 28 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
25 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS; 29 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
@@ -42,27 +46,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @@ -42,27 +46,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
42 private SmsTemplateService smsTemplateService; 46 private SmsTemplateService smsTemplateService;
43 47
44 @Test 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 public void testCreateSmsChannel_success() { 49 public void testCreateSmsChannel_success() {
67 // 准备参数 50 // 准备参数
68 SmsChannelCreateReqVO reqVO = randomPojo(SmsChannelCreateReqVO.class, o -> o.setStatus(randomCommonStatus())); 51 SmsChannelCreateReqVO reqVO = randomPojo(SmsChannelCreateReqVO.class, o -> o.setStatus(randomCommonStatus()));
@@ -74,6 +57,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @@ -74,6 +57,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
74 // 校验记录的属性是否正确 57 // 校验记录的属性是否正确
75 SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId); 58 SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId);
76 assertPojoEquals(reqVO, smsChannel); 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 @Test 65 @Test
@@ -93,6 +79,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @@ -93,6 +79,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
93 // 校验是否更新正确 79 // 校验是否更新正确
94 SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的 80 SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的
95 assertPojoEquals(reqVO, smsChannel); 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 @Test 87 @Test
@@ -116,6 +105,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @@ -116,6 +105,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
116 smsChannelService.deleteSmsChannel(id); 105 smsChannelService.deleteSmsChannel(id);
117 // 校验数据不存在了 106 // 校验数据不存在了
118 assertNull(smsChannelMapper.selectById(id)); 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 @Test 113 @Test
@@ -199,4 +191,46 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @@ -199,4 +191,46 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
199 assertPojoEquals(dbSmsChannel, pageResult.getList().get(0)); 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,7 +5,6 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; 6 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
7 import cn.iocoder.yudao.framework.sms.core.client.SmsClient; 7 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
8 -import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;  
9 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; 8 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
10 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; 9 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
11 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; 10 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
@@ -52,9 +51,6 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest { @@ -52,9 +51,6 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
52 @Mock 51 @Mock
53 private SmsProducer smsProducer; 52 private SmsProducer smsProducer;
54 53
55 - @Mock  
56 - private SmsClientFactory smsClientFactory;  
57 -  
58 @Test 54 @Test
59 public void testSendSingleSmsToAdmin() { 55 public void testSendSingleSmsToAdmin() {
60 // 准备参数 56 // 准备参数
@@ -253,7 +249,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest { @@ -253,7 +249,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
253 SmsSendMessage message = randomPojo(SmsSendMessage.class); 249 SmsSendMessage message = randomPojo(SmsSendMessage.class);
254 // mock SmsClientFactory 的方法 250 // mock SmsClientFactory 的方法
255 SmsClient smsClient = spy(SmsClient.class); 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 // mock SmsClient 的方法 253 // mock SmsClient 的方法
258 SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class); 254 SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class);
259 sendResult.setData(randomPojo(SmsSendRespDTO.class)); 255 sendResult.setData(randomPojo(SmsSendRespDTO.class));
@@ -275,7 +271,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest { @@ -275,7 +271,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
275 String text = randomString(); 271 String text = randomString();
276 // mock SmsClientFactory 的方法 272 // mock SmsClientFactory 的方法
277 SmsClient smsClient = spy(SmsClient.class); 273 SmsClient smsClient = spy(SmsClient.class);
278 - when(smsClientFactory.getSmsClient(eq(channelCode))).thenReturn(smsClient); 274 + when(smsChannelService.getSmsClient(eq(channelCode))).thenReturn(smsClient);
279 // mock SmsClient 的方法 275 // mock SmsClient 的方法
280 List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class); 276 List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class);
281 277