重构短信功能
parent
37c39365ec
commit
009f332106
@ -1,107 +0,0 @@
|
||||
package cn.iocoder.dashboard.framework.sms;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* 短信父接口
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/1/25 14:14
|
||||
*/
|
||||
public interface SmsClient<R> {
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param msgBody 消息内容
|
||||
* @param targets 发送对象列表
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
SmsResult<R> send(SmsBody msgBody, Collection<String> targets);
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param msgBody 消息内容
|
||||
* @param target 发送对象
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
default SmsResult<R> send(SmsBody msgBody, String target) {
|
||||
if (StringUtils.isBlank(target)) {
|
||||
return failResult();
|
||||
}
|
||||
|
||||
return send(msgBody, Collections.singletonList(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param msgBody 消息内容
|
||||
* @param targets 发送对象列表
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
default SmsResult<R> send(SmsBody msgBody, String... targets) {
|
||||
if (targets == null) {
|
||||
return failResult();
|
||||
}
|
||||
|
||||
return send(msgBody, Arrays.asList(targets));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 异步发送消息
|
||||
*
|
||||
* @param msgBody 消息内容
|
||||
* @param targets 发送对象列表
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
SmsResult<R> sendAsync(SmsBody msgBody, Collection<String> targets);
|
||||
|
||||
/**
|
||||
* 异步发送消息
|
||||
*
|
||||
* @param msgBody 消息内容
|
||||
* @param target 发送对象
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
default SmsResult<R> sendAsync(SmsBody msgBody, String target) {
|
||||
if (StringUtils.isBlank(target)) {
|
||||
return failResult("target must not null.");
|
||||
}
|
||||
|
||||
return sendAsync(msgBody, Collections.singletonList(target));
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步发送消息
|
||||
*
|
||||
* @param msgBody 消息内容
|
||||
* @param targets 发送对象列表
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
default SmsResult<R> sendAsync(SmsBody msgBody, String... targets) {
|
||||
if (targets == null) {
|
||||
return failResult("targets must not null.");
|
||||
}
|
||||
|
||||
return sendAsync(msgBody, Arrays.asList(targets));
|
||||
}
|
||||
|
||||
default SmsResult<R> failResult() {
|
||||
SmsResult<R> resultBody = new SmsResult<>();
|
||||
resultBody.setSuccess(false);
|
||||
return resultBody;
|
||||
}
|
||||
|
||||
default SmsResult<R> failResult(String message) {
|
||||
SmsResult<R> resultBody = failResult();
|
||||
resultBody.setMessage(message);
|
||||
return resultBody;
|
||||
}
|
||||
}
|
||||
@ -1,42 +0,0 @@
|
||||
package cn.iocoder.dashboard.framework.sms;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.dashboard.common.exception.ServiceException;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.SMS_CHANNEL_NOT_INIT;
|
||||
|
||||
/**
|
||||
* 抽象短信客户端工厂
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/1/28 14:01
|
||||
*/
|
||||
public class SmsClientAdapter {
|
||||
|
||||
private final Map<Long, SmsClient<?>> smsSenderMap;
|
||||
|
||||
public SmsClientAdapter(Map<Long, SmsClient<?>> smsSenderMap) {
|
||||
if (ObjectUtil.isEmpty(smsSenderMap)) {
|
||||
throw new ServiceException(SMS_CHANNEL_NOT_INIT);
|
||||
}
|
||||
this.smsSenderMap = smsSenderMap;
|
||||
}
|
||||
|
||||
public void flushClient(Map<Long, SmsClient<?>> smsSenderMap) {
|
||||
this.smsSenderMap.clear();
|
||||
smsSenderMap.putAll(Collections.unmodifiableMap(smsSenderMap));
|
||||
}
|
||||
|
||||
public SmsResult<?> send(Long channelId, SmsBody smsBody, Collection<String> targetPhone) {
|
||||
SmsClient<?> smsClient = getSmsSender(channelId);
|
||||
return smsClient.send(smsBody, targetPhone);
|
||||
}
|
||||
|
||||
private SmsClient<?> getSmsSender(Long channelId) {
|
||||
return smsSenderMap.get(channelId);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package cn.iocoder.dashboard.framework.sms.client;
|
||||
|
||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
|
||||
|
||||
/**
|
||||
* 抽象短息客户端
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/2/1 9:28
|
||||
*/
|
||||
public abstract class AbstractSmsClient<R> implements SmsClient<R> {
|
||||
|
||||
/**
|
||||
* 短信渠道参数
|
||||
*/
|
||||
protected final SmsChannelPropertyVO channelVO;
|
||||
|
||||
/**
|
||||
* 构造阿里云短信发送处理
|
||||
*
|
||||
* @param channelVO 阿里云短信配置
|
||||
*/
|
||||
public AbstractSmsClient(SmsChannelPropertyVO channelVO) {
|
||||
this.channelVO = channelVO;
|
||||
}
|
||||
|
||||
|
||||
public SmsChannelPropertyVO getProperty() {
|
||||
return channelVO;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package cn.iocoder.dashboard.framework.sms.client;
|
||||
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 短信父接口
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/1/25 14:14
|
||||
*/
|
||||
public interface SmsClient<R> {
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param smsBody 消息内容
|
||||
* @param targets 发送对象列表
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
SmsResult<R> send(SmsBody smsBody, Collection<String> targets);
|
||||
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package cn.iocoder.dashboard.framework.sms.core;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
|
||||
import cn.iocoder.dashboard.common.exception.ServiceException;
|
||||
import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
|
||||
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
|
||||
import cn.iocoder.dashboard.framework.sms.client.AliyunSmsClient;
|
||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 短信客户端工厂
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/1/28 14:01
|
||||
*/
|
||||
@Component
|
||||
public class SmsClientFactory {
|
||||
|
||||
private final Map<Long, AbstractSmsClient<?>> smsSenderMap = new ConcurrentHashMap<>(8);
|
||||
|
||||
/**
|
||||
* 创建短信客户端
|
||||
*
|
||||
* @param propertyVO 参数对象
|
||||
* @return 客户端id(默认channelId)
|
||||
*/
|
||||
public Long createClient(SmsChannelPropertyVO propertyVO) {
|
||||
if (StrUtil.isBlank(propertyVO.getCode())) {
|
||||
throw ServiceExceptionUtil.exception(PARAM_VALUE_IS_NULL, "短信渠道编码");
|
||||
}
|
||||
if (ObjectUtil.isNull(propertyVO.getId())) {
|
||||
throw ServiceExceptionUtil.exception(PARAM_VALUE_IS_NULL, "短信渠道ID");
|
||||
}
|
||||
|
||||
AbstractSmsClient<?> sender = createClient(SmsChannelEnum.getByCode(propertyVO.getCode()), propertyVO);
|
||||
smsSenderMap.put(propertyVO.getId(), sender);
|
||||
return propertyVO.getId();
|
||||
}
|
||||
|
||||
private AbstractSmsClient<?> createClient(SmsChannelEnum channelEnum, SmsChannelPropertyVO channelVO) {
|
||||
if (channelEnum == null) {
|
||||
throw new ServiceException(INVALID_CHANNEL_CODE);
|
||||
}
|
||||
switch (channelEnum) {
|
||||
case ALI:
|
||||
return new AliyunSmsClient(channelVO);
|
||||
// TODO fill more channel
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw new ServiceException(SMS_SENDER_NOT_FOUND);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取短信客户端
|
||||
*
|
||||
* @param channelId 渠道id
|
||||
* @return 短信id
|
||||
*/
|
||||
public AbstractSmsClient<?> getClient(Long channelId) {
|
||||
return smsSenderMap.get(channelId);
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package cn.iocoder.dashboard.framework.sms;
|
||||
package cn.iocoder.dashboard.framework.sms.core;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms;
|
||||
|
||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLog;
|
||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLogDO;
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface SmsLogMapper extends BaseMapper<SmsLog> {
|
||||
public interface SmsLogMapper extends BaseMapper<SmsLogDO> {
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,30 @@
|
||||
package cn.iocoder.dashboard.modules.system.enums.sms;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 短信发送状态
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/2/1 13:39
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum SmsSendStatusEnum {
|
||||
|
||||
//异步转发中
|
||||
ASYNC(1),
|
||||
|
||||
//发送中
|
||||
SENDING(2),
|
||||
|
||||
//失败
|
||||
FAIL(3),
|
||||
|
||||
//成功
|
||||
SUCCESS(4);
|
||||
|
||||
private final int status;
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package cn.iocoder.dashboard.modules.system.mq.consumer.sms;
|
||||
|
||||
import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
|
||||
import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage;
|
||||
import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage;
|
||||
import cn.iocoder.dashboard.modules.system.service.sms.SmsService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* 针对 {@link SysDeptRefreshMessage} 的消费者
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> {
|
||||
|
||||
@Resource
|
||||
private SmsService smsService;
|
||||
|
||||
@Override
|
||||
public void onMessage(SmsSendMessage message) {
|
||||
log.info("[onMessage][收到 发送短信 消息]");
|
||||
SmsResult<?> send = smsService.send(message.getSmsBody(), message.getTargetPhones());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package cn.iocoder.dashboard.modules.system.mq.message.sms;
|
||||
|
||||
import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 部门数据刷新 Message
|
||||
*/
|
||||
@Data
|
||||
public class SmsSendMessage implements ChannelMessage {
|
||||
|
||||
private SmsBody smsBody;
|
||||
|
||||
private List<String> targetPhones;
|
||||
|
||||
@Override
|
||||
public String getChannel() {
|
||||
return "sms.send";
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package cn.iocoder.dashboard.modules.system.mq.producer.sms;
|
||||
|
||||
import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
|
||||
import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 短信的 Producer
|
||||
*/
|
||||
@Component
|
||||
public class SmsProducer {
|
||||
|
||||
@Resource
|
||||
private StringRedisTemplate stringRedisTemplate;
|
||||
|
||||
/**
|
||||
* 发送 {@link SmsSendMessage} 消息
|
||||
*/
|
||||
public void sendSmsSendMessage(SmsBody smsBody, List<String> targetPhoneList) {
|
||||
SmsSendMessage message = new SmsSendMessage();
|
||||
message.setSmsBody(smsBody);
|
||||
message.setTargetPhones(targetPhoneList);
|
||||
RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
package cn.iocoder.dashboard.modules.system.service.sms;
|
||||
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 短信Service接口
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/1/25 9:24
|
||||
*/
|
||||
public interface SmsService {
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param smsBody 消息内容
|
||||
* @param targetPhones 发送对象手机号列表
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
SmsResult<?> send(SmsBody smsBody, List<String> targetPhones);
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param smsBody 消息内容
|
||||
* @param targetPhone 发送对象手机号
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
default SmsResult<?> send(SmsBody smsBody, String targetPhone) {
|
||||
if (StringUtils.isBlank(targetPhone)) {
|
||||
return failResult("targetPhone must not null.");
|
||||
}
|
||||
|
||||
return send(smsBody, Collections.singletonList(targetPhone));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @param smsBody 消息内容
|
||||
* @param targetPhones 发送对象手机号数组
|
||||
* @return 是否发送成功
|
||||
*/
|
||||
default SmsResult<?> send(SmsBody smsBody, String... targetPhones) {
|
||||
if (targetPhones == null) {
|
||||
return failResult("targetPhones must not null.");
|
||||
}
|
||||
|
||||
return send(smsBody, Arrays.asList(targetPhones));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 异步发送消息
|
||||
*
|
||||
* @param msgBody 消息内容
|
||||
* @param targetPhones 发送对象列表
|
||||
*/
|
||||
void sendAsync(SmsBody msgBody, List<String> targetPhones);
|
||||
|
||||
/**
|
||||
* 异步发送消息
|
||||
*
|
||||
* @param msgBody 消息内容
|
||||
* @param targetPhone 发送对象
|
||||
*/
|
||||
default void sendAsync(SmsBody msgBody, String targetPhone) {
|
||||
if (StringUtils.isBlank(targetPhone)) {
|
||||
return;
|
||||
}
|
||||
sendAsync(msgBody, Collections.singletonList(targetPhone));
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步发送消息
|
||||
*
|
||||
* @param msgBody 消息内容
|
||||
* @param targetPhones 发送对象列表
|
||||
*/
|
||||
default void sendAsync(SmsBody msgBody, String... targetPhones) {
|
||||
if (targetPhones == null) {
|
||||
return;
|
||||
}
|
||||
sendAsync(msgBody, Arrays.asList(targetPhones));
|
||||
}
|
||||
|
||||
|
||||
default SmsResult<?> failResult(String message) {
|
||||
SmsResult<?> resultBody = new SmsResult<>();
|
||||
resultBody.setSuccess(false);
|
||||
resultBody.setMessage(message);
|
||||
return resultBody;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package cn.iocoder.dashboard.modules.system.service.sms.impl;
|
||||
|
||||
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
|
||||
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
|
||||
import cn.iocoder.dashboard.modules.system.mq.producer.sms.SmsProducer;
|
||||
import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
|
||||
import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService;
|
||||
import cn.iocoder.dashboard.modules.system.service.sms.SmsService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 短信日志Service实现类
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/1/25 9:25
|
||||
*/
|
||||
@Service
|
||||
public class SmsServiceImpl implements SmsService {
|
||||
|
||||
@Resource
|
||||
private SmsChannelService channelService;
|
||||
|
||||
@Resource
|
||||
private SmsLogService smsLogService;
|
||||
|
||||
@Resource
|
||||
private SmsProducer smsProducer;
|
||||
|
||||
@Override
|
||||
public SmsResult<?> send(SmsBody smsBody, List<String> targetPhones) {
|
||||
AbstractSmsClient<?> client = channelService.getClient(smsBody.getTemplateCode());
|
||||
Long logId = smsLogService.beforeSendLog(smsBody, targetPhones, client, false);
|
||||
|
||||
SmsResult<?> result = client.send(smsBody, targetPhones);
|
||||
|
||||
smsLogService.afterSendLog(logId, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAsync(SmsBody smsBody, List<String> targetPhones) {
|
||||
AbstractSmsClient<?> client = channelService.getClient(smsBody.getTemplateCode());
|
||||
smsLogService.beforeSendLog(smsBody, targetPhones, client, true);
|
||||
smsProducer.sendSmsSendMessage(smsBody, targetPhones);
|
||||
}
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
package cn.iocoder.dashboard.modules.system.sms;
|
||||
|
||||
import cn.iocoder.dashboard.framework.sms.SmsClient;
|
||||
import cn.iocoder.dashboard.framework.sms.SmsClientAdapter;
|
||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
|
||||
import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 短信服务配置
|
||||
*
|
||||
* @author guer
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnProperty("sms.enabled")
|
||||
public class SmsConfiguration {
|
||||
|
||||
@Resource
|
||||
private SmsChannelService channelService;
|
||||
|
||||
@Bean
|
||||
public SmsClientAdapter smsClientWrapper() {
|
||||
List<SmsChannelAllVO> smsChannelAllVOList = channelService.listChannelAllEnabledInfo();
|
||||
Map<Long, SmsClient<?>> channelId2SmsClientMap = SmsSenderUtils.init(smsChannelAllVOList);
|
||||
return new SmsClientAdapter(channelId2SmsClientMap);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,143 +0,0 @@
|
||||
package cn.iocoder.dashboard.modules.system.sms;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
|
||||
import cn.iocoder.dashboard.common.exception.ServiceException;
|
||||
import cn.iocoder.dashboard.framework.sms.SmsBody;
|
||||
import cn.iocoder.dashboard.framework.sms.SmsClient;
|
||||
import cn.iocoder.dashboard.framework.sms.SmsClientAdapter;
|
||||
import cn.iocoder.dashboard.framework.sms.SmsResult;
|
||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
|
||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO;
|
||||
import cn.iocoder.dashboard.modules.system.sms.client.AliSmsClient;
|
||||
import cn.iocoder.dashboard.modules.system.sms.proxy.SmsClientLogProxy;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
|
||||
|
||||
/**
|
||||
* 短信发送者工厂
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/1/25 16:18
|
||||
*/
|
||||
public class SmsSenderUtils {
|
||||
|
||||
/**
|
||||
* 短信渠道id:短信客户端map
|
||||
* key: channelId
|
||||
* val: SmsClient
|
||||
*/
|
||||
private static final Map<Long, SmsClient<?>> smsSenderMap = new ConcurrentHashMap<>(8);
|
||||
|
||||
/**
|
||||
* 短信模板code: 短信渠道id map
|
||||
* key: templateCode
|
||||
* val: channelId
|
||||
*/
|
||||
private static final Map<String, Long> templateCode2ChannelIdMap = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 将短信渠道信息初始化成短信客户端
|
||||
*
|
||||
* @param smsChannelAllVOList 短信渠道信息
|
||||
* @return 短信渠道id:短信客户端map
|
||||
*/
|
||||
public synchronized static Map<Long, SmsClient<?>> init(List<SmsChannelAllVO> smsChannelAllVOList) {
|
||||
if (ObjectUtil.isEmpty(smsChannelAllVOList)) {
|
||||
throw new ServiceException(SMS_CHANNEL_NOT_FOUND);
|
||||
}
|
||||
addSender(smsChannelAllVOList);
|
||||
return smsSenderMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置短信客户端信息
|
||||
*
|
||||
* @param smsClientAdapter 短信客户端适配器
|
||||
* @param smsChannelAllVOList 短信渠道信息集合
|
||||
*/
|
||||
public synchronized static void flush(SmsClientAdapter smsClientAdapter, List<SmsChannelAllVO> smsChannelAllVOList) {
|
||||
smsSenderMap.clear();
|
||||
smsClientAdapter.flushClient(init(smsChannelAllVOList));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信
|
||||
*
|
||||
* @param smsClientAdapter 短信客户端适配器
|
||||
* @param smsBody 短信内容
|
||||
* @param targetPhones 对象手机集合
|
||||
* @return 短信发送结果
|
||||
*/
|
||||
public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, Collection<String> targetPhones) {
|
||||
Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode());
|
||||
if (channelId == null) {
|
||||
throw new ServiceException(SMS_SENDER_NOT_FOUND);
|
||||
}
|
||||
return smsClientAdapter.send(channelId, smsBody, targetPhones);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信
|
||||
*
|
||||
* @param smsClientAdapter 短信客户端适配器
|
||||
* @param smsBody 短信内容
|
||||
* @param targetPhone 对象手机
|
||||
* @return 短信发送结果
|
||||
*/
|
||||
public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, String targetPhone) {
|
||||
Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode());
|
||||
if (channelId == null) {
|
||||
throw new ServiceException(SMS_SENDER_NOT_FOUND);
|
||||
}
|
||||
return smsClientAdapter.send(channelId, smsBody, Collections.singletonList(targetPhone));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送短信
|
||||
*
|
||||
* @param smsClientAdapter 短信客户端适配器
|
||||
* @param smsBody 短信内容
|
||||
* @param targetPhones 对象手机数组
|
||||
* @return 短信发送结果
|
||||
*/
|
||||
public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, String... targetPhones) {
|
||||
Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode());
|
||||
if (channelId == null) {
|
||||
throw new ServiceException(SMS_SENDER_NOT_FOUND);
|
||||
}
|
||||
return smsClientAdapter.send(channelId, smsBody, Arrays.asList(targetPhones));
|
||||
}
|
||||
|
||||
|
||||
private static void addSender(List<SmsChannelAllVO> smsChannelAllVOList) {
|
||||
smsChannelAllVOList.forEach(channelAllVO -> addSender(SmsChannelEnum.getByCode(channelAllVO.getCode()), channelAllVO));
|
||||
}
|
||||
|
||||
private static void addSender(SmsChannelEnum channelEnum, SmsChannelAllVO channelAllVO) {
|
||||
if (channelEnum == null) {
|
||||
throw new ServiceException(INVALID_CHANNEL_CODE);
|
||||
}
|
||||
List<SmsTemplateVO> templateList = channelAllVO.getTemplateList();
|
||||
if (ObjectUtil.isEmpty(templateList)) {
|
||||
throw new ServiceException(SMS_TEMPLATE_NOT_FOUND);
|
||||
}
|
||||
SmsClient<?> aliSmsClient = getSender(channelEnum, channelAllVO);
|
||||
templateList.forEach(smsTemplateVO -> templateCode2ChannelIdMap.put(smsTemplateVO.getCode(), channelAllVO.getId()));
|
||||
smsSenderMap.put(channelAllVO.getId(), aliSmsClient);
|
||||
}
|
||||
|
||||
private static SmsClient<?> getSender(SmsChannelEnum channelEnum, SmsChannelAllVO channelAllVO) {
|
||||
switch (channelEnum) {
|
||||
case ALI:
|
||||
return new SmsClientLogProxy<>(new AliSmsClient(channelAllVO));
|
||||
// TODO fill more channel
|
||||
default:
|
||||
break;
|
||||
}
|
||||
throw new ServiceException(SMS_SENDER_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
@ -1,48 +0,0 @@
|
||||
package cn.iocoder.dashboard.modules.system.sms.proxy;
|
||||
|
||||
import cn.iocoder.dashboard.framework.sms.SmsBody;
|
||||
import cn.iocoder.dashboard.framework.sms.SmsClient;
|
||||
import cn.iocoder.dashboard.framework.sms.SmsResult;
|
||||
import cn.iocoder.dashboard.util.json.JsonUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* 消息父接口
|
||||
*
|
||||
* @author zzf
|
||||
* @date 2021/1/22 15:46
|
||||
*/
|
||||
@Slf4j
|
||||
public class SmsClientLogProxy<R> implements SmsClient<R> {
|
||||
|
||||
private final SmsClient<R> smsClient;
|
||||
|
||||
@Override
|
||||
public SmsResult<R> send(SmsBody msgBody, Collection<String> targets) {
|
||||
log.debug("ready send sms, body: {}, target: {}", JsonUtils.toJsonString(msgBody), targets);
|
||||
|
||||
SmsResult<R> resultBody = smsClient.send(msgBody, targets);
|
||||
|
||||
if (resultBody.getSuccess()) {
|
||||
//
|
||||
} else {
|
||||
log.warn("send sms fail, body: {}, target: {}, resultBody: {}",
|
||||
JsonUtils.toJsonString(msgBody),
|
||||
targets,
|
||||
JsonUtils.toJsonString(resultBody)
|
||||
);
|
||||
}
|
||||
return resultBody;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsResult<R> sendAsync(SmsBody msgBody, Collection<String> targets) {
|
||||
return send(msgBody, targets);
|
||||
}
|
||||
|
||||
public SmsClientLogProxy(SmsClient<R> smsClient) {
|
||||
this.smsClient = smsClient;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue