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 884 INSERT INTO `sys_user_role` VALUES (6, 100, 2, '', NULL, '', NULL, b'0');
885 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 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 6 import com.aliyuncs.DefaultAcsClient;
8 7 import com.aliyuncs.IAcsClient;
9 8 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
... ... @@ -23,7 +22,7 @@ import java.util.Collection;
23 22 * @date 2021/1/25 14:17
24 23 */
25 24 @Slf4j
26   -public class AliSmsClient implements SmsClient<SendSmsResponse> {
  25 +public class AliyunSmsClient extends AbstractSmsClient<SendSmsResponse> {
27 26  
28 27 private static final String OK = "OK";
29 28  
... ... @@ -33,8 +32,6 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; {
33 32  
34 33 private static final String ENDPOINT = "cn-hangzhou";
35 34  
36   - private final SmsChannelAllVO channelVO;
37   -
38 35 private final IAcsClient acsClient;
39 36  
40 37 /**
... ... @@ -42,9 +39,8 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; {
42 39 *
43 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 45 String accessKeyId = channelVO.getApiKey();
50 46 String accessKeySecret = channelVO.getApiSecret();
... ... @@ -57,13 +53,13 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; {
57 53  
58 54  
59 55 @Override
60   - public SmsResult<SendSmsResponse> send(SmsBody msgBody, Collection<String> targets) {
  56 + public SmsResult<SendSmsResponse> send(SmsBody smsBody, Collection<String> targets) {
61 57 SendSmsRequest request = new SendSmsRequest();
62 58 request.setSysMethod(MethodType.POST);
63 59 request.setPhoneNumbers(StringUtils.join(targets, ","));
64 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 64 try {
69 65 SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
... ... @@ -78,14 +74,15 @@ public class AliSmsClient implements SmsClient&lt;SendSmsResponse&gt; {
78 74 return resultBody;
79 75 } catch (Exception e) {
80 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 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 3 import cn.iocoder.dashboard.util.json.JsonUtils;
4 4 import lombok.Data;
5 5  
6 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 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 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 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 14 */
15 15 @Data
16 16 @EqualsAndHashCode
17   -public class SmsChannelAllVO implements Serializable {
  17 +public class SmsChannelPropertyVO implements Serializable {
18 18  
19 19 /**
20 20 * id
... ...
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsTemplateVO.java
... ... @@ -17,6 +17,7 @@ public class SmsTemplateVO {
17 17 * 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)
18 18 */
19 19 private String bizCode;
  20 +
20 21 /**
21 22 * 编码
22 23 */
... ... @@ -27,4 +28,11 @@ public class SmsTemplateVO {
27 28 */
28 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 2  
3 3 import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
4 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 6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
7 7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
8 8 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO;
... ... @@ -28,7 +28,7 @@ public interface SmsChannelConvert {
28 28  
29 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 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 4 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 5 import org.apache.ibatis.annotations.Mapper;
6 6  
7 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 3 import com.baomidou.mybatisplus.annotation.TableName;
4 4 import lombok.Data;
5 5 import lombok.EqualsAndHashCode;
  6 +import lombok.experimental.Accessors;
6 7  
7 8 import java.io.Serializable;
8 9 import java.util.Date;
... ... @@ -15,8 +16,9 @@ import java.util.Date;
15 16 */
16 17 @Data
17 18 @EqualsAndHashCode
  19 +@Accessors(chain = true)
18 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 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 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 56 private String remark;
55 57  
56 58 /**
57   - * 发送状态(0发送中 1成功 2失败)
  59 + * 发送状态(1异步推送中 2发送中 3失败 4成功)
58 60 */
59 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 83 ErrorCode SMS_TEMPLATE_NOT_FOUND = new ErrorCode(1003001003, "没有短信模板信息, 请初始化sms_template表数据。");
84 84 ErrorCode SMS_SENDER_NOT_FOUND = new ErrorCode(1003001004, "没有找到对应的短信发送对象,请检查sms_channel表和sms_template表数据");
85 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 1 package cn.iocoder.dashboard.modules.system.service.sms;
2 2  
3 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 6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
6 7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
7 8 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
... ... @@ -17,18 +18,46 @@ import java.util.List;
17 18 */
18 19 public interface SmsChannelService {
19 20  
  21 + /**
  22 + * 初始化短信渠道
  23 + */
  24 + void initSmsClient();
  25 +
  26 + /**
  27 + * 分页查询短信渠道信息
  28 + *
  29 + * @param reqVO 参数对象
  30 + * @return 短信渠道分页对象
  31 + */
20 32 PageResult<SmsChannelDO> pageChannels(SmsChannelPageReqVO reqVO);
21 33  
  34 + /**
  35 + * 创建新的渠道信息
  36 + *
  37 + * @param reqVO 参数对象
  38 + * @return 渠道id
  39 + */
22 40 Long createChannel(SmsChannelCreateReqVO reqVO);
23 41  
  42 + /**
  43 + * 获取短信渠道枚举/渠道编码
  44 + *
  45 + * @return 短信渠道枚举/渠道编码
  46 + */
24 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 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 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 10 * 短信渠道Service接口
5 11 *
... ... @@ -7,4 +13,23 @@ package cn.iocoder.dashboard.modules.system.service.sms;
7 13 * @date 2021/1/25 9:24
8 14 */
9 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 5 import org.apache.commons.lang3.StringUtils;
4 6  
5 7 import java.util.Arrays;
6   -import java.util.Collection;
7 8 import java.util.Collections;
  9 +import java.util.List;
8 10  
9 11 /**
10   - * 短信父接口
  12 + * 短信Service接口
11 13 *
12 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 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 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 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 97 resultBody.setMessage(message);
105 98 return resultBody;
106 99 }
107   -}
108 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 3 import cn.hutool.core.util.ObjectUtil;
4 4 import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
5 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 9 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
8 10 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
9 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 16 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO;
15 17 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsTemplateDO;
16 18 import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
  19 +import org.springframework.beans.factory.annotation.Autowired;
17 20 import org.springframework.stereotype.Service;
18 21  
  22 +import javax.annotation.PostConstruct;
19 23 import javax.annotation.Resource;
20 24 import java.util.ArrayList;
21 25 import java.util.Arrays;
22 26 import java.util.List;
  27 +import java.util.Map;
  28 +import java.util.concurrent.ConcurrentHashMap;
23 29  
24 30 /**
25 31 * 短信渠道Service实现类
... ... @@ -30,6 +36,30 @@ import java.util.List;
30 36 @Service
31 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 63 @Resource
34 64 private SmsChannelMapper mapper;
35 65  
... ... @@ -54,12 +84,17 @@ public class SmsChannelServiceImpl implements SmsChannelService {
54 84 }
55 85  
56 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 93 List<SmsChannelDO> channelDOList = mapper.selectEnabledList();
59 94 if (ObjectUtil.isNull(channelDOList)) {
60 95 return null;
61 96 }
62   - List<SmsChannelAllVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList);
  97 + List<SmsChannelPropertyVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList);
63 98  
64 99 channelAllVOList.forEach(smsChannelDO -> {
65 100  
... ... @@ -71,10 +106,4 @@ public class SmsChannelServiceImpl implements SmsChannelService {
71 106 });
72 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 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 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 14 import org.springframework.stereotype.Service;
5 15  
  16 +import javax.annotation.Resource;
  17 +import java.util.List;
  18 +
6 19 /**
7 20 * 短信日志Service实现类
8 21 *
... ... @@ -12,4 +25,48 @@ import org.springframework.stereotype.Service;
12 25 @Service
13 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 1 package cn.iocoder.dashboard.util.string;
2 2  
  3 +import cn.hutool.core.util.ObjectUtil;
3 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 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 }
... ...