Commit 48fd4972da171f10c6ac2012f05fea58a5e86562
1 parent
ab800aa8
sms 缓存,使用 guava 替代 job 扫描,目的:提升启动速度,加快缓存失效
Showing
7 changed files
with
164 additions
and
104 deletions
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<SmsChannelDO> { | @@ -21,7 +18,8 @@ public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> { | ||
| 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 |