Commit 009f33210680bb77a727636732ce23ab5c8c5fb5

Authored by zengzefeng
1 parent 37c39365

重构短信功能

Showing 30 changed files with 642 additions and 439 deletions
sql/ruoyi-vue-pro.sql
@@ -884,75 +884,4 @@ INSERT INTO `sys_user_role` VALUES (5, 100, 1, '', NULL, '', NULL, b'0'); @@ -884,75 +884,4 @@ INSERT INTO `sys_user_role` VALUES (5, 100, 1, '', NULL, '', NULL, b'0');
884 INSERT INTO `sys_user_role` VALUES (6, 100, 2, '', NULL, '', NULL, b'0'); 884 INSERT INTO `sys_user_role` VALUES (6, 100, 2, '', NULL, '', NULL, b'0');
885 COMMIT; 885 COMMIT;
886 886
887 -  
888 --- ----------------------------  
889 --- Table structure for sms_channel  
890 --- ----------------------------  
891 -DROP TABLE IF EXISTS `sms_channel`;  
892 -CREATE TABLE `sms_channel` (  
893 - `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',  
894 - `code` varchar(50) not null COMMENT '编码(来自枚举类 阿里、华为、七牛等)',  
895 - `api_key` varchar(100) NOT NULL COMMENT '账号id',  
896 - `api_secret` varchar(100) NOT NULL COMMENT '账号秘钥',  
897 - `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识',  
898 - `name` varchar(50) not null COMMENT '名称',  
899 - `signature` varchar(50) not null COMMENT '签名值',  
900 - `remark` varchar(200) NOT NULL COMMENT '备注',  
901 -  
902 - `status` tinyint(4) NOT NULL default 0 COMMENT '启用状态(0正常 1停用)',  
903 - `create_by` varchar(64) not null DEFAULT '' COMMENT '创建者',  
904 - `create_time` datetime DEFAULT NULL COMMENT '创建时间',  
905 - `update_by` varchar(64) DEFAULT '' COMMENT '更新者',  
906 - `update_time` datetime DEFAULT NULL COMMENT '更新时间',  
907 - `deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',  
908 - PRIMARY KEY (`id`) USING BTREE  
909 -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信渠道';  
910 -/*  
911 - 优先级值一样时,按照id顺序取值  
912 -*/  
913 -  
914 --- ----------------------------  
915 --- Table structure for sms_template  
916 --- ----------------------------  
917 -DROP TABLE IF EXISTS `sms_template`;  
918 -CREATE TABLE `sms_template` (  
919 - `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',  
920 - `channel_code` varchar(50) not null COMMENT '短信渠道编码(来自枚举类)',  
921 - `channel_id` bigint(20) not null COMMENT '短信渠道id (对于前端来说就是绑定一个签名)',  
922 - `type` tinyint(4) NOT NULL default 1 COMMENT '消息类型 [0验证码 1短信通知 2推广短信 3国际/港澳台消息]',  
923 - `biz_code` varchar(50) not null COMMENT '业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)',  
924 - `code` varchar(50) not null COMMENT '编码',  
925 - `name` varchar(50) not null COMMENT '名称',  
926 - `api_template_id` varchar(100) NOT NULL COMMENT '实际渠道模板唯一标识',  
927 - `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',  
928 - `params` varchar(200) NOT NULL DEFAULT '' COMMENT '参数数组(自动根据内容生成)',  
929 - `remark` varchar(200) NOT NULL COMMENT '备注',  
930 -  
931 - `status` tinyint(4) NOT NULL default 0 COMMENT '启用状态(0正常 1停用)',  
932 - `create_by` varchar(64) not null DEFAULT '' COMMENT '创建者',  
933 - `create_time` datetime DEFAULT NULL COMMENT '创建时间',  
934 - `update_by` varchar(64) DEFAULT '' COMMENT '更新者',  
935 - `update_time` datetime DEFAULT NULL COMMENT '更新时间',  
936 - `deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',  
937 - PRIMARY KEY (`id`) USING BTREE  
938 -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信模板';  
939 -  
940 --- ----------------------------  
941 --- Table structure for sms_log  
942 --- ----------------------------  
943 -DROP TABLE IF EXISTS `sms_log`;  
944 -CREATE TABLE `sms_log` (  
945 - `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',  
946 - `channel_code` varchar(50) not null COMMENT '短信渠道编码(来自枚举类)',  
947 - `api_sms_id` varchar(50) not null COMMENT '实际渠道短信唯一标识',  
948 - `template_id` bigint(20) NOT NULL COMMENT '模板id',  
949 - `phone` char(11) not null COMMENT '手机号',  
950 - `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',  
951 - `remark` varchar(200) NOT NULL COMMENT '备注',  
952 - `send_status` tinyint(4) NOT NULL default 0 COMMENT '发送状态(0发送中 1成功 2失败)',  
953 - `create_by` varchar(64) not null DEFAULT '' COMMENT '创建者',  
954 - `create_time` datetime DEFAULT NULL COMMENT '创建时间',  
955 - PRIMARY KEY (`id`) USING BTREE  
956 -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信日志';  
957 -  
958 SET FOREIGN_KEY_CHECKS = 1; 887 SET FOREIGN_KEY_CHECKS = 1;
sql/sms.sql 0 → 100644
  1 +/*
  2 + --2021.02.01 by fight, sms about table info
  3 +*/
  4 +
  5 +-- ----------------------------
  6 +-- Table structure for sms_channel
  7 +-- ----------------------------
  8 +DROP TABLE IF EXISTS `sms_channel`;
  9 +CREATE TABLE `sms_channel`
  10 +(
  11 + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
  12 + `code` varchar(50) NOT NULL COMMENT '编码(来自枚举类 阿里、华为、七牛等)',
  13 + `api_key` varchar(100) NOT NULL COMMENT '账号id',
  14 + `api_secret` varchar(100) NOT NULL COMMENT '账号秘钥',
  15 + `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识',
  16 + `name` varchar(50) NOT NULL COMMENT '名称',
  17 + `signature` varchar(50) NOT NULL COMMENT '签名值',
  18 + `remark` varchar(200) NOT NULL COMMENT '备注',
  19 +
  20 + `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '启用状态(0正常 1停用)',
  21 + `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者',
  22 + `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  23 + `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  24 + `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  25 + `deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',
  26 + PRIMARY KEY (`id`) USING BTREE
  27 +) ENGINE = InnoDB
  28 + AUTO_INCREMENT = 1
  29 + DEFAULT CHARSET = utf8mb4 COMMENT ='短信渠道';
  30 +/*
  31 + 优先级值一样时,按照id顺序取值
  32 +*/
  33 +
  34 +-- ----------------------------
  35 +-- Table structure for sms_template
  36 +-- ----------------------------
  37 +DROP TABLE IF EXISTS `sms_template`;
  38 +CREATE TABLE `sms_template`
  39 +(
  40 + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
  41 + `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)',
  42 + `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id (对于前端来说就是绑定一个签名)',
  43 + `type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '消息类型 [0验证码 1短信通知 2推广短信 3国际/港澳台消息]',
  44 + `biz_code` varchar(50) NOT NULL COMMENT '业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)',
  45 + `code` varchar(50) NOT NULL COMMENT '编码',
  46 + `name` varchar(50) NOT NULL COMMENT '名称',
  47 + `api_template_id` varchar(100) NOT NULL COMMENT '实际渠道模板唯一标识',
  48 + `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',
  49 + `params` varchar(200) NOT NULL DEFAULT '' COMMENT '参数数组(自动根据内容生成)',
  50 + `remark` varchar(200) NOT NULL COMMENT '备注',
  51 +
  52 + `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '启用状态(0正常 1停用)',
  53 + `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者',
  54 + `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  55 + `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
  56 + `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  57 + `deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',
  58 + PRIMARY KEY (`id`) USING BTREE
  59 +) ENGINE = InnoDB
  60 + AUTO_INCREMENT = 1
  61 + DEFAULT CHARSET = utf8mb4 COMMENT ='短信模板';
  62 +
  63 +-- ----------------------------
  64 +-- Table structure for sms_log
  65 +-- ----------------------------
  66 +DROP TABLE IF EXISTS `sms_log`;
  67 +CREATE TABLE `sms_log`
  68 +(
  69 + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
  70 + `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)',
  71 + `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id',
  72 + `template_code` varchar(50) NOT NULL COMMENT '渠道编码',
  73 + `phones` char(11) NOT NULL COMMENT '手机号(数组json字符串)',
  74 + `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',
  75 + `remark` varchar(200) DEFAULT NULL COMMENT '备注',
  76 + `send_status` tinyint(4) NOT NULL DEFAULT 2 COMMENT '发送状态(1异步推送中 2发送中 3失败 4成功)',
  77 + `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者',
  78 + `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  79 + PRIMARY KEY (`id`) USING BTREE
  80 +) ENGINE = InnoDB
  81 + AUTO_INCREMENT = 1
  82 + DEFAULT CHARSET = utf8mb4 COMMENT ='短信日志';
src/main/java/cn/iocoder/dashboard/framework/sms/SmsClientAdapter.java deleted
1 -package cn.iocoder.dashboard.framework.sms;  
2 -  
3 -import cn.hutool.core.util.ObjectUtil;  
4 -import cn.iocoder.dashboard.common.exception.ServiceException;  
5 -  
6 -import java.util.Collection;  
7 -import java.util.Collections;  
8 -import java.util.Map;  
9 -  
10 -import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.SMS_CHANNEL_NOT_INIT;  
11 -  
12 -/**  
13 - * 抽象短信客户端工厂  
14 - *  
15 - * @author zzf  
16 - * @date 2021/1/28 14:01  
17 - */  
18 -public class SmsClientAdapter {  
19 -  
20 - private final Map<Long, SmsClient<?>> smsSenderMap;  
21 -  
22 - public SmsClientAdapter(Map<Long, SmsClient<?>> smsSenderMap) {  
23 - if (ObjectUtil.isEmpty(smsSenderMap)) {  
24 - throw new ServiceException(SMS_CHANNEL_NOT_INIT);  
25 - }  
26 - this.smsSenderMap = smsSenderMap;  
27 - }  
28 -  
29 - public void flushClient(Map<Long, SmsClient<?>> smsSenderMap) {  
30 - this.smsSenderMap.clear();  
31 - smsSenderMap.putAll(Collections.unmodifiableMap(smsSenderMap));  
32 - }  
33 -  
34 - public SmsResult<?> send(Long channelId, SmsBody smsBody, Collection<String> targetPhone) {  
35 - SmsClient<?> smsClient = getSmsSender(channelId);  
36 - return smsClient.send(smsBody, targetPhone);  
37 - }  
38 -  
39 - private SmsClient<?> getSmsSender(Long channelId) {  
40 - return smsSenderMap.get(channelId);  
41 - }  
42 -}  
src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.client;
  2 +
  3 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
  4 +
  5 +/**
  6 + * 抽象短息客户端
  7 + *
  8 + * @author zzf
  9 + * @date 2021/2/1 9:28
  10 + */
  11 +public abstract class AbstractSmsClient<R> implements SmsClient<R> {
  12 +
  13 + /**
  14 + * 短信渠道参数
  15 + */
  16 + protected final SmsChannelPropertyVO channelVO;
  17 +
  18 + /**
  19 + * 构造阿里云短信发送处理
  20 + *
  21 + * @param channelVO 阿里云短信配置
  22 + */
  23 + public AbstractSmsClient(SmsChannelPropertyVO channelVO) {
  24 + this.channelVO = channelVO;
  25 + }
  26 +
  27 +
  28 + public SmsChannelPropertyVO getProperty() {
  29 + return channelVO;
  30 + }
  31 +
  32 +}
src/main/java/cn/iocoder/dashboard/modules/system/sms/client/AliSmsClient.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java
1 -package cn.iocoder.dashboard.modules.system.sms.client; 1 +package cn.iocoder.dashboard.framework.sms.client;
2 2
3 -import cn.iocoder.dashboard.framework.sms.SmsBody;  
4 -import cn.iocoder.dashboard.framework.sms.SmsClient;  
5 -import cn.iocoder.dashboard.framework.sms.SmsResult;  
6 -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; 3 +import cn.iocoder.dashboard.framework.sms.core.SmsBody;
  4 +import cn.iocoder.dashboard.framework.sms.core.SmsResult;
  5 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
