sms 缓存,使用 guava 替代 job 扫描,目的:提升启动速度,加快缓存失效
parent
3b9affe056
commit
f0897cb435
|
@ -32,8 +32,8 @@ public interface SmsChannelConvert {
|
||||||
|
|
||||||
PageResult<SmsChannelRespVO> convertPage(PageResult<SmsChannelDO> page);
|
PageResult<SmsChannelRespVO> convertPage(PageResult<SmsChannelDO> page);
|
||||||
|
|
||||||
List<SmsChannelProperties> convertList02(List<SmsChannelDO> list);
|
|
||||||
|
|
||||||
List<SmsChannelSimpleRespVO> convertList03(List<SmsChannelDO> list);
|
List<SmsChannelSimpleRespVO> convertList03(List<SmsChannelDO> list);
|
||||||
|
|
||||||
|
SmsChannelProperties convert02(SmsChannelDO channel);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,6 @@ import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
import org.apache.ibatis.annotations.Select;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
|
public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
|
||||||
|
@ -21,7 +18,8 @@ public interface SmsChannelMapper extends BaseMapperX<SmsChannelDO> {
|
||||||
.orderByDesc(SmsChannelDO::getId));
|
.orderByDesc(SmsChannelDO::getId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Select("SELECT COUNT(*) FROM system_sms_channel WHERE update_time > #{maxUpdateTime}")
|
default SmsChannelDO selectByCode(String code) {
|
||||||
Long selectCountByUpdateTimeGt(LocalDateTime maxTime);
|
return selectOne(SmsChannelDO::getCode, code);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package cn.iocoder.yudao.module.system.service.sms;
|
package cn.iocoder.yudao.module.system.service.sms;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
|
||||||
|
@ -13,15 +14,10 @@ import java.util.List;
|
||||||
* 短信渠道 Service 接口
|
* 短信渠道 Service 接口
|
||||||
*
|
*
|
||||||
* @author zzf
|
* @author zzf
|
||||||
* @date 2021/1/25 9:24
|
* @since 2021/1/25 9:24
|
||||||
*/
|
*/
|
||||||
public interface SmsChannelService {
|
public interface SmsChannelService {
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化短信客户端
|
|
||||||
*/
|
|
||||||
void initLocalCache();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 创建短信渠道
|
* 创建短信渠道
|
||||||
*
|
*
|
||||||
|
@ -67,4 +63,20 @@ public interface SmsChannelService {
|
||||||
*/
|
*/
|
||||||
PageResult<SmsChannelDO> getSmsChannelPage(SmsChannelPageReqVO pageReqVO);
|
PageResult<SmsChannelDO> getSmsChannelPage(SmsChannelPageReqVO pageReqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得短信客户端
|
||||||
|
*
|
||||||
|
* @param id 编号
|
||||||
|
* @return 短信客户端
|
||||||
|
*/
|
||||||
|
SmsClient getSmsClient(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得短信客户端
|
||||||
|
*
|
||||||
|
* @param code 编码
|
||||||
|
* @return 短信客户端
|
||||||
|
*/
|
||||||
|
SmsClient getSmsClient(String code);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
package cn.iocoder.yudao.module.system.service.sms;
|
package cn.iocoder.yudao.module.system.service.sms;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
||||||
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
|
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
|
||||||
|
@ -10,20 +11,18 @@ import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannel
|
||||||
import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert;
|
import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
|
||||||
|
import com.google.common.cache.CacheLoader;
|
||||||
|
import com.google.common.cache.LoadingCache;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.scheduling.annotation.Scheduled;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.time.LocalDateTime;
|
import java.time.Duration;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getMaxValue;
|
import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
|
||||||
|
|
||||||
|
@ -37,10 +36,44 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNE
|
||||||
public class SmsChannelServiceImpl implements SmsChannelService {
|
public class SmsChannelServiceImpl implements SmsChannelService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信渠道列表的缓存
|
* {@link SmsClient} 缓存,通过它异步刷新 smsClientFactory
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
private volatile List<SmsChannelDO> channelCache = Collections.emptyList();
|
private final LoadingCache<Long, SmsClient> idClientCache = buildAsyncReloadingCache(Duration.ofSeconds(10L),
|
||||||
|
new CacheLoader<Long, SmsClient>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SmsClient load(Long id) {
|
||||||
|
// 查询,然后尝试刷新
|
||||||
|
SmsChannelDO channel = smsChannelMapper.selectById(id);
|
||||||
|
if (channel != null) {
|
||||||
|
SmsChannelProperties properties = SmsChannelConvert.INSTANCE.convert02(channel);
|
||||||
|
smsClientFactory.createOrUpdateSmsClient(properties);
|
||||||
|
}
|
||||||
|
return smsClientFactory.getSmsClient(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SmsClient} 缓存,通过它异步刷新 smsClientFactory
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
private final LoadingCache<String, SmsClient> codeClientCache = buildAsyncReloadingCache(Duration.ofSeconds(60L),
|
||||||
|
new CacheLoader<String, SmsClient>() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SmsClient load(String code) {
|
||||||
|
// 查询,然后尝试刷新
|
||||||
|
SmsChannelDO channel = smsChannelMapper.selectByCode(code);
|
||||||
|
if (channel != null) {
|
||||||
|
SmsChannelProperties properties = SmsChannelConvert.INSTANCE.convert02(channel);
|
||||||
|
smsClientFactory.createOrUpdateSmsClient(properties);
|
||||||
|
}
|
||||||
|
return smsClientFactory.getSmsClient(code);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private SmsClientFactory smsClientFactory;
|
private SmsClientFactory smsClientFactory;
|
||||||
|
@ -51,66 +84,29 @@ public class SmsChannelServiceImpl implements SmsChannelService {
|
||||||
@Resource
|
@Resource
|
||||||
private SmsTemplateService smsTemplateService;
|
private SmsTemplateService smsTemplateService;
|
||||||
|
|
||||||
@Override
|
|
||||||
@PostConstruct
|
|
||||||
public void initLocalCache() {
|
|
||||||
// 第一步:查询数据
|
|
||||||
List<SmsChannelDO> channels = smsChannelMapper.selectList();
|
|
||||||
log.info("[initLocalCache][缓存短信渠道,数量为:{}]", channels.size());
|
|
||||||
|
|
||||||
// 第二步:构建缓存:创建或更新短信 Client
|
|
||||||
List<SmsChannelProperties> propertiesList = SmsChannelConvert.INSTANCE.convertList02(channels);
|
|
||||||
propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
|
|
||||||
this.channelCache = channels;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过定时任务轮询,刷新缓存
|
|
||||||
*
|
|
||||||
* 目的:多节点部署时,通过轮询”通知“所有节点,进行刷新
|
|
||||||
*/
|
|
||||||
@Scheduled(initialDelay = 60, fixedRate = 60, timeUnit = TimeUnit.SECONDS)
|
|
||||||
public void refreshLocalCache() {
|
|
||||||
// 情况一:如果缓存里没有数据,则直接刷新缓存
|
|
||||||
if (CollUtil.isEmpty(channelCache)) {
|
|
||||||
initLocalCache();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 情况二,如果缓存里数据,则通过 updateTime 判断是否有数据变更,有变更则刷新缓存
|
|
||||||
LocalDateTime maxTime = getMaxValue(channelCache, SmsChannelDO::getUpdateTime);
|
|
||||||
if (smsChannelMapper.selectCountByUpdateTimeGt(maxTime) > 0) {
|
|
||||||
initLocalCache();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long createSmsChannel(SmsChannelCreateReqVO createReqVO) {
|
public Long createSmsChannel(SmsChannelCreateReqVO createReqVO) {
|
||||||
// 插入
|
SmsChannelDO channel = SmsChannelConvert.INSTANCE.convert(createReqVO);
|
||||||
SmsChannelDO smsChannel = SmsChannelConvert.INSTANCE.convert(createReqVO);
|
smsChannelMapper.insert(channel);
|
||||||
smsChannelMapper.insert(smsChannel);
|
return channel.getId();
|
||||||
|
|
||||||
// 刷新缓存
|
|
||||||
initLocalCache();
|
|
||||||
return smsChannel.getId();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateSmsChannel(SmsChannelUpdateReqVO updateReqVO) {
|
public void updateSmsChannel(SmsChannelUpdateReqVO updateReqVO) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
validateSmsChannelExists(updateReqVO.getId());
|
SmsChannelDO channel = validateSmsChannelExists(updateReqVO.getId());
|
||||||
// 更新
|
// 更新
|
||||||
SmsChannelDO updateObj = SmsChannelConvert.INSTANCE.convert(updateReqVO);
|
SmsChannelDO updateObj = SmsChannelConvert.INSTANCE.convert(updateReqVO);
|
||||||
smsChannelMapper.updateById(updateObj);
|
smsChannelMapper.updateById(updateObj);
|
||||||
|
|
||||||
// 刷新缓存
|
// 清空缓存
|
||||||
initLocalCache();
|
clearCache(updateReqVO.getId(), channel.getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteSmsChannel(Long id) {
|
public void deleteSmsChannel(Long id) {
|
||||||
// 校验存在
|
// 校验存在
|
||||||
validateSmsChannelExists(id);
|
SmsChannelDO channel = validateSmsChannelExists(id);
|
||||||
// 校验是否有在使用该账号的模版
|
// 校验是否有在使用该账号的模版
|
||||||
if (smsTemplateService.countByChannelId(id) > 0) {
|
if (smsTemplateService.countByChannelId(id) > 0) {
|
||||||
throw exception(SMS_CHANNEL_HAS_CHILDREN);
|
throw exception(SMS_CHANNEL_HAS_CHILDREN);
|
||||||
|
@ -118,14 +114,29 @@ public class SmsChannelServiceImpl implements SmsChannelService {
|
||||||
// 删除
|
// 删除
|
||||||
smsChannelMapper.deleteById(id);
|
smsChannelMapper.deleteById(id);
|
||||||
|
|
||||||
// 刷新缓存
|
// 清空缓存
|
||||||
initLocalCache();
|
clearCache(id, channel.getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateSmsChannelExists(Long id) {
|
/**
|
||||||
if (smsChannelMapper.selectById(id) == null) {
|
* 清空指定渠道编号的缓存
|
||||||
|
*
|
||||||
|
* @param id 渠道编号
|
||||||
|
* @param code 渠道编码
|
||||||
|
*/
|
||||||
|
private void clearCache(Long id, String code) {
|
||||||
|
idClientCache.invalidate(id);
|
||||||
|
if (StrUtil.isNotEmpty(code)) {
|
||||||
|
codeClientCache.invalidate(code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SmsChannelDO validateSmsChannelExists(Long id) {
|
||||||
|
SmsChannelDO channel = smsChannelMapper.selectById(id);
|
||||||
|
if (channel == null) {
|
||||||
throw exception(SMS_CHANNEL_NOT_EXISTS);
|
throw exception(SMS_CHANNEL_NOT_EXISTS);
|
||||||
}
|
}
|
||||||
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -143,4 +154,14 @@ public class SmsChannelServiceImpl implements SmsChannelService {
|
||||||
return smsChannelMapper.selectPage(pageReqVO);
|
return smsChannelMapper.selectPage(pageReqVO);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SmsClient getSmsClient(Long id) {
|
||||||
|
return idClientCache.getUnchecked(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SmsClient getSmsClient(String code) {
|
||||||
|
return codeClientCache.getUnchecked(code);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
|
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
|
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
|
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||||
|
@ -49,9 +48,6 @@ public class SmsSendServiceImpl implements SmsSendService {
|
||||||
@Resource
|
@Resource
|
||||||
private SmsLogService smsLogService;
|
private SmsLogService smsLogService;
|
||||||
|
|
||||||
@Resource
|
|
||||||
private SmsClientFactory smsClientFactory;
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private SmsProducer smsProducer;
|
private SmsProducer smsProducer;
|
||||||
|
|
||||||
|
@ -95,7 +91,6 @@ public class SmsSendServiceImpl implements SmsSendService {
|
||||||
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
// 创建发送日志。如果模板被禁用,则不发送短信,只记录日志
|
||||||
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus())
|
||||||
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
&& CommonStatusEnum.ENABLE.getStatus().equals(smsChannel.getStatus());
|
||||||
;
|
|
||||||
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
|
||||||
Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams);
|
Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams);
|
||||||
|
|
||||||
|
@ -132,7 +127,7 @@ public class SmsSendServiceImpl implements SmsSendService {
|
||||||
/**
|
/**
|
||||||
* 将参数模板,处理成有序的 KeyValue 数组
|
* 将参数模板,处理成有序的 KeyValue 数组
|
||||||
* <p>
|
* <p>
|
||||||
* 原因是,部分短信平台并不是使用 key 作为参数,而是数组下标,例如说腾讯云 https://cloud.tencent.com/document/product/382/39023
|
* 原因是,部分短信平台并不是使用 key 作为参数,而是数组下标,例如说 <a href="https://cloud.tencent.com/document/product/382/39023">腾讯云</a>
|
||||||
*
|
*
|
||||||
* @param template 短信模板
|
* @param template 短信模板
|
||||||
* @param templateParams 原始参数
|
* @param templateParams 原始参数
|
||||||
|
@ -160,7 +155,7 @@ public class SmsSendServiceImpl implements SmsSendService {
|
||||||
@Override
|
@Override
|
||||||
public void doSendSms(SmsSendMessage message) {
|
public void doSendSms(SmsSendMessage message) {
|
||||||
// 获得渠道对应的 SmsClient 客户端
|
// 获得渠道对应的 SmsClient 客户端
|
||||||
SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId());
|
SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId());
|
||||||
Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId());
|
Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId());
|
||||||
// 发送短信
|
// 发送短信
|
||||||
SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(),
|
SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(),
|
||||||
|
@ -173,7 +168,7 @@ public class SmsSendServiceImpl implements SmsSendService {
|
||||||
@Override
|
@Override
|
||||||
public void receiveSmsStatus(String channelCode, String text) throws Throwable {
|
public void receiveSmsStatus(String channelCode, String text) throws Throwable {
|
||||||
// 获得渠道对应的 SmsClient 客户端
|
// 获得渠道对应的 SmsClient 客户端
|
||||||
SmsClient smsClient = smsClientFactory.getSmsClient(channelCode);
|
SmsClient smsClient = smsChannelService.getSmsClient(channelCode);
|
||||||
Assert.notNull(smsClient, "短信客户端({}) 不存在", channelCode);
|
Assert.notNull(smsClient, "短信客户端({}) 不存在", channelCode);
|
||||||
// 解析内容
|
// 解析内容
|
||||||
List<SmsReceiveRespDTO> receiveResults = smsClient.parseSmsReceiveStatus(text);
|
List<SmsReceiveRespDTO> receiveResults = smsClient.parseSmsReceiveStatus(text);
|
||||||
|
|
|
@ -2,11 +2,14 @@ package cn.iocoder.yudao.module.system.service.sms;
|
||||||
|
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||||
|
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
||||||
|
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
|
||||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelCreateReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelPageReqVO;
|
||||||
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
|
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.channel.SmsChannelUpdateReqVO;
|
||||||
|
import cn.iocoder.yudao.module.system.convert.sms.SmsChannelConvert;
|
||||||
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
|
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
|
||||||
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
|
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsChannelMapper;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
@ -19,7 +22,8 @@ import java.util.List;
|
||||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
|
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
|
||||||
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildTime;
|
||||||
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
|
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
|
||||||
|
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
|
||||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_HAS_CHILDREN;
|
||||||
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
|
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SMS_CHANNEL_NOT_EXISTS;
|
||||||
|
@ -41,27 +45,6 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
|
||||||
@MockBean
|
@MockBean
|
||||||
private SmsTemplateService smsTemplateService;
|
private SmsTemplateService smsTemplateService;
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testInitLocalCache_success() {
|
|
||||||
// mock 数据
|
|
||||||
SmsChannelDO smsChannelDO01 = randomPojo(SmsChannelDO.class);
|
|
||||||
smsChannelMapper.insert(smsChannelDO01);
|
|
||||||
SmsChannelDO smsChannelDO02 = randomPojo(SmsChannelDO.class);
|
|
||||||
smsChannelMapper.insert(smsChannelDO02);
|
|
||||||
|
|
||||||
// 调用
|
|
||||||
smsChannelService.initLocalCache();
|
|
||||||
// 校验调用
|
|
||||||
verify(smsClientFactory, times(1)).createOrUpdateSmsClient(
|
|
||||||
argThat(properties -> isPojoEquals(smsChannelDO01, properties)));
|
|
||||||
verify(smsClientFactory, times(1)).createOrUpdateSmsClient(
|
|
||||||
argThat(properties -> isPojoEquals(smsChannelDO02, properties)));
|
|
||||||
// 断言 channelCache 缓存
|
|
||||||
assertEquals(2, smsChannelService.getChannelCache().size());
|
|
||||||
assertPojoEquals(smsChannelDO01, smsChannelService.getChannelCache().get(0));
|
|
||||||
assertPojoEquals(smsChannelDO02, smsChannelService.getChannelCache().get(1));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreateSmsChannel_success() {
|
public void testCreateSmsChannel_success() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
|
@ -74,6 +57,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
|
||||||
// 校验记录的属性是否正确
|
// 校验记录的属性是否正确
|
||||||
SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId);
|
SmsChannelDO smsChannel = smsChannelMapper.selectById(smsChannelId);
|
||||||
assertPojoEquals(reqVO, smsChannel);
|
assertPojoEquals(reqVO, smsChannel);
|
||||||
|
// 断言 cache
|
||||||
|
assertNull(smsChannelService.getIdClientCache().getIfPresent(smsChannel.getId()));
|
||||||
|
assertNull(smsChannelService.getCodeClientCache().getIfPresent(smsChannel.getCode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -93,6 +79,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
|
||||||
// 校验是否更新正确
|
// 校验是否更新正确
|
||||||
SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的
|
SmsChannelDO smsChannel = smsChannelMapper.selectById(reqVO.getId()); // 获取最新的
|
||||||
assertPojoEquals(reqVO, smsChannel);
|
assertPojoEquals(reqVO, smsChannel);
|
||||||
|
// 断言 cache
|
||||||
|
assertNull(smsChannelService.getIdClientCache().getIfPresent(smsChannel.getId()));
|
||||||
|
assertNull(smsChannelService.getCodeClientCache().getIfPresent(smsChannel.getCode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -116,6 +105,9 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
|
||||||
smsChannelService.deleteSmsChannel(id);
|
smsChannelService.deleteSmsChannel(id);
|
||||||
// 校验数据不存在了
|
// 校验数据不存在了
|
||||||
assertNull(smsChannelMapper.selectById(id));
|
assertNull(smsChannelMapper.selectById(id));
|
||||||
|
// 断言 cache
|
||||||
|
assertNull(smsChannelService.getIdClientCache().getIfPresent(dbSmsChannel.getId()));
|
||||||
|
assertNull(smsChannelService.getCodeClientCache().getIfPresent(dbSmsChannel.getCode()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -199,4 +191,46 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
|
||||||
assertPojoEquals(dbSmsChannel, pageResult.getList().get(0));
|
assertPojoEquals(dbSmsChannel, pageResult.getList().get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSmsClient_id() {
|
||||||
|
// mock 数据
|
||||||
|
SmsChannelDO channel = randomPojo(SmsChannelDO.class);
|
||||||
|
smsChannelMapper.insert(channel);
|
||||||
|
// mock 参数
|
||||||
|
Long id = channel.getId();
|
||||||
|
// mock 方法
|
||||||
|
SmsClient mockClient = mock(SmsClient.class);
|
||||||
|
when(smsClientFactory.getSmsClient(eq(id))).thenReturn(mockClient);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
SmsClient client = smsChannelService.getSmsClient(id);
|
||||||
|
// 断言
|
||||||
|
assertSame(client, mockClient);
|
||||||
|
verify(smsClientFactory).createOrUpdateSmsClient(argThat(arg -> {
|
||||||
|
SmsChannelProperties properties = SmsChannelConvert.INSTANCE.convert02(channel);
|
||||||
|
return properties.equals(arg);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetSmsClient_code() {
|
||||||
|
// mock 数据
|
||||||
|
SmsChannelDO channel = randomPojo(SmsChannelDO.class);
|
||||||
|
smsChannelMapper.insert(channel);
|
||||||
|
// mock 参数
|
||||||
|
String code = channel.getCode();
|
||||||
|
// mock 方法
|
||||||
|
SmsClient mockClient = mock(SmsClient.class);
|
||||||
|
when(smsClientFactory.getSmsClient(eq(code))).thenReturn(mockClient);
|
||||||
|
|
||||||
|
// 调用
|
||||||
|
SmsClient client = smsChannelService.getSmsClient(code);
|
||||||
|
// 断言
|
||||||
|
assertSame(client, mockClient);
|
||||||
|
verify(smsClientFactory).createOrUpdateSmsClient(argThat(arg -> {
|
||||||
|
SmsChannelProperties properties = SmsChannelConvert.INSTANCE.convert02(channel);
|
||||||
|
return properties.equals(arg);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||||
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
|
import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
|
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
||||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
|
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||||
|
@ -52,9 +51,6 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||||
@Mock
|
@Mock
|
||||||
private SmsProducer smsProducer;
|
private SmsProducer smsProducer;
|
||||||
|
|
||||||
@Mock
|
|
||||||
private SmsClientFactory smsClientFactory;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSendSingleSmsToAdmin() {
|
public void testSendSingleSmsToAdmin() {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
|
@ -253,7 +249,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||||
SmsSendMessage message = randomPojo(SmsSendMessage.class);
|
SmsSendMessage message = randomPojo(SmsSendMessage.class);
|
||||||
// mock SmsClientFactory 的方法
|
// mock SmsClientFactory 的方法
|
||||||
SmsClient smsClient = spy(SmsClient.class);
|
SmsClient smsClient = spy(SmsClient.class);
|
||||||
when(smsClientFactory.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
|
when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
|
||||||
// mock SmsClient 的方法
|
// mock SmsClient 的方法
|
||||||
SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class);
|
SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class);
|
||||||
sendResult.setData(randomPojo(SmsSendRespDTO.class));
|
sendResult.setData(randomPojo(SmsSendRespDTO.class));
|
||||||
|
@ -275,7 +271,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
|
||||||
String text = randomString();
|
String text = randomString();
|
||||||
// mock SmsClientFactory 的方法
|
// mock SmsClientFactory 的方法
|
||||||
SmsClient smsClient = spy(SmsClient.class);
|
SmsClient smsClient = spy(SmsClient.class);
|
||||||
when(smsClientFactory.getSmsClient(eq(channelCode))).thenReturn(smsClient);
|
when(smsChannelService.getSmsClient(eq(channelCode))).thenReturn(smsClient);
|
||||||
// mock SmsClient 的方法
|
// mock SmsClient 的方法
|
||||||
List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class);
|
List<SmsReceiveRespDTO> receiveResults = randomPojoList(SmsReceiveRespDTO.class);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue