mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-30 03:01:53 +08:00
重构短信功能
This commit is contained in:
parent
37c39365ec
commit
009f332106
@ -884,75 +884,4 @@ INSERT INTO `sys_user_role` VALUES (5, 100, 1, '', NULL, '', NULL, b'0');
|
|||||||
INSERT INTO `sys_user_role` VALUES (6, 100, 2, '', NULL, '', NULL, b'0');
|
INSERT INTO `sys_user_role` VALUES (6, 100, 2, '', NULL, '', NULL, b'0');
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- Table structure for sms_channel
|
|
||||||
-- ----------------------------
|
|
||||||
DROP TABLE IF EXISTS `sms_channel`;
|
|
||||||
CREATE TABLE `sms_channel` (
|
|
||||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
|
|
||||||
`code` varchar(50) not null COMMENT '编码(来自枚举类 阿里、华为、七牛等)',
|
|
||||||
`api_key` varchar(100) NOT NULL COMMENT '账号id',
|
|
||||||
`api_secret` varchar(100) NOT NULL COMMENT '账号秘钥',
|
|
||||||
`api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识',
|
|
||||||
`name` varchar(50) not null COMMENT '名称',
|
|
||||||
`signature` varchar(50) not null COMMENT '签名值',
|
|
||||||
`remark` varchar(200) NOT NULL COMMENT '备注',
|
|
||||||
|
|
||||||
`status` tinyint(4) NOT NULL default 0 COMMENT '启用状态(0正常 1停用)',
|
|
||||||
`create_by` varchar(64) not null DEFAULT '' COMMENT '创建者',
|
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|
||||||
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
|
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
|
||||||
`deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',
|
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信渠道';
|
|
||||||
/*
|
|
||||||
优先级值一样时,按照id顺序取值
|
|
||||||
*/
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- Table structure for sms_template
|
|
||||||
-- ----------------------------
|
|
||||||
DROP TABLE IF EXISTS `sms_template`;
|
|
||||||
CREATE TABLE `sms_template` (
|
|
||||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
|
|
||||||
`channel_code` varchar(50) not null COMMENT '短信渠道编码(来自枚举类)',
|
|
||||||
`channel_id` bigint(20) not null COMMENT '短信渠道id (对于前端来说就是绑定一个签名)',
|
|
||||||
`type` tinyint(4) NOT NULL default 1 COMMENT '消息类型 [0验证码 1短信通知 2推广短信 3国际/港澳台消息]',
|
|
||||||
`biz_code` varchar(50) not null COMMENT '业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)',
|
|
||||||
`code` varchar(50) not null COMMENT '编码',
|
|
||||||
`name` varchar(50) not null COMMENT '名称',
|
|
||||||
`api_template_id` varchar(100) NOT NULL COMMENT '实际渠道模板唯一标识',
|
|
||||||
`content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',
|
|
||||||
`params` varchar(200) NOT NULL DEFAULT '' COMMENT '参数数组(自动根据内容生成)',
|
|
||||||
`remark` varchar(200) NOT NULL COMMENT '备注',
|
|
||||||
|
|
||||||
`status` tinyint(4) NOT NULL default 0 COMMENT '启用状态(0正常 1停用)',
|
|
||||||
`create_by` varchar(64) not null DEFAULT '' COMMENT '创建者',
|
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|
||||||
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
|
|
||||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
|
||||||
`deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',
|
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信模板';
|
|
||||||
|
|
||||||
-- ----------------------------
|
|
||||||
-- Table structure for sms_log
|
|
||||||
-- ----------------------------
|
|
||||||
DROP TABLE IF EXISTS `sms_log`;
|
|
||||||
CREATE TABLE `sms_log` (
|
|
||||||
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
|
|
||||||
`channel_code` varchar(50) not null COMMENT '短信渠道编码(来自枚举类)',
|
|
||||||
`api_sms_id` varchar(50) not null COMMENT '实际渠道短信唯一标识',
|
|
||||||
`template_id` bigint(20) NOT NULL COMMENT '模板id',
|
|
||||||
`phone` char(11) not null COMMENT '手机号',
|
|
||||||
`content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',
|
|
||||||
`remark` varchar(200) NOT NULL COMMENT '备注',
|
|
||||||
`send_status` tinyint(4) NOT NULL default 0 COMMENT '发送状态(0发送中 1成功 2失败)',
|
|
||||||
`create_by` varchar(64) not null DEFAULT '' COMMENT '创建者',
|
|
||||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
|
||||||
PRIMARY KEY (`id`) USING BTREE
|
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信日志';
|
|
||||||
|
|
||||||
SET FOREIGN_KEY_CHECKS = 1;
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
|
82
sql/sms.sql
Normal file
82
sql/sms.sql
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
/*
|
||||||
|
--2021.02.01 by fight, sms about table info
|
||||||
|
*/
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for sms_channel
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `sms_channel`;
|
||||||
|
CREATE TABLE `sms_channel`
|
||||||
|
(
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
|
||||||
|
`code` varchar(50) NOT NULL COMMENT '编码(来自枚举类 阿里、华为、七牛等)',
|
||||||
|
`api_key` varchar(100) NOT NULL COMMENT '账号id',
|
||||||
|
`api_secret` varchar(100) NOT NULL COMMENT '账号秘钥',
|
||||||
|
`api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识',
|
||||||
|
`name` varchar(50) NOT NULL COMMENT '名称',
|
||||||
|
`signature` varchar(50) NOT NULL COMMENT '签名值',
|
||||||
|
`remark` varchar(200) NOT NULL COMMENT '备注',
|
||||||
|
|
||||||
|
`status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '启用状态(0正常 1停用)',
|
||||||
|
`create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
) ENGINE = InnoDB
|
||||||
|
AUTO_INCREMENT = 1
|
||||||
|
DEFAULT CHARSET = utf8mb4 COMMENT ='短信渠道';
|
||||||
|
/*
|
||||||
|
优先级值一样时,按照id顺序取值
|
||||||
|
*/
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for sms_template
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `sms_template`;
|
||||||
|
CREATE TABLE `sms_template`
|
||||||
|
(
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
|
||||||
|
`channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)',
|
||||||
|
`channel_id` bigint(20) NOT NULL COMMENT '短信渠道id (对于前端来说就是绑定一个签名)',
|
||||||
|
`type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '消息类型 [0验证码 1短信通知 2推广短信 3国际/港澳台消息]',
|
||||||
|
`biz_code` varchar(50) NOT NULL COMMENT '业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)',
|
||||||
|
`code` varchar(50) NOT NULL COMMENT '编码',
|
||||||
|
`name` varchar(50) NOT NULL COMMENT '名称',
|
||||||
|
`api_template_id` varchar(100) NOT NULL COMMENT '实际渠道模板唯一标识',
|
||||||
|
`content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',
|
||||||
|
`params` varchar(200) NOT NULL DEFAULT '' COMMENT '参数数组(自动根据内容生成)',
|
||||||
|
`remark` varchar(200) NOT NULL COMMENT '备注',
|
||||||
|
|
||||||
|
`status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '启用状态(0正常 1停用)',
|
||||||
|
`create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
`update_by` varchar(64) DEFAULT '' COMMENT '更新者',
|
||||||
|
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||||
|
`deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
) ENGINE = InnoDB
|
||||||
|
AUTO_INCREMENT = 1
|
||||||
|
DEFAULT CHARSET = utf8mb4 COMMENT ='短信模板';
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for sms_log
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `sms_log`;
|
||||||
|
CREATE TABLE `sms_log`
|
||||||
|
(
|
||||||
|
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
|
||||||
|
`channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)',
|
||||||
|
`channel_id` bigint(20) NOT NULL COMMENT '短信渠道id',
|
||||||
|
`template_code` varchar(50) NOT NULL COMMENT '渠道编码',
|
||||||
|
`phones` char(11) NOT NULL COMMENT '手机号(数组json字符串)',
|
||||||
|
`content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',
|
||||||
|
`remark` varchar(200) DEFAULT NULL COMMENT '备注',
|
||||||
|
`send_status` tinyint(4) NOT NULL DEFAULT 2 COMMENT '发送状态(1异步推送中 2发送中 3失败 4成功)',
|
||||||
|
`create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者',
|
||||||
|
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE
|
||||||
|
) ENGINE = InnoDB
|
||||||
|
AUTO_INCREMENT = 1
|
||||||
|
DEFAULT CHARSET = utf8mb4 COMMENT ='短信日志';
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,9 +1,8 @@
|
|||||||
package cn.iocoder.dashboard.modules.system.sms.client;
|
package cn.iocoder.dashboard.framework.sms.client;
|
||||||
|
|
||||||
import cn.iocoder.dashboard.framework.sms.SmsBody;
|
import cn.iocoder.dashboard.framework.sms.core.SmsBody;
|
||||||
import cn.iocoder.dashboard.framework.sms.SmsClient;
|
import cn.iocoder.dashboard.framework.sms.core.SmsResult;
|
||||||
import cn.iocoder.dashboard.framework.sms.SmsResult;
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
|
|
||||||
import com.aliyuncs.DefaultAcsClient;
|
import com.aliyuncs.DefaultAcsClient;
|
||||||
import com.aliyuncs.IAcsClient;
|
import com.aliyuncs.IAcsClient;
|
||||||
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
|
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
|
||||||
@ -23,7 +22,7 @@ import java.util.Collection;
|
|||||||
* @date 2021/1/25 14:17
|
* @date 2021/1/25 14:17
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AliSmsClient implements SmsClient<SendSmsResponse> {
|
public class AliyunSmsClient extends AbstractSmsClient<SendSmsResponse> {
|
||||||
|
|
||||||
private static final String OK = "OK";
|
private static final String OK = "OK";
|
||||||
|
|
||||||
@ -33,8 +32,6 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> {
|
|||||||
|
|
||||||
private static final String ENDPOINT = "cn-hangzhou";
|
private static final String ENDPOINT = "cn-hangzhou";
|
||||||
|
|
||||||
private final SmsChannelAllVO channelVO;
|
|
||||||
|
|
||||||
private final IAcsClient acsClient;
|
private final IAcsClient acsClient;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,9 +39,8 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> {
|
|||||||
*
|
*
|
||||||
* @param channelVO 阿里云短信配置
|
* @param channelVO 阿里云短信配置
|
||||||
*/
|
*/
|
||||||
public AliSmsClient(SmsChannelAllVO channelVO) {
|
public AliyunSmsClient(SmsChannelPropertyVO channelVO) {
|
||||||
|
super(channelVO);
|
||||||
this.channelVO = channelVO;
|
|
||||||
|
|
||||||
String accessKeyId = channelVO.getApiKey();
|
String accessKeyId = channelVO.getApiKey();
|
||||||
String accessKeySecret = channelVO.getApiSecret();
|
String accessKeySecret = channelVO.getApiSecret();
|
||||||
@ -57,13 +53,13 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> {
|
|||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SmsResult<SendSmsResponse> send(SmsBody msgBody, Collection<String> targets) {
|
public SmsResult<SendSmsResponse> send(SmsBody smsBody, Collection<String> targets) {
|
||||||
SendSmsRequest request = new SendSmsRequest();
|
SendSmsRequest request = new SendSmsRequest();
|
||||||
request.setSysMethod(MethodType.POST);
|
request.setSysMethod(MethodType.POST);
|
||||||
request.setPhoneNumbers(StringUtils.join(targets, ","));
|
request.setPhoneNumbers(StringUtils.join(targets, ","));
|
||||||
request.setSignName(channelVO.getApiSignatureId());
|
request.setSignName(channelVO.getApiSignatureId());
|
||||||
request.setTemplateCode(channelVO.getTemplateByTemplateCode(msgBody.getCode()).getApiTemplateId());
|
request.setTemplateCode(channelVO.getTemplateByTemplateCode(smsBody.getTemplateCode()).getApiTemplateId());
|
||||||
request.setTemplateParam(msgBody.getParamsStr());
|
request.setTemplateParam(smsBody.getParamsStr());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
|
SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
|
||||||
@ -78,14 +74,15 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> {
|
|||||||
return resultBody;
|
return resultBody;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.debug(e.getMessage(), e);
|
log.debug(e.getMessage(), e);
|
||||||
|
return failResult("发送异常: " + e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return failResult();
|
SmsResult<SendSmsResponse> failResult(String message) {
|
||||||
}
|
SmsResult<SendSmsResponse> resultBody = new SmsResult<>();
|
||||||
|
resultBody.setSuccess(false);
|
||||||
@Override
|
resultBody.setMessage(message);
|
||||||
public SmsResult<SendSmsResponse> sendAsync(SmsBody msgBody, Collection<String> targets) {
|
return resultBody;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -1,9 +1,10 @@
|
|||||||
package cn.iocoder.dashboard.framework.sms;
|
package cn.iocoder.dashboard.framework.sms.core;
|
||||||
|
|
||||||
import cn.iocoder.dashboard.util.json.JsonUtils;
|
import cn.iocoder.dashboard.util.json.JsonUtils;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 消息内容实体类
|
* 消息内容实体类
|
||||||
@ -11,10 +12,15 @@ import java.util.Map;
|
|||||||
@Data
|
@Data
|
||||||
public class SmsBody {
|
public class SmsBody {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 消息日志id
|
||||||
|
*/
|
||||||
|
private Long smsLogId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模板编码
|
* 模板编码
|
||||||
*/
|
*/
|
||||||
private String code;
|
private String templateCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 参数列表
|
* 参数列表
|
@ -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;
|
import lombok.Data;
|
||||||
|
|
@ -44,11 +44,5 @@ public class SmsChannelController {
|
|||||||
return success(service.createChannel(reqVO));
|
return success(service.createChannel(reqVO));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOperation("刷新消息渠道信息")
|
|
||||||
@PutMapping("/flush")
|
|
||||||
public CommonResult<Boolean> flushChannel() {
|
|
||||||
return success(service.flushChannel());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
public class SmsChannelAllVO implements Serializable {
|
public class SmsChannelPropertyVO implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* id
|
* id
|
@ -17,6 +17,7 @@ public class SmsTemplateVO {
|
|||||||
* 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)
|
* 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)
|
||||||
*/
|
*/
|
||||||
private String bizCode;
|
private String bizCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编码
|
* 编码
|
||||||
*/
|
*/
|
||||||
@ -27,4 +28,11 @@ public class SmsTemplateVO {
|
|||||||
*/
|
*/
|
||||||
private String apiTemplateId;
|
private String apiTemplateId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 内容
|
||||||
|
*/
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ package cn.iocoder.dashboard.modules.system.convert.sms;
|
|||||||
|
|
||||||
import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
|
import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
|
||||||
import cn.iocoder.dashboard.common.pojo.PageResult;
|
import cn.iocoder.dashboard.common.pojo.PageResult;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
|
||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO;
|
||||||
@ -28,7 +28,7 @@ public interface SmsChannelConvert {
|
|||||||
|
|
||||||
List<SmsChannelEnumRespVO> convertEnum(List<SmsChannelEnum> bean);
|
List<SmsChannelEnumRespVO> convertEnum(List<SmsChannelEnum> bean);
|
||||||
|
|
||||||
List<SmsChannelAllVO> convert(List<SmsChannelDO> bean);
|
List<SmsChannelPropertyVO> convert(List<SmsChannelDO> bean);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms;
|
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 com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
import org.apache.ibatis.annotations.Mapper;
|
import org.apache.ibatis.annotations.Mapper;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface SmsLogMapper extends BaseMapper<SmsLog> {
|
public interface SmsLogMapper extends BaseMapper<SmsLogDO> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms;
|
|||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -15,8 +16,9 @@ import java.util.Date;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode
|
@EqualsAndHashCode
|
||||||
|
@Accessors(chain = true)
|
||||||
@TableName(value = "sms_log", autoResultMap = true)
|
@TableName(value = "sms_log", autoResultMap = true)
|
||||||
public class SmsLog implements Serializable {
|
public class SmsLogDO implements Serializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 自增编号
|
* 自增编号
|
||||||
@ -29,19 +31,19 @@ public class SmsLog implements Serializable {
|
|||||||
private String channelCode;
|
private String channelCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实际渠道短信唯一标识
|
* 短信渠道id
|
||||||
*/
|
*/
|
||||||
private String apiSmsId;
|
private Long channelId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模板id
|
* 模板id
|
||||||
*/
|
*/
|
||||||
private Long templateId;
|
private String templateCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 手机号
|
* 手机号(数组json字符串)
|
||||||
*/
|
*/
|
||||||
private String phone;
|
private String phones;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 内容
|
* 内容
|
||||||
@ -54,7 +56,7 @@ public class SmsLog implements Serializable {
|
|||||||
private String remark;
|
private String remark;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送状态(0发送中 1成功 2失败)
|
* 发送状态(1异步推送中 2发送中 3失败 4成功)
|
||||||
*/
|
*/
|
||||||
private Integer sendStatus;
|
private Integer sendStatus;
|
||||||
|
|
@ -83,5 +83,6 @@ public interface SysErrorCodeConstants {
|
|||||||
ErrorCode SMS_TEMPLATE_NOT_FOUND = new ErrorCode(1003001003, "没有短信模板信息, 请初始化sms_template表数据。");
|
ErrorCode SMS_TEMPLATE_NOT_FOUND = new ErrorCode(1003001003, "没有短信模板信息, 请初始化sms_template表数据。");
|
||||||
ErrorCode SMS_SENDER_NOT_FOUND = new ErrorCode(1003001004, "没有找到对应的短信发送对象,请检查sms_channel表和sms_template表数据");
|
ErrorCode SMS_SENDER_NOT_FOUND = new ErrorCode(1003001004, "没有找到对应的短信发送对象,请检查sms_channel表和sms_template表数据");
|
||||||
ErrorCode INVALID_CHANNEL_CODE = new ErrorCode(1003001005, "非法的短信渠道code,请检查sms_channel表的code值是否与SmsChannelEnum中的code值一致。");
|
ErrorCode INVALID_CHANNEL_CODE = new ErrorCode(1003001005, "非法的短信渠道code,请检查sms_channel表的code值是否与SmsChannelEnum中的code值一致。");
|
||||||
|
ErrorCode PARAM_VALUE_IS_NULL = new ErrorCode(1003001006, "参数【{}】不能为空");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,7 +1,8 @@
|
|||||||
package cn.iocoder.dashboard.modules.system.service.sms;
|
package cn.iocoder.dashboard.modules.system.service.sms;
|
||||||
|
|
||||||
import cn.iocoder.dashboard.common.pojo.PageResult;
|
import cn.iocoder.dashboard.common.pojo.PageResult;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
|
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
|
||||||
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
|
||||||
@ -17,18 +18,46 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public interface SmsChannelService {
|
public interface SmsChannelService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化短信渠道
|
||||||
|
*/
|
||||||
|
void initSmsClient();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询短信渠道信息
|
||||||
|
*
|
||||||
|
* @param reqVO 参数对象
|
||||||
|
* @return 短信渠道分页对象
|
||||||
|
*/
|
||||||
PageResult<SmsChannelDO> pageChannels(SmsChannelPageReqVO reqVO);
|
PageResult<SmsChannelDO> pageChannels(SmsChannelPageReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建新的渠道信息
|
||||||
|
*
|
||||||
|
* @param reqVO 参数对象
|
||||||
|
* @return 渠道id
|
||||||
|
*/
|
||||||
Long createChannel(SmsChannelCreateReqVO reqVO);
|
Long createChannel(SmsChannelCreateReqVO reqVO);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取短信渠道枚举/渠道编码
|
||||||
|
*
|
||||||
|
* @return 短信渠道枚举/渠道编码
|
||||||
|
*/
|
||||||
List<SmsChannelEnumRespVO> getChannelEnums();
|
List<SmsChannelEnumRespVO> getChannelEnums();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据短信模板编码获取短信客户端
|
||||||
|
*
|
||||||
|
* @param templateCode 短信模板编码
|
||||||
|
* @return 短信客户端
|
||||||
|
*/
|
||||||
|
AbstractSmsClient<?> getClient(String templateCode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询渠道(包含名下模块)信息集合
|
* 查询渠道(包含名下模块)信息集合
|
||||||
*
|
*
|
||||||
* @return 渠道(包含名下模块)信息集合
|
* @return 渠道(包含名下模块)信息集合
|
||||||
*/
|
*/
|
||||||
List<SmsChannelAllVO> listChannelAllEnabledInfo();
|
List<SmsChannelPropertyVO> listChannelAllEnabledInfo();
|
||||||
|
|
||||||
boolean flushChannel();
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
package cn.iocoder.dashboard.modules.system.service.sms;
|
package cn.iocoder.dashboard.modules.system.service.sms;
|
||||||
|
|
||||||
|
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 java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信渠道Service接口
|
* 短信渠道Service接口
|
||||||
*
|
*
|
||||||
@ -7,4 +13,23 @@ package cn.iocoder.dashboard.modules.system.service.sms;
|
|||||||
* @date 2021/1/25 9:24
|
* @date 2021/1/25 9:24
|
||||||
*/
|
*/
|
||||||
public interface SmsLogService {
|
public interface SmsLogService {
|
||||||
|
/**
|
||||||
|
* 发送短信前的日志处理
|
||||||
|
*
|
||||||
|
* @param smsBody 短信内容
|
||||||
|
* @param targetPhones 发送对象手机号集合
|
||||||
|
* @param client 短信客户端
|
||||||
|
* @param isAsync 是否异步发送
|
||||||
|
* @return 生成的日志id
|
||||||
|
*/
|
||||||
|
Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient<?> client, Boolean isAsync);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息后的日志处理
|
||||||
|
*
|
||||||
|
* @param logId 日志id
|
||||||
|
* @param result 消息结果
|
||||||
|
*/
|
||||||
|
void afterSendLog(Long logId, SmsResult<?> result);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,7 +3,9 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
|
import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
|
||||||
import cn.iocoder.dashboard.common.pojo.PageResult;
|
import cn.iocoder.dashboard.common.pojo.PageResult;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
|
import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
|
||||||
|
import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory;
|
||||||
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
|
||||||
import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
|
||||||
@ -14,12 +16,16 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsTemplateMapper;
|
|||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO;
|
||||||
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsTemplateDO;
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsTemplateDO;
|
||||||
import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
|
import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.Resource;
|
import javax.annotation.Resource;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信渠道Service实现类
|
* 短信渠道Service实现类
|
||||||
@ -30,6 +36,30 @@ import java.util.List;
|
|||||||
@Service
|
@Service
|
||||||
public class SmsChannelServiceImpl implements SmsChannelService {
|
public class SmsChannelServiceImpl implements SmsChannelService {
|
||||||
|
|
||||||
|
private final Map<String, Long> templateCode2ChannelIdMap = new ConcurrentHashMap<>(32);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SmsClientFactory smsClientFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化短信客户端
|
||||||
|
*/
|
||||||
|
@PostConstruct
|
||||||
|
@Override
|
||||||
|
public void initSmsClient() {
|
||||||
|
List<SmsChannelPropertyVO> smsChannelPropertyVOList = listChannelAllEnabledInfo();
|
||||||
|
if (ObjectUtil.isEmpty(smsChannelPropertyVOList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
smsChannelPropertyVOList.forEach(smsChannelPropertyVO -> {
|
||||||
|
Long clientId = smsClientFactory.createClient(smsChannelPropertyVO);
|
||||||
|
smsChannelPropertyVO.getTemplateList().forEach(smsTemplateVO -> {
|
||||||
|
templateCode2ChannelIdMap.put(smsTemplateVO.getCode(), clientId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Resource
|
@Resource
|
||||||
private SmsChannelMapper mapper;
|
private SmsChannelMapper mapper;
|
||||||
|
|
||||||
@ -54,12 +84,17 @@ public class SmsChannelServiceImpl implements SmsChannelService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SmsChannelAllVO> listChannelAllEnabledInfo() {
|
public AbstractSmsClient<?> getClient(String templateCode) {
|
||||||
|
return smsClientFactory.getClient(templateCode2ChannelIdMap.get(templateCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<SmsChannelPropertyVO> listChannelAllEnabledInfo() {
|
||||||
List<SmsChannelDO> channelDOList = mapper.selectEnabledList();
|
List<SmsChannelDO> channelDOList = mapper.selectEnabledList();
|
||||||
if (ObjectUtil.isNull(channelDOList)) {
|
if (ObjectUtil.isNull(channelDOList)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List<SmsChannelAllVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList);
|
List<SmsChannelPropertyVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList);
|
||||||
|
|
||||||
channelAllVOList.forEach(smsChannelDO -> {
|
channelAllVOList.forEach(smsChannelDO -> {
|
||||||
|
|
||||||
@ -71,10 +106,4 @@ public class SmsChannelServiceImpl implements SmsChannelService {
|
|||||||
});
|
});
|
||||||
return channelAllVOList;
|
return channelAllVOList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean flushChannel() {
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,21 @@
|
|||||||
package cn.iocoder.dashboard.modules.system.service.sms.impl;
|
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.controller.sms.vo.SmsChannelPropertyVO;
|
||||||
|
import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO;
|
||||||
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsLogMapper;
|
||||||
|
import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLogDO;
|
||||||
|
import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum;
|
||||||
import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService;
|
import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService;
|
||||||
|
import cn.iocoder.dashboard.util.json.JsonUtils;
|
||||||
|
import cn.iocoder.dashboard.util.string.StrUtils;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 短信日志Service实现类
|
* 短信日志Service实现类
|
||||||
*
|
*
|
||||||
@ -12,4 +25,48 @@ import org.springframework.stereotype.Service;
|
|||||||
@Service
|
@Service
|
||||||
public class SmsLogServiceImpl implements SmsLogService {
|
public class SmsLogServiceImpl implements SmsLogService {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SmsLogMapper smsLogMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient<?> client, Boolean isAsync) {
|
||||||
|
SmsLogDO smsLog = new SmsLogDO();
|
||||||
|
if (smsBody.getSmsLogId() != null) {
|
||||||
|
smsLog.setId(smsBody.getSmsLogId());
|
||||||
|
smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus());
|
||||||
|
smsLogMapper.updateById(smsLog);
|
||||||
|
return smsBody.getSmsLogId();
|
||||||
|
} else {
|
||||||
|
SmsChannelPropertyVO property = client.getProperty();
|
||||||
|
SmsTemplateVO smsTemplate = property.getTemplateByTemplateCode(smsBody.getTemplateCode());
|
||||||
|
|
||||||
|
smsLog.setChannelCode(property.getCode())
|
||||||
|
.setChannelId(property.getId())
|
||||||
|
.setTemplateCode(smsTemplate.getCode())
|
||||||
|
.setPhones(JsonUtils.toJsonString(targetPhones))
|
||||||
|
.setContent(StrUtils.replace(smsTemplate.getContent(), smsBody.getParams()));
|
||||||
|
|
||||||
|
if (isAsync) {
|
||||||
|
smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus());
|
||||||
|
} else {
|
||||||
|
smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus());
|
||||||
|
}
|
||||||
|
smsLogMapper.insert(smsLog);
|
||||||
|
return smsLog.getId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterSendLog(Long logId, SmsResult<?> result) {
|
||||||
|
SmsLogDO smsLog = new SmsLogDO();
|
||||||
|
smsLog.setId(logId);
|
||||||
|
if (result.getSuccess()) {
|
||||||
|
smsLog.setSendStatus(SmsSendStatusEnum.SUCCESS.getStatus());
|
||||||
|
} else {
|
||||||
|
smsLog.setSendStatus(SmsSendStatusEnum.FAIL.getStatus());
|
||||||
|
smsLog.setRemark(result.getMessage() + JsonUtils.toJsonString(result.getResult()));
|
||||||
|
}
|
||||||
|
smsLogMapper.updateById(smsLog);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,10 @@
|
|||||||
package cn.iocoder.dashboard.util.string;
|
package cn.iocoder.dashboard.util.string;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 字符串工具类
|
* 字符串工具类
|
||||||
*
|
*
|
||||||
@ -13,4 +16,22 @@ public class StrUtils {
|
|||||||
return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
|
return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定字符串的
|
||||||
|
* @param str
|
||||||
|
* @param replaceMap
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String replace(String str, Map<String, String> replaceMap) {
|
||||||
|
assert StrUtil.isNotBlank(str);
|
||||||
|
if (ObjectUtil.isEmpty(replaceMap)) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
String result = null;
|
||||||
|
for (String key : replaceMap.keySet()) {
|
||||||
|
result = str.replace(key, replaceMap.get(key));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user