7 import com.aliyuncs.DefaultAcsClient; 6 import com.aliyuncs.DefaultAcsClient;
8 import com.aliyuncs.IAcsClient; 7 import com.aliyuncs.IAcsClient;
9 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; 8 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
@@ -23,7 +22,7 @@ import java.util.Collection; @@ -23,7 +22,7 @@ import java.util.Collection;
23 * @date 2021/1/25 14:17 22 * @date 2021/1/25 14:17
24 */ 23 */
25 @Slf4j 24 @Slf4j
26 -public class AliSmsClient implements SmsClient<SendSmsResponse> { 25 +public class AliyunSmsClient extends AbstractSmsClient<SendSmsResponse> {
27 26
28 private static final String OK = "OK"; 27 private static final String OK = "OK";
29 28
@@ -33,8 +32,6 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; { @@ -33,8 +32,6 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; {
33 32
34 private static final String ENDPOINT = "cn-hangzhou"; 33 private static final String ENDPOINT = "cn-hangzhou";
35 34
36 - private final SmsChannelAllVO channelVO;  
37 -  
38 private final IAcsClient acsClient; 35 private final IAcsClient acsClient;
39 36
40 /** 37 /**
@@ -42,9 +39,8 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; { @@ -42,9 +39,8 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; {
42 * 39 *
43 * @param channelVO 阿里云短信配置 40 * @param channelVO 阿里云短信配置
44 */ 41 */
45 - public AliSmsClient(SmsChannelAllVO channelVO) {  
46 -  
47 - this.channelVO = channelVO; 42 + public AliyunSmsClient(SmsChannelPropertyVO channelVO) {
  43 + super(channelVO);
48 44
49 String accessKeyId = channelVO.getApiKey(); 45 String accessKeyId = channelVO.getApiKey();
50 String accessKeySecret = channelVO.getApiSecret(); 46 String accessKeySecret = channelVO.getApiSecret();
@@ -57,13 +53,13 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; { @@ -57,13 +53,13 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; {
57 53
58 54
59 @Override 55 @Override
60 - public SmsResult<SendSmsResponse> send(SmsBody msgBody, Collection<String> targets) { 56 + public SmsResult<SendSmsResponse> send(SmsBody smsBody, Collection<String> targets) {
61 SendSmsRequest request = new SendSmsRequest(); 57 SendSmsRequest request = new SendSmsRequest();
62 request.setSysMethod(MethodType.POST); 58 request.setSysMethod(MethodType.POST);
63 request.setPhoneNumbers(StringUtils.join(targets, ",")); 59 request.setPhoneNumbers(StringUtils.join(targets, ","));
64 request.setSignName(channelVO.getApiSignatureId()); 60 request.setSignName(channelVO.getApiSignatureId());
65 - request.setTemplateCode(channelVO.getTemplateByTemplateCode(msgBody.getCode()).getApiTemplateId());  
66 - request.setTemplateParam(msgBody.getParamsStr()); 61 + request.setTemplateCode(channelVO.getTemplateByTemplateCode(smsBody.getTemplateCode()).getApiTemplateId());
  62 + request.setTemplateParam(smsBody.getParamsStr());
67 63
68 try { 64 try {
69 SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); 65 SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
@@ -78,14 +74,15 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; { @@ -78,14 +74,15 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; {
78 return resultBody; 74 return resultBody;
79 } catch (Exception e) { 75 } catch (Exception e) {
80 log.debug(e.getMessage(), e); 76 log.debug(e.getMessage(), e);
  77 + return failResult("发送异常: " + e.getMessage());
81 } 78 }
82 -  
83 - return failResult();  
84 } 79 }
85 80
86 - @Override  
87 - public SmsResult<SendSmsResponse> sendAsync(SmsBody msgBody, Collection<String> targets) {  
88 - return null; 81 + SmsResult<SendSmsResponse> failResult(String message) {
  82 + SmsResult<SendSmsResponse> resultBody = new SmsResult<>();
  83 + resultBody.setSuccess(false);
  84 + resultBody.setMessage(message);
  85 + return resultBody;
89 } 86 }
90 87
91 } 88 }
src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.client;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.core.SmsBody;
  4 +import cn.iocoder.dashboard.framework.sms.core.SmsResult;
  5 +
  6 +import java.util.Collection;
  7 +
  8 +/**
  9 + * 短信父接口
  10 + *
  11 + * @author zzf
  12 + * @date 2021/1/25 14:14
  13 + */
  14 +public interface SmsClient<R> {
  15 +
  16 + /**
  17 + * 发送消息
  18 + *
  19 + * @param smsBody 消息内容
  20 + * @param targets 发送对象列表
  21 + * @return 是否发送成功
  22 + */
  23 + SmsResult<R> send(SmsBody smsBody, Collection<String> targets);
  24 +
  25 +}
0 \ No newline at end of file 26 \ No newline at end of file
src/main/java/cn/iocoder/dashboard/framework/sms/SmsBody.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java
1 -package cn.iocoder.dashboard.framework.sms; 1 +package cn.iocoder.dashboard.framework.sms.core;
2 2
3 import cn.iocoder.dashboard.util.json.JsonUtils; 3 import cn.iocoder.dashboard.util.json.JsonUtils;
4 import lombok.Data; 4 import lombok.Data;
5 5
6 import java.util.Map; 6 import java.util.Map;
  7 +import java.util.UUID;
7 8
8 /** 9 /**
9 * 消息内容实体类 10 * 消息内容实体类
@@ -12,9 +13,14 @@ import java.util.Map; @@ -12,9 +13,14 @@ import java.util.Map;
12 public class SmsBody { 13 public class SmsBody {
13 14
14 /** 15 /**
  16 + * 消息日志id
  17 + */
  18 + private Long smsLogId;
  19 +
  20 + /**
15 * 模板编码 21 * 模板编码
16 */ 22 */
17 - private String code; 23 + private String templateCode;
18 24
19 /** 25 /**
20 * 参数列表 26 * 参数列表
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core;
  2 +
  3 +import cn.hutool.core.util.ObjectUtil;
  4 +import cn.hutool.core.util.StrUtil;
  5 +import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
  6 +import cn.iocoder.dashboard.common.exception.ServiceException;
  7 +import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
  8 +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
  9 +import cn.iocoder.dashboard.framework.sms.client.AliyunSmsClient;
  10 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
  11 +import org.springframework.stereotype.Component;
  12 +
  13 +import java.util.Map;
  14 +import java.util.concurrent.ConcurrentHashMap;
  15 +
  16 +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
  17 +
  18 +/**
  19 + * 短信客户端工厂
  20 + *
  21 + * @author zzf
  22 + * @date 2021/1/28 14:01
  23 + */
  24 +@Component
  25 +public class SmsClientFactory {
  26 +
  27 + private final Map<Long, AbstractSmsClient<?>> smsSenderMap = new ConcurrentHashMap<>(8);
  28 +
  29 + /**
  30 + * 创建短信客户端
  31 + *
  32 + * @param propertyVO 参数对象
  33 + * @return 客户端id(默认channelId)
  34 + */
  35 + public Long createClient(SmsChannelPropertyVO propertyVO) {
  36 + if (StrUtil.isBlank(propertyVO.getCode())) {
  37 + throw ServiceExceptionUtil.exception(PARAM_VALUE_IS_NULL, "短信渠道编码");
  38 + }
  39 + if (ObjectUtil.isNull(propertyVO.getId())) {
  40 + throw ServiceExceptionUtil.exception(PARAM_VALUE_IS_NULL, "短信渠道ID");
  41 + }
  42 +
  43 + AbstractSmsClient<?> sender = createClient(SmsChannelEnum.getByCode(propertyVO.getCode()), propertyVO);
  44 + smsSenderMap.put(propertyVO.getId(), sender);
  45 + return propertyVO.getId();
  46 + }
  47 +
  48 + private AbstractSmsClient<?> createClient(SmsChannelEnum channelEnum, SmsChannelPropertyVO channelVO) {
  49 + if (channelEnum == null) {
  50 + throw new ServiceException(INVALID_CHANNEL_CODE);
  51 + }
  52 + switch (channelEnum) {
  53 + case ALI:
  54 + return new AliyunSmsClient(channelVO);
  55 + // TODO fill more channel
  56 + default:
  57 + break;
  58 + }
  59 + throw new ServiceException(SMS_SENDER_NOT_FOUND);
  60 + }
  61 +
  62 + /**
  63 + * 获取短信客户端
  64 + *
  65 + * @param channelId 渠道id
  66 + * @return 短信id
  67 + */
  68 + public AbstractSmsClient<?> getClient(Long channelId) {
  69 + return smsSenderMap.get(channelId);
  70 + }
  71 +}
src/main/java/cn/iocoder/dashboard/framework/sms/SmsResult.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java
1 -package cn.iocoder.dashboard.framework.sms; 1 +package cn.iocoder.dashboard.framework.sms.core;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 4
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsChannelController.java
@@ -44,11 +44,5 @@ public class SmsChannelController { @@ -44,11 +44,5 @@ public class SmsChannelController {
44 return success(service.createChannel(reqVO)); 44 return success(service.createChannel(reqVO));
45 } 45 }
46 46
47 - @ApiOperation("刷新消息渠道信息")  
48 - @PutMapping("/flush")  
49 - public CommonResult<Boolean> flushChannel() {  
50 - return success(service.flushChannel());  
51 - }  
52 -  
53 47
54 } 48 }
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsChannelAllVO.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsChannelPropertyVO.java
@@ -14,7 +14,7 @@ import java.util.List; @@ -14,7 +14,7 @@ import java.util.List;
14 */ 14 */
15 @Data 15 @Data
16 @EqualsAndHashCode 16 @EqualsAndHashCode
17 -public class SmsChannelAllVO implements Serializable { 17 +public class SmsChannelPropertyVO implements Serializable {
18 18
19 /** 19 /**
20 * id 20 * id
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsTemplateVO.java
@@ -17,6 +17,7 @@ public class SmsTemplateVO { @@ -17,6 +17,7 @@ public class SmsTemplateVO {
17 * 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板) 17 * 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)
18 */ 18 */
19 private String bizCode; 19 private String bizCode;
  20 +
20 /** 21 /**
21 * 编码 22 * 编码
22 */ 23 */
@@ -27,4 +28,11 @@ public class SmsTemplateVO { @@ -27,4 +28,11 @@ public class SmsTemplateVO {
27 */ 28 */
28 private String apiTemplateId; 29 private String apiTemplateId;
29 30
  31 + /**
  32 + * 内容
  33 + */
  34 + private String content;
  35 +
  36 +
  37 +
30 } 38 }
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java
@@ -2,7 +2,7 @@ package cn.iocoder.dashboard.modules.system.convert.sms; @@ -2,7 +2,7 @@ package cn.iocoder.dashboard.modules.system.convert.sms;
2 2
3 import cn.iocoder.dashboard.common.enums.SmsChannelEnum; 3 import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
4 import cn.iocoder.dashboard.common.pojo.PageResult; 4 import cn.iocoder.dashboard.common.pojo.PageResult;
5 -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; 5 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; 6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; 7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
8 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO; 8 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO;
@@ -28,7 +28,7 @@ public interface SmsChannelConvert { @@ -28,7 +28,7 @@ public interface SmsChannelConvert {
28 28
29 List<SmsChannelEnumRespVO> convertEnum(List<SmsChannelEnum> bean); 29 List<SmsChannelEnumRespVO> convertEnum(List<SmsChannelEnum> bean);
30 30
31 - List<SmsChannelAllVO> convert(List<SmsChannelDO> bean); 31 + List<SmsChannelPropertyVO> convert(List<SmsChannelDO> bean);
32 32
33 33
34 } 34 }
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SmsLogMapper.java
1 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms; 1 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms;
2 2
3 -import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLog; 3 +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLogDO;
4 import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 import org.apache.ibatis.annotations.Mapper; 5 import org.apache.ibatis.annotations.Mapper;
6 6
7 @Mapper 7 @Mapper
8 -public interface SmsLogMapper extends BaseMapper<SmsLog> { 8 +public interface SmsLogMapper extends BaseMapper<SmsLogDO> {
9 9
10 } 10 }
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SmsLog.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SmsLogDO.java
@@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms; @@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms;
3 import com.baomidou.mybatisplus.annotation.TableName; 3 import com.baomidou.mybatisplus.annotation.TableName;
4 import lombok.Data; 4 import lombok.Data;
5 import lombok.EqualsAndHashCode; 5 import lombok.EqualsAndHashCode;
  6 +import lombok.experimental.Accessors;
6 7
7 import java.io.Serializable; 8 import java.io.Serializable;
8 import java.util.Date; 9 import java.util.Date;
@@ -15,8 +16,9 @@ import java.util.Date; @@ -15,8 +16,9 @@ import java.util.Date;
15 */ 16 */
16 @Data 17 @Data
17 @EqualsAndHashCode 18 @EqualsAndHashCode
  19 +@Accessors(chain = true)
18 @TableName(value = "sms_log", autoResultMap = true) 20 @TableName(value = "sms_log", autoResultMap = true)
19 -public class SmsLog implements Serializable { 21 +public class SmsLogDO implements Serializable {
20 22
21 /** 23 /**
22 * 自增编号 24 * 自增编号
@@ -29,19 +31,19 @@ public class SmsLog implements Serializable { @@ -29,19 +31,19 @@ public class SmsLog implements Serializable {
29 private String channelCode; 31 private String channelCode;
30 32
31 /** 33 /**
32 - * 实际渠道短信唯一标识 34 + * 短信渠道id
33 */ 35 */
34 - private String apiSmsId; 36 + private Long channelId;
35 37
36 /** 38 /**
37 * 模板id 39 * 模板id
38 */ 40 */
39 - private Long templateId; 41 + private String templateCode;
40 42
41 /** 43 /**
42 - * 手机号 44 + * 手机号(数组json字符串)
43 */ 45 */
44 - private String phone; 46 + private String phones;
45 47
46 /** 48 /**
47 * 内容 49 * 内容
@@ -54,7 +56,7 @@ public class SmsLog implements Serializable { @@ -54,7 +56,7 @@ public class SmsLog implements Serializable {
54 private String remark; 56 private String remark;
55 57
56 /** 58 /**
57 - * 发送状态(0发送中 1成功 2失败) 59 + * 发送状态(1异步推送中 2发送中 3失败 4成功)
58 */ 60 */
59 private Integer sendStatus; 61 private Integer sendStatus;
60 62
src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
@@ -83,5 +83,6 @@ public interface SysErrorCodeConstants { @@ -83,5 +83,6 @@ public interface SysErrorCodeConstants {
83 ErrorCode SMS_TEMPLATE_NOT_FOUND = new ErrorCode(1003001003, "没有短信模板信息, 请初始化sms_template表数据。"); 83 ErrorCode SMS_TEMPLATE_NOT_FOUND = new ErrorCode(1003001003, "没有短信模板信息, 请初始化sms_template表数据。");
84 ErrorCode SMS_SENDER_NOT_FOUND = new ErrorCode(1003001004, "没有找到对应的短信发送对象,请检查sms_channel表和sms_template表数据"); 84 ErrorCode SMS_SENDER_NOT_FOUND = new ErrorCode(1003001004, "没有找到对应的短信发送对象,请检查sms_channel表和sms_template表数据");
85 ErrorCode INVALID_CHANNEL_CODE = new ErrorCode(1003001005, "非法的短信渠道code,请检查sms_channel表的code值是否与SmsChannelEnum中的code值一致。"); 85 ErrorCode INVALID_CHANNEL_CODE = new ErrorCode(1003001005, "非法的短信渠道code,请检查sms_channel表的code值是否与SmsChannelEnum中的code值一致。");
  86 + ErrorCode PARAM_VALUE_IS_NULL = new ErrorCode(1003001006, "参数【{}】不能为空");
86 87
87 } 88 }
src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.enums.sms;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * 短信发送状态
  8 + *
  9 + * @author zzf
  10 + * @date 2021/2/1 13:39
  11 + */
  12 +@Getter
  13 +@AllArgsConstructor
  14 +public enum SmsSendStatusEnum {
  15 +
  16 + //异步转发中
  17 + ASYNC(1),
  18 +
  19 + //发送中
  20 + SENDING(2),
  21 +
  22 + //失败
  23 + FAIL(3),
  24 +
  25 + //成功
  26 + SUCCESS(4);
  27 +
  28 + private final int status;
  29 +
  30 +}
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.mq.consumer.sms;
  2 +
  3 +import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener;
  4 +import cn.iocoder.dashboard.framework.sms.core.SmsResult;
  5 +import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage;
  6 +import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage;
  7 +import cn.iocoder.dashboard.modules.system.service.sms.SmsService;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.stereotype.Component;
  10 +
  11 +import javax.annotation.Resource;
  12 +
  13 +/**
  14 + * 针对 {@link SysDeptRefreshMessage} 的消费者
  15 + *
  16 + * @author 芋道源码
  17 + */
  18 +@Component
  19 +@Slf4j
  20 +public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> {
  21 +
  22 + @Resource
  23 + private SmsService smsService;
  24 +
  25 + @Override
  26 + public void onMessage(SmsSendMessage message) {
  27 + log.info("[onMessage][收到 发送短信 消息]");
  28 + SmsResult<?> send = smsService.send(message.getSmsBody(), message.getTargetPhones());
  29 + }
  30 +
  31 +}
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SmsSendMessage.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.mq.message.sms;
  2 +
  3 +import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage;
  4 +import cn.iocoder.dashboard.framework.sms.core.SmsBody;
  5 +import lombok.Data;
  6 +
  7 +import java.util.Collection;
  8 +import java.util.List;
  9 +
  10 +/**
  11 + * 部门数据刷新 Message
  12 + */
  13 +@Data
  14 +public class SmsSendMessage implements ChannelMessage {
  15 +
  16 + private SmsBody smsBody;
  17 +
  18 + private List<String> targetPhones;
  19 +
  20 + @Override
  21 + public String getChannel() {
  22 + return "sms.send";
  23 + }
  24 +
  25 +}
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SmsProducer.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.mq.producer.sms;
  2 +
  3 +import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils;
  4 +import cn.iocoder.dashboard.framework.sms.core.SmsBody;
  5 +import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage;
  6 +import org.springframework.data.redis.core.StringRedisTemplate;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +import javax.annotation.Resource;
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 短信的 Producer
  14 + */
  15 +@Component
  16 +public class SmsProducer {
  17 +
  18 + @Resource
  19 + private StringRedisTemplate stringRedisTemplate;
  20 +
  21 + /**
  22 + * 发送 {@link SmsSendMessage} 消息
  23 + */
  24 + public void sendSmsSendMessage(SmsBody smsBody, List<String> targetPhoneList) {
  25 + SmsSendMessage message = new SmsSendMessage();
  26 + message.setSmsBody(smsBody);
  27 + message.setTargetPhones(targetPhoneList);
  28 + RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message);
  29 + }
  30 +
  31 +}
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsChannelService.java
1 package cn.iocoder.dashboard.modules.system.service.sms; 1 package cn.iocoder.dashboard.modules.system.service.sms;
2 2
3 import cn.iocoder.dashboard.common.pojo.PageResult; 3 import cn.iocoder.dashboard.common.pojo.PageResult;
4 -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; 4 +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
  5 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
5 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; 6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; 7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; 8 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
@@ -17,18 +18,46 @@ import java.util.List; @@ -17,18 +18,46 @@ import java.util.List;
17 */ 18 */
18 public interface SmsChannelService { 19 public interface SmsChannelService {
19 20
  21 + /**
  22 + * 初始化短信渠道
  23 + */
  24 + void initSmsClient();
  25 +
  26 + /**
  27 + * 分页查询短信渠道信息
  28 + *
  29 + * @param reqVO 参数对象
  30 + * @return 短信渠道分页对象
  31 + */
20 PageResult<SmsChannelDO> pageChannels(SmsChannelPageReqVO reqVO); 32 PageResult<SmsChannelDO> pageChannels(SmsChannelPageReqVO reqVO);
21 33
  34 + /**
  35 + * 创建新的渠道信息
  36 + *
  37 + * @param reqVO 参数对象
  38 + * @return 渠道id
  39 + */
22 Long createChannel(SmsChannelCreateReqVO reqVO); 40 Long createChannel(SmsChannelCreateReqVO reqVO);
23 41
  42 + /**
  43 + * 获取短信渠道枚举/渠道编码
  44 + *
  45 + * @return 短信渠道枚举/渠道编码
  46 + */
24 List<SmsChannelEnumRespVO> getChannelEnums(); 47 List<SmsChannelEnumRespVO> getChannelEnums();
25 48
26 /** 49 /**
  50 + * 根据短信模板编码获取短信客户端
  51 + *
  52 + * @param templateCode 短信模板编码
  53 + * @return 短信客户端
  54 + */
  55 + AbstractSmsClient<?> getClient(String templateCode);
  56 +
  57 + /**
27 * 查询渠道(包含名下模块)信息集合 58 * 查询渠道(包含名下模块)信息集合
28 * 59 *
29 * @return 渠道(包含名下模块)信息集合 60 * @return 渠道(包含名下模块)信息集合
30 */ 61 */
31 - List<SmsChannelAllVO> listChannelAllEnabledInfo();  
32 -  
33 - boolean flushChannel(); 62 + List<SmsChannelPropertyVO> listChannelAllEnabledInfo();
34 } 63 }
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsLogService.java
1 package cn.iocoder.dashboard.modules.system.service.sms; 1 package cn.iocoder.dashboard.modules.system.service.sms;
2 2
  3 +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
  4 +import cn.iocoder.dashboard.framework.sms.core.SmsBody;
  5 +import cn.iocoder.dashboard.framework.sms.core.SmsResult;
  6 +
  7 +import java.util.List;
  8 +
3 /** 9 /**
4 * 短信渠道Service接口 10 * 短信渠道Service接口
5 * 11 *
@@ -7,4 +13,23 @@ package cn.iocoder.dashboard.modules.system.service.sms; @@ -7,4 +13,23 @@ package cn.iocoder.dashboard.modules.system.service.sms;
7 * @date 2021/1/25 9:24 13 * @date 2021/1/25 9:24
8 */ 14 */
9 public interface SmsLogService { 15 public interface SmsLogService {
  16 + /**
  17 + * 发送短信前的日志处理
  18 + *
  19 + * @param smsBody 短信内容
  20 + * @param targetPhones 发送对象手机号集合
  21 + * @param client 短信客户端
  22 + * @param isAsync 是否异步发送
  23 + * @return 生成的日志id
  24 + */
  25 + Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient<?> client, Boolean isAsync);
  26 +
  27 + /**
  28 + * 发送消息后的日志处理
  29 + *
  30 + * @param logId 日志id
  31 + * @param result 消息结果
  32 + */
  33 + void afterSendLog(Long logId, SmsResult<?> result);
  34 +
10 } 35 }
src/main/java/cn/iocoder/dashboard/framework/sms/SmsClient.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsService.java
1 -package cn.iocoder.dashboard.framework.sms; 1 +package cn.iocoder.dashboard.modules.system.service.sms;
2 2
  3 +import cn.iocoder.dashboard.framework.sms.core.SmsBody;
  4 +import cn.iocoder.dashboard.framework.sms.core.SmsResult;
3 import org.apache.commons.lang3.StringUtils; 5 import org.apache.commons.lang3.StringUtils;
4 6
5 import java.util.Arrays; 7 import java.util.Arrays;
6 -import java.util.Collection;  
7 import java.util.Collections; 8 import java.util.Collections;
  9 +import java.util.List;
8 10
9 /** 11 /**
10 - * 短信父接口 12 + * 短信Service接口
11 * 13 *
12 * @author zzf 14 * @author zzf
13 - * @date 2021/1/25 14:14 15 + * @date 2021/1/25 9:24
14 */ 16 */
15 -public interface SmsClient<R> { 17 +public interface SmsService {
16 18
17 /** 19 /**
18 * 发送消息 20 * 发送消息
19 * 21 *
20 - * @param msgBody 消息内容  
21 - * @param targets 发送对象列表 22 + * @param smsBody 消息内容
  23 + * @param targetPhones 发送对象手机号列表
22 * @return 是否发送成功 24 * @return 是否发送成功
23 */ 25 */
24 - SmsResult<R> send(SmsBody msgBody, Collection<String> targets); 26 + SmsResult<?> send(SmsBody smsBody, List<String> targetPhones);
25 27
26 /** 28 /**
27 * 发送消息 29 * 发送消息
28 * 30 *
29 - * @param msgBody 消息内容  
30 - * @param target 发送对象 31 + * @param smsBody 消息内容
  32 + * @param targetPhone 发送对象手机号
31 * @return 是否发送成功 33 * @return 是否发送成功
32 */ 34 */
33 - default SmsResult<R> send(SmsBody msgBody, String target) {  
34 - if (StringUtils.isBlank(target)) {  
35 - return failResult(); 35 + default SmsResult<?> send(SmsBody smsBody, String targetPhone) {
  36 + if (StringUtils.isBlank(targetPhone)) {
  37 + return failResult("targetPhone must not null.");
36 } 38 }
37 39
38 - return send(msgBody, Collections.singletonList(target)); 40 + return send(smsBody, Collections.singletonList(targetPhone));
39 } 41 }
40 42
41 /** 43 /**
42 * 发送消息 44 * 发送消息
43 * 45 *
44 - * @param msgBody 消息内容  
45 - * @param targets 发送对象列表 46 + * @param smsBody 消息内容
  47 + * @param targetPhones 发送对象手机号数组
46 * @return 是否发送成功 48 * @return 是否发送成功
47 */ 49 */
48 - default SmsResult<R> send(SmsBody msgBody, String... targets) {  
49 - if (targets == null) {  
50 - return failResult(); 50 + default SmsResult<?> send(SmsBody smsBody, String... targetPhones) {
  51 + if (targetPhones == null) {
  52 + return failResult("targetPhones must not null.");
51 } 53 }
52 54
53 - return send(msgBody, Arrays.asList(targets)); 55 + return send(smsBody, Arrays.asList(targetPhones));
54 } 56 }
55 57
56 58
57 /** 59 /**
58 * 异步发送消息 60 * 异步发送消息
59 * 61 *
60 - * @param msgBody 消息内容  
61 - * @param targets 发送对象列表  
62 - * @return 是否发送成功 62 + * @param msgBody 消息内容
  63 + * @param targetPhones 发送对象列表
63 */ 64 */
64 - SmsResult<R> sendAsync(SmsBody msgBody, Collection<String> targets); 65 + void sendAsync(SmsBody msgBody, List<String> targetPhones);
65 66
66 /** 67 /**
67 * 异步发送消息 68 * 异步发送消息
68 * 69 *
69 - * @param msgBody 消息内容  
70 - * @param target 发送对象  
71 - * @return 是否发送成功 70 + * @param msgBody 消息内容
  71 + * @param targetPhone 发送对象
72 */ 72 */
73 - default SmsResult<R> sendAsync(SmsBody msgBody, String target) {  
74 - if (StringUtils.isBlank(target)) {  
75 - return failResult("target must not null."); 73 + default void sendAsync(SmsBody msgBody, String targetPhone) {
  74 + if (StringUtils.isBlank(targetPhone)) {
  75 + return;
76 } 76 }
77 -  
78 - return sendAsync(msgBody, Collections.singletonList(target)); 77 + sendAsync(msgBody, Collections.singletonList(targetPhone));
79 } 78 }
80 79
81 /** 80 /**
82 * 异步发送消息 81 * 异步发送消息
83 * 82 *
84 - * @param msgBody 消息内容  
85 - * @param targets 发送对象列表  
86 - * @return 是否发送成功 83 + * @param msgBody 消息内容
  84 + * @param targetPhones 发送对象列表
87 */ 85 */
88 - default SmsResult<R> sendAsync(SmsBody msgBody, String... targets) {  
89 - if (targets == null) {  
90 - return failResult("targets must not null."); 86 + default void sendAsync(SmsBody msgBody, String... targetPhones) {
  87 + if (targetPhones == null) {
  88 + return;
91 } 89 }
92 -  
93 - return sendAsync(msgBody, Arrays.asList(targets)); 90 + sendAsync(msgBody, Arrays.asList(targetPhones));
94 } 91 }
95 92
96 - default SmsResult<R> failResult() {  
97 - SmsResult<R> resultBody = new SmsResult<>();  
98 - resultBody.setSuccess(false);  
99 - return resultBody;  
100 - }  
101 93
102 - default SmsResult<R> failResult(String message) {  
103 - SmsResult<R> resultBody = failResult(); 94 + default SmsResult<?> failResult(String message) {
  95 + SmsResult<?> resultBody = new SmsResult<>();
  96 + resultBody.setSuccess(false);
104 resultBody.setMessage(message); 97 resultBody.setMessage(message);
105 return resultBody; 98 return resultBody;
106 } 99 }
107 -}  
108 \ No newline at end of file 100 \ No newline at end of file
  101 +
  102 +}
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsChannelServiceImpl.java
@@ -3,7 +3,9 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl; @@ -3,7 +3,9 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl;
3 import cn.hutool.core.util.ObjectUtil; 3 import cn.hutool.core.util.ObjectUtil;
4 import cn.iocoder.dashboard.common.enums.SmsChannelEnum; 4 import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
5 import cn.iocoder.dashboard.common.pojo.PageResult; 5 import cn.iocoder.dashboard.common.pojo.PageResult;
6 -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; 6 +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
  7 +import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory;
  8 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; 9 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
8 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; 10 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
9 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; 11 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; @@ -14,12 +16,16 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsTemplateMapper;
14 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO; 16 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO;
15 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsTemplateDO; 17 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsTemplateDO;
16 import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService; 18 import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
  19 +import org.springframework.beans.factory.annotation.Autowired;
17 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
18 21
  22 +import javax.annotation.PostConstruct;
19 import javax.annotation.Resource; 23 import javax.annotation.Resource;
20 import java.util.ArrayList; 24 import java.util.ArrayList;
21 import java.util.Arrays; 25 import java.util.Arrays;
22 import java.util.List; 26 import java.util.List;
  27 +import java.util.Map;
  28 +import java.util.concurrent.ConcurrentHashMap;
23 29
24 /** 30 /**
25 * 短信渠道Service实现类 31 * 短信渠道Service实现类
@@ -30,6 +36,30 @@ import java.util.List; @@ -30,6 +36,30 @@ import java.util.List;
30 @Service 36 @Service
31 public class SmsChannelServiceImpl implements SmsChannelService { 37 public class SmsChannelServiceImpl implements SmsChannelService {
32 38
  39 + private final Map<String, Long> templateCode2ChannelIdMap = new ConcurrentHashMap<>(32);
  40 +
  41 + @Autowired
  42 + private SmsClientFactory smsClientFactory;
  43 +
  44 + /**
  45 + * 初始化短信客户端
  46 + */
  47 + @PostConstruct
  48 + @Override
  49 + public void initSmsClient() {
  50 + List<SmsChannelPropertyVO> smsChannelPropertyVOList = listChannelAllEnabledInfo();
  51 + if (ObjectUtil.isEmpty(smsChannelPropertyVOList)) {
  52 + return;
  53 + }
  54 + smsChannelPropertyVOList.forEach(smsChannelPropertyVO -> {
  55 + Long clientId = smsClientFactory.createClient(smsChannelPropertyVO);
  56 + smsChannelPropertyVO.getTemplateList().forEach(smsTemplateVO -> {
  57 + templateCode2ChannelIdMap.put(smsTemplateVO.getCode(), clientId);
  58 + });
  59 + });
  60 + }
  61 +
  62 +
33 @Resource 63 @Resource
34 private SmsChannelMapper mapper; 64 private SmsChannelMapper mapper;
35 65
@@ -54,12 +84,17 @@ public class SmsChannelServiceImpl implements SmsChannelService { @@ -54,12 +84,17 @@ public class SmsChannelServiceImpl implements SmsChannelService {
54 } 84 }
55 85
56 @Override 86 @Override
57 - public List<SmsChannelAllVO> listChannelAllEnabledInfo() { 87 + public AbstractSmsClient<?> getClient(String templateCode) {
  88 + return smsClientFactory.getClient(templateCode2ChannelIdMap.get(templateCode));
  89 + }
  90 +
  91 + @Override
  92 + public List<SmsChannelPropertyVO> listChannelAllEnabledInfo() {
58 List<SmsChannelDO> channelDOList = mapper.selectEnabledList(); 93 List<SmsChannelDO> channelDOList = mapper.selectEnabledList();
59 if (ObjectUtil.isNull(channelDOList)) { 94 if (ObjectUtil.isNull(channelDOList)) {
60 return null; 95 return null;
61 } 96 }
62 - List<SmsChannelAllVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList); 97 + List<SmsChannelPropertyVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList);
63 98
64 channelAllVOList.forEach(smsChannelDO -> { 99 channelAllVOList.forEach(smsChannelDO -> {
65 100
@@ -71,10 +106,4 @@ public class SmsChannelServiceImpl implements SmsChannelService { @@ -71,10 +106,4 @@ public class SmsChannelServiceImpl implements SmsChannelService {
71 }); 106 });
72 return channelAllVOList; 107 return channelAllVOList;
73 } 108 }
74 -  
75 - @Override  
76 - public boolean flushChannel() {  
77 -  
78 - return true;  
79 - }  
80 } 109 }
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsLogServiceImpl.java
1 package cn.iocoder.dashboard.modules.system.service.sms.impl; 1 package cn.iocoder.dashboard.modules.system.service.sms.impl;
2 2
  3 +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
  4 +import cn.iocoder.dashboard.framework.sms.core.SmsBody;
  5 +import cn.iocoder.dashboard.framework.sms.core.SmsResult;
  6 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
  7 +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO;
  8 +import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsLogMapper;
  9 +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLogDO;
  10 +import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum;
3 import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService; 11 import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService;
  12 +import cn.iocoder.dashboard.util.json.JsonUtils;
  13 +import cn.iocoder.dashboard.util.string.StrUtils;
4 import org.springframework.stereotype.Service; 14 import org.springframework.stereotype.Service;
5 15
  16 +import javax.annotation.Resource;
  17 +import java.util.List;
  18 +
6 /** 19 /**
7 * 短信日志Service实现类 20 * 短信日志Service实现类
8 * 21 *
@@ -12,4 +25,48 @@ import org.springframework.stereotype.Service; @@ -12,4 +25,48 @@ import org.springframework.stereotype.Service;
12 @Service 25 @Service
13 public class SmsLogServiceImpl implements SmsLogService { 26 public class SmsLogServiceImpl implements SmsLogService {
14 27
  28 + @Resource
  29 + private SmsLogMapper smsLogMapper;
  30 +
  31 + @Override
  32 + public Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient<?> client, Boolean isAsync) {
  33 + SmsLogDO smsLog = new SmsLogDO();
  34 + if (smsBody.getSmsLogId() != null) {
  35 + smsLog.setId(smsBody.getSmsLogId());
  36 + smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus());
  37 + smsLogMapper.updateById(smsLog);
  38 + return smsBody.getSmsLogId();
  39 + } else {
  40 + SmsChannelPropertyVO property = client.getProperty();
  41 + SmsTemplateVO smsTemplate = property.getTemplateByTemplateCode(smsBody.getTemplateCode());
  42 +
  43 + smsLog.setChannelCode(property.getCode())
  44 + .setChannelId(property.getId())
  45 + .setTemplateCode(smsTemplate.getCode())
  46 + .setPhones(JsonUtils.toJsonString(targetPhones))
  47 + .setContent(StrUtils.replace(smsTemplate.getContent(), smsBody.getParams()));
  48 +
  49 + if (isAsync) {
  50 + smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus());
  51 + } else {
  52 + smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus());
  53 + }
  54 + smsLogMapper.insert(smsLog);
  55 + return smsLog.getId();
  56 + }
  57 + }
  58 +
  59 + @Override
  60 + public void afterSendLog(Long logId, SmsResult<?> result) {
  61 + SmsLogDO smsLog = new SmsLogDO();
  62 + smsLog.setId(logId);
  63 + if (result.getSuccess()) {
  64 + smsLog.setSendStatus(SmsSendStatusEnum.SUCCESS.getStatus());
  65 + } else {
  66 + smsLog.setSendStatus(SmsSendStatusEnum.FAIL.getStatus());
  67 + smsLog.setRemark(result.getMessage() + JsonUtils.toJsonString(result.getResult()));
  68 + }
  69 + smsLogMapper.updateById(smsLog);
  70 + }
  71 +
15 } 72 }
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsServiceImpl.java 0 → 100644
  1 +package cn.iocoder.dashboard.modules.system.service.sms.impl;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
  4 +import cn.iocoder.dashboard.framework.sms.core.SmsBody;
  5 +import cn.iocoder.dashboard.framework.sms.core.SmsResult;
  6 +import cn.iocoder.dashboard.modules.system.mq.producer.sms.SmsProducer;
  7 +import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
  8 +import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService;
  9 +import cn.iocoder.dashboard.modules.system.service.sms.SmsService;
  10 +import org.springframework.stereotype.Service;
  11 +
  12 +import javax.annotation.Resource;
  13 +import java.util.List;
  14 +
  15 +/**
  16 + * 短信日志Service实现类
  17 + *
  18 + * @author zzf
  19 + * @date 2021/1/25 9:25
  20 + */
  21 +@Service
  22 +public class SmsServiceImpl implements SmsService {
  23 +
  24 + @Resource
  25 + private SmsChannelService channelService;
  26 +
  27 + @Resource
  28 + private SmsLogService smsLogService;
  29 +
  30 + @Resource
  31 + private SmsProducer smsProducer;
  32 +
  33 + @Override
  34 + public SmsResult<?> send(SmsBody smsBody, List<String> targetPhones) {
  35 + AbstractSmsClient<?> client = channelService.getClient(smsBody.getTemplateCode());
  36 + Long logId = smsLogService.beforeSendLog(smsBody, targetPhones, client, false);
  37 +
  38 + SmsResult<?> result = client.send(smsBody, targetPhones);
  39 +
  40 + smsLogService.afterSendLog(logId, result);
  41 +
  42 + return result;
  43 + }
  44 +
  45 + @Override
  46 + public void sendAsync(SmsBody smsBody, List<String> targetPhones) {
  47 + AbstractSmsClient<?> client = channelService.getClient(smsBody.getTemplateCode());
  48 + smsLogService.beforeSendLog(smsBody, targetPhones, client, true);
  49 + smsProducer.sendSmsSendMessage(smsBody, targetPhones);
  50 + }
  51 +}
src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsConfiguration.java deleted
1 -package cn.iocoder.dashboard.modules.system.sms;  
2 -  
3 -import cn.iocoder.dashboard.framework.sms.SmsClient;  
4 -import cn.iocoder.dashboard.framework.sms.SmsClientAdapter;  
5 -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;  
6 -import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;  
7 -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;  
8 -import org.springframework.context.annotation.Bean;  
9 -import org.springframework.context.annotation.Configuration;  
10 -  
11 -import javax.annotation.Resource;  
12 -import java.util.List;  
13 -import java.util.Map;  
14 -  
15 -/**  
16 - * 短信服务配置  
17 - *  
18 - * @author guer  
19 - */  
20 -@Configuration  
21 -@ConditionalOnProperty("sms.enabled")  
22 -public class SmsConfiguration {  
23 -  
24 - @Resource  
25 - private SmsChannelService channelService;  
26 -  
27 - @Bean  
28 - public SmsClientAdapter smsClientWrapper() {  
29 - List<SmsChannelAllVO> smsChannelAllVOList = channelService.listChannelAllEnabledInfo();  
30 - Map<Long, SmsClient<?>> channelId2SmsClientMap = SmsSenderUtils.init(smsChannelAllVOList);  
31 - return new SmsClientAdapter(channelId2SmsClientMap);  
32 - }  
33 -  
34 -}  
src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsSenderUtils.java deleted
1 -package cn.iocoder.dashboard.modules.system.sms;  
2 -  
3 -import cn.hutool.core.util.ObjectUtil;  
4 -import cn.iocoder.dashboard.common.enums.SmsChannelEnum;  
5 -import cn.iocoder.dashboard.common.exception.ServiceException;  
6 -import cn.iocoder.dashboard.framework.sms.SmsBody;  
7 -import cn.iocoder.dashboard.framework.sms.SmsClient;  
8 -import cn.iocoder.dashboard.framework.sms.SmsClientAdapter;  
9 -import cn.iocoder.dashboard.framework.sms.SmsResult;  
10 -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;  
11 -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO;  
12 -import cn.iocoder.dashboard.modules.system.sms.client.AliSmsClient;  
13 -import cn.iocoder.dashboard.modules.system.sms.proxy.SmsClientLogProxy;  
14 -  
15 -import java.util.*;  
16 -import java.util.concurrent.ConcurrentHashMap;  
17 -  
18 -import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;  
19 -  
20 -/**  
21 - * 短信发送者工厂  
22 - *  
23 - * @author zzf  
24 - * @date 2021/1/25 16:18  
25 - */  
26 -public class SmsSenderUtils {  
27 -  
28 - /**  
29 - * 短信渠道id:短信客户端map  
30 - * key: channelId  
31 - * val: SmsClient  
32 - */  
33 - private static final Map<Long, SmsClient<?>> smsSenderMap = new ConcurrentHashMap<>(8);  
34 -  
35 - /**  
36 - * 短信模板code: 短信渠道id map  
37 - * key: templateCode  
38 - * val: channelId  
39 - */  
40 - private static final Map<String, Long> templateCode2ChannelIdMap = new HashMap<>();  
41 -  
42 - /**  
43 - * 将短信渠道信息初始化成短信客户端  
44 - *  
45 - * @param smsChannelAllVOList 短信渠道信息  
46 - * @return 短信渠道id:短信客户端map  
47 - */  
48 - public synchronized static Map<Long, SmsClient<?>> init(List<SmsChannelAllVO> smsChannelAllVOList) {  
49 - if (ObjectUtil.isEmpty(smsChannelAllVOList)) {  
50 - throw new ServiceException(SMS_CHANNEL_NOT_FOUND);  
51 - }  
52 - addSender(smsChannelAllVOList);  
53 - return smsSenderMap;  
54 - }  
55 -  
56 - /**  
57 - * 重置短信客户端信息  
58 - *  
59 - * @param smsClientAdapter 短信客户端适配器  
60 - * @param smsChannelAllVOList 短信渠道信息集合  
61 - */  
62 - public synchronized static void flush(SmsClientAdapter smsClientAdapter, List<SmsChannelAllVO> smsChannelAllVOList) {  
63 - smsSenderMap.clear();  
64 - smsClientAdapter.flushClient(init(smsChannelAllVOList));  
65 - }  
66 -  
67 - /**  
68 - * 发送短信  
69 - *  
70 - * @param smsClientAdapter 短信客户端适配器  
71 - * @param smsBody 短信内容  
72 - * @param targetPhones 对象手机集合  
73 - * @return 短信发送结果  
74 - */  
75 - public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, Collection<String> targetPhones) {  
76 - Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode());  
77 - if (channelId == null) {  
78 - throw new ServiceException(SMS_SENDER_NOT_FOUND);  
79 - }  
80 - return smsClientAdapter.send(channelId, smsBody, targetPhones);  
81 - }  
82 -  
83 - /**  
84 - * 发送短信  
85 - *  
86 - * @param smsClientAdapter 短信客户端适配器  
87 - * @param smsBody 短信内容  
88 - * @param targetPhone 对象手机  
89 - * @return 短信发送结果  
90 - */  
91 - public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, String targetPhone) {  
92 - Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode());  
93 - if (channelId == null) {  
94 - throw new ServiceException(SMS_SENDER_NOT_FOUND);  
95 - }  
96 - return smsClientAdapter.send(channelId, smsBody, Collections.singletonList(targetPhone));  
97 - }  
98 -  
99 - /**  
100 - * 发送短信  
101 - *  
102 - * @param smsClientAdapter 短信客户端适配器  
103 - * @param smsBody 短信内容  
104 - * @param targetPhones 对象手机数组  
105 - * @return 短信发送结果  
106 - */  
107 - public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, String... targetPhones) {  
108 - Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode());  
109 - if (channelId == null) {  
110 - throw new ServiceException(SMS_SENDER_NOT_FOUND);  
111 - }  
112 - return smsClientAdapter.send(channelId, smsBody, Arrays.asList(targetPhones));  
113 - }  
114 -  
115 -  
116 - private static void addSender(List<SmsChannelAllVO> smsChannelAllVOList) {  
117 - smsChannelAllVOList.forEach(channelAllVO -> addSender(SmsChannelEnum.getByCode(channelAllVO.getCode()), channelAllVO));  
118 - }  
119 -  
120 - private static void addSender(SmsChannelEnum channelEnum, SmsChannelAllVO channelAllVO) {  
121 - if (channelEnum == null) {  
122 - throw new ServiceException(INVALID_CHANNEL_CODE);  
123 - }  
124 - List<SmsTemplateVO> templateList = channelAllVO.getTemplateList();  
125 - if (ObjectUtil.isEmpty(templateList)) {  
126 - throw new ServiceException(SMS_TEMPLATE_NOT_FOUND);  
127 - }  
128 - SmsClient<?> aliSmsClient = getSender(channelEnum, channelAllVO);  
129 - templateList.forEach(smsTemplateVO -> templateCode2ChannelIdMap.put(smsTemplateVO.getCode(), channelAllVO.getId()));  
130 - smsSenderMap.put(channelAllVO.getId(), aliSmsClient);  
131 - }  
132 -  
133 - private static SmsClient<?> getSender(SmsChannelEnum channelEnum, SmsChannelAllVO channelAllVO) {  
134 - switch (channelEnum) {  
135 - case ALI:  
136 - return new SmsClientLogProxy<>(new AliSmsClient(channelAllVO));  
137 - // TODO fill more channel  
138 - default:  
139 - break;  
140 - }  
141 - throw new ServiceException(SMS_SENDER_NOT_FOUND);  
142 - }  
143 -}  
src/main/java/cn/iocoder/dashboard/modules/system/sms/proxy/SmsClientLogProxy.java deleted
1 -package cn.iocoder.dashboard.modules.system.sms.proxy;  
2 -  
3 -import cn.iocoder.dashboard.framework.sms.SmsBody;  
4 -import cn.iocoder.dashboard.framework.sms.SmsClient;  
5 -import cn.iocoder.dashboard.framework.sms.SmsResult;  
6 -import cn.iocoder.dashboard.util.json.JsonUtils;  
7 -import lombok.extern.slf4j.Slf4j;  
8 -  
9 -import java.util.Collection;  
10 -  
11 -/**  
12 - * 消息父接口  
13 - *  
14 - * @author zzf  
15 - * @date 2021/1/22 15:46  
16 - */  
17 -@Slf4j  
18 -public class SmsClientLogProxy<R> implements SmsClient<R> {  
19 -  
20 - private final SmsClient<R> smsClient;  
21 -  
22 - @Override  
23 - public SmsResult<R> send(SmsBody msgBody, Collection<String> targets) {  
24 - log.debug("ready send sms, body: {}, target: {}", JsonUtils.toJsonString(msgBody), targets);  
25 -  
26 - SmsResult<R> resultBody = smsClient.send(msgBody, targets);  
27 -  
28 - if (resultBody.getSuccess()) {  
29 - //  
30 - } else {  
31 - log.warn("send sms fail, body: {}, target: {}, resultBody: {}",  
32 - JsonUtils.toJsonString(msgBody),  
33 - targets,  
34 - JsonUtils.toJsonString(resultBody)  
35 - );  
36 - }  
37 - return resultBody;  
38 - }  
39 -  
40 - @Override  
41 - public SmsResult<R> sendAsync(SmsBody msgBody, Collection<String> targets) {  
42 - return send(msgBody, targets);  
43 - }  
44 -  
45 - public SmsClientLogProxy(SmsClient<R> smsClient) {  
46 - this.smsClient = smsClient;  
47 - }  
48 -}  
src/main/java/cn/iocoder/dashboard/util/string/StrUtils.java
1 package cn.iocoder.dashboard.util.string; 1 package cn.iocoder.dashboard.util.string;
2 2
  3 +import cn.hutool.core.util.ObjectUtil;
3 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
4 5
  6 +import java.util.Map;
  7 +
5 /** 8 /**
6 * 字符串工具类 9 * 字符串工具类
7 * 10 *
@@ -13,4 +16,22 @@ public class StrUtils { @@ -13,4 +16,22 @@ public class StrUtils {
13 return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好 16 return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
14 } 17 }
15 18
  19 + /**
  20 + * 指定字符串的
  21 + * @param str
  22 + * @param replaceMap
  23 + * @return
  24 + */
  25 + public static String replace(String str, Map<String, String> replaceMap) {
  26 + assert StrUtil.isNotBlank(str);
  27 + if (ObjectUtil.isEmpty(replaceMap)) {
  28 + return str;
  29 + }
  30 + String result = null;
  31 + for (String key : replaceMap.keySet()) {
  32 + result = str.replace(key, replaceMap.get(key));
  33 + }
  34 + return result;
  35 + }
  36 +
16 } 37 }