From 009f33210680bb77a727636732ce23ab5c8c5fb5 Mon Sep 17 00:00:00 2001
From: zengzefeng <986510453@qq.com>
Date: Mon, 1 Feb 2021 14:25:22 +0800
Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E7=9F=AD=E4=BF=A1=E5=8A=9F?=
 =?UTF-8?q?=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 sql/ruoyi-vue-pro.sql                         |  71 ---------
 sql/sms.sql                                   |  82 ++++++++++
 .../dashboard/framework/sms/SmsClient.java    | 107 -------------
 .../framework/sms/SmsClientAdapter.java       |  42 -----
 .../sms/client/AbstractSmsClient.java         |  32 ++++
 .../sms/client/AliyunSmsClient.java}          |  35 ++---
 .../framework/sms/client/SmsClient.java       |  25 +++
 .../framework/sms/{ => core}/SmsBody.java     |  10 +-
 .../framework/sms/core/SmsClientFactory.java  |  71 +++++++++
 .../framework/sms/{ => core}/SmsResult.java   |   2 +-
 .../controller/sms/SmsChannelController.java  |   6 -
 ...elAllVO.java => SmsChannelPropertyVO.java} |   2 +-
 .../controller/sms/vo/SmsTemplateVO.java      |   8 +
 .../system/convert/sms/SmsChannelConvert.java |   4 +-
 .../dal/mysql/dao/sms/SmsLogMapper.java       |   4 +-
 .../sms/{SmsLog.java => SmsLogDO.java}        |  16 +-
 .../system/enums/SysErrorCodeConstants.java   |   1 +
 .../system/enums/sms/SmsSendStatusEnum.java   |  30 ++++
 .../mq/consumer/sms/SmsSendConsumer.java      |  31 ++++
 .../system/mq/message/sms/SmsSendMessage.java |  25 +++
 .../system/mq/producer/sms/SmsProducer.java   |  31 ++++
 .../system/service/sms/SmsChannelService.java |  37 ++++-
 .../system/service/sms/SmsLogService.java     |  25 +++
 .../system/service/sms/SmsService.java        | 101 +++++++++++++
 .../sms/impl/SmsChannelServiceImpl.java       |  47 ++++--
 .../service/sms/impl/SmsLogServiceImpl.java   |  57 +++++++
 .../service/sms/impl/SmsServiceImpl.java      |  51 +++++++
 .../modules/system/sms/SmsConfiguration.java  |  34 -----
 .../modules/system/sms/SmsSenderUtils.java    | 143 ------------------
 .../system/sms/proxy/SmsClientLogProxy.java   |  48 ------
 .../dashboard/util/string/StrUtils.java       |  21 +++
 31 files changed, 701 insertions(+), 498 deletions(-)
 create mode 100644 sql/sms.sql
 delete mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/SmsClient.java
 delete mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/SmsClientAdapter.java
 create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java
 rename src/main/java/cn/iocoder/dashboard/{modules/system/sms/client/AliSmsClient.java => framework/sms/client/AliyunSmsClient.java} (73%)
 create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java
 rename src/main/java/cn/iocoder/dashboard/framework/sms/{ => core}/SmsBody.java (67%)
 create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java
 rename src/main/java/cn/iocoder/dashboard/framework/sms/{ => core}/SmsResult.java (86%)
 rename src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/{SmsChannelAllVO.java => SmsChannelPropertyVO.java} (94%)
 rename src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/{SmsLog.java => SmsLogDO.java} (73%)
 create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java
 create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java
 create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SmsSendMessage.java
 create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SmsProducer.java
 create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsService.java
 create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsServiceImpl.java
 delete mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsConfiguration.java
 delete mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsSenderUtils.java
 delete mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/sms/proxy/SmsClientLogProxy.java

diff --git a/sql/ruoyi-vue-pro.sql b/sql/ruoyi-vue-pro.sql
index 9355fab64..af7f492f9 100644
--- a/sql/ruoyi-vue-pro.sql
+++ b/sql/ruoyi-vue-pro.sql
@@ -884,75 +884,4 @@ INSERT INTO `sys_user_role` VALUES (5, 100, 1, '', NULL, '', NULL, b'0');
 INSERT INTO `sys_user_role` VALUES (6, 100, 2, '', NULL, '', NULL, b'0');
 COMMIT;
 
-
--- ----------------------------
--- Table structure for sms_channel
--- ----------------------------
-DROP TABLE IF EXISTS `sms_channel`;
-CREATE TABLE `sms_channel` (
-    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
-    `code` varchar(50) not null COMMENT '编码(来自枚举类 阿里、华为、七牛等)',
-    `api_key` varchar(100) NOT NULL COMMENT '账号id',
-    `api_secret` varchar(100) NOT NULL COMMENT '账号秘钥',
-    `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识',
-    `name` varchar(50) not null COMMENT '名称',
-    `signature` varchar(50) not null COMMENT '签名值',
-    `remark` varchar(200) NOT NULL COMMENT '备注',
-
-    `status` tinyint(4) NOT NULL default 0 COMMENT '启用状态(0正常 1停用)',
-    `create_by` varchar(64) not null DEFAULT '' COMMENT '创建者',
-    `create_time` datetime DEFAULT NULL COMMENT '创建时间',
-    `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
-    `update_time` datetime DEFAULT NULL COMMENT '更新时间',
-    `deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',
-    PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信渠道';
-/*
-	优先级值一样时,按照id顺序取值
-*/
-
--- ----------------------------
--- Table structure for sms_template
--- ----------------------------
-DROP TABLE IF EXISTS `sms_template`;
-CREATE TABLE `sms_template` (
-    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
-    `channel_code` varchar(50) not null COMMENT '短信渠道编码(来自枚举类)',
-    `channel_id` bigint(20) not null COMMENT '短信渠道id (对于前端来说就是绑定一个签名)',
-    `type` tinyint(4) NOT NULL default 1 COMMENT '消息类型 [0验证码 1短信通知 2推广短信 3国际/港澳台消息]',
-    `biz_code` varchar(50) not null COMMENT '业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)',
-    `code` varchar(50) not null COMMENT '编码',
-    `name` varchar(50) not null COMMENT '名称',
-    `api_template_id` varchar(100) NOT NULL COMMENT '实际渠道模板唯一标识',
-    `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',
-    `params` varchar(200) NOT NULL DEFAULT '' COMMENT '参数数组(自动根据内容生成)',
-    `remark` varchar(200) NOT NULL COMMENT '备注',
-
-    `status` tinyint(4) NOT NULL default 0 COMMENT '启用状态(0正常 1停用)',
-    `create_by` varchar(64) not null DEFAULT '' COMMENT '创建者',
-    `create_time` datetime DEFAULT NULL COMMENT '创建时间',
-    `update_by` varchar(64) DEFAULT '' COMMENT '更新者',
-    `update_time` datetime DEFAULT NULL COMMENT '更新时间',
-    `deleted` bit(1) DEFAULT b'0' COMMENT '是否删除',
-    PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信模板';
-
--- ----------------------------
--- Table structure for sms_log
--- ----------------------------
-DROP TABLE IF EXISTS `sms_log`;
-CREATE TABLE `sms_log` (
-    `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号',
-    `channel_code` varchar(50) not null COMMENT '短信渠道编码(来自枚举类)',
-    `api_sms_id` varchar(50) not null COMMENT '实际渠道短信唯一标识',
-    `template_id` bigint(20) NOT NULL COMMENT '模板id',
-    `phone` char(11) not null COMMENT '手机号',
-    `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',
-    `remark` varchar(200) NOT NULL COMMENT '备注',
-    `send_status` tinyint(4) NOT NULL default 0 COMMENT '发送状态(0发送中 1成功 2失败)',
-    `create_by` varchar(64) not null DEFAULT '' COMMENT '创建者',
-    `create_time` datetime DEFAULT NULL COMMENT '创建时间',
-    PRIMARY KEY (`id`) USING BTREE
-) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信日志';
-
 SET FOREIGN_KEY_CHECKS = 1;
diff --git a/sql/sms.sql b/sql/sms.sql
new file mode 100644
index 000000000..82d854155
--- /dev/null
+++ b/sql/sms.sql
@@ -0,0 +1,82 @@
+/*
+    --2021.02.01 by fight, sms about table info
+*/
+
+-- ----------------------------
+-- Table structure for sms_channel
+-- ----------------------------
+DROP TABLE IF EXISTS `sms_channel`;
+CREATE TABLE `sms_channel`
+(
+    `id`               bigint(20)   NOT NULL AUTO_INCREMENT COMMENT '自增编号',
+    `code`             varchar(50)  NOT NULL COMMENT '编码(来自枚举类 阿里、华为、七牛等)',
+    `api_key`          varchar(100) NOT NULL COMMENT '账号id',
+    `api_secret`       varchar(100) NOT NULL COMMENT '账号秘钥',
+    `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识',
+    `name`             varchar(50)  NOT NULL COMMENT '名称',
+    `signature`        varchar(50)  NOT NULL COMMENT '签名值',
+    `remark`           varchar(200) NOT NULL COMMENT '备注',
+
+    `status`           tinyint(4)   NOT NULL DEFAULT 0 COMMENT '启用状态(0正常 1停用)',
+    `create_by`        varchar(64)  NOT NULL DEFAULT '' COMMENT '创建者',
+    `create_time`      datetime              DEFAULT NULL COMMENT '创建时间',
+    `update_by`        varchar(64)           DEFAULT '' COMMENT '更新者',
+    `update_time`      datetime              DEFAULT NULL COMMENT '更新时间',
+    `deleted`          bit(1)                DEFAULT b'0' COMMENT '是否删除',
+    PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 1
+  DEFAULT CHARSET = utf8mb4 COMMENT ='短信渠道';
+/*
+	优先级值一样时,按照id顺序取值
+*/
+
+-- ----------------------------
+-- Table structure for sms_template
+-- ----------------------------
+DROP TABLE IF EXISTS `sms_template`;
+CREATE TABLE `sms_template`
+(
+    `id`              bigint(20)    NOT NULL AUTO_INCREMENT COMMENT '自增编号',
+    `channel_code`    varchar(50)   NOT NULL COMMENT '短信渠道编码(来自枚举类)',
+    `channel_id`      bigint(20)    NOT NULL COMMENT '短信渠道id (对于前端来说就是绑定一个签名)',
+    `type`            tinyint(4)    NOT NULL DEFAULT 1 COMMENT '消息类型 [0验证码 1短信通知 2推广短信 3国际/港澳台消息]',
+    `biz_code`        varchar(50)   NOT NULL COMMENT '业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)',
+    `code`            varchar(50)   NOT NULL COMMENT '编码',
+    `name`            varchar(50)   NOT NULL COMMENT '名称',
+    `api_template_id` varchar(100)  NOT NULL COMMENT '实际渠道模板唯一标识',
+    `content`         varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',
+    `params`          varchar(200)  NOT NULL DEFAULT '' COMMENT '参数数组(自动根据内容生成)',
+    `remark`          varchar(200)  NOT NULL COMMENT '备注',
+
+    `status`          tinyint(4)    NOT NULL DEFAULT 0 COMMENT '启用状态(0正常 1停用)',
+    `create_by`       varchar(64)   NOT NULL DEFAULT '' COMMENT '创建者',
+    `create_time`     datetime               DEFAULT NULL COMMENT '创建时间',
+    `update_by`       varchar(64)            DEFAULT '' COMMENT '更新者',
+    `update_time`     datetime               DEFAULT NULL COMMENT '更新时间',
+    `deleted`         bit(1)                 DEFAULT b'0' COMMENT '是否删除',
+    PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 1
+  DEFAULT CHARSET = utf8mb4 COMMENT ='短信模板';
+
+-- ----------------------------
+-- Table structure for sms_log
+-- ----------------------------
+DROP TABLE IF EXISTS `sms_log`;
+CREATE TABLE `sms_log`
+(
+    `id`            bigint(20)    NOT NULL AUTO_INCREMENT COMMENT '自增编号',
+    `channel_code`  varchar(50)   NOT NULL COMMENT '短信渠道编码(来自枚举类)',
+    `channel_id`    bigint(20)    NOT NULL COMMENT '短信渠道id',
+    `template_code` varchar(50)   NOT NULL COMMENT '渠道编码',
+    `phones`        char(11)      NOT NULL COMMENT '手机号(数组json字符串)',
+    `content`       varchar(1000) NOT NULL DEFAULT '' COMMENT '内容',
+    `remark`        varchar(200)  DEFAULT NULL COMMENT '备注',
+    `send_status`   tinyint(4)    NOT NULL DEFAULT 2 COMMENT '发送状态(1异步推送中 2发送中 3失败 4成功)',
+    `create_by`     varchar(64)   NOT NULL DEFAULT '' COMMENT '创建者',
+    `create_time`   datetime               DEFAULT NULL COMMENT '创建时间',
+    PRIMARY KEY (`id`) USING BTREE
+) ENGINE = InnoDB
+  AUTO_INCREMENT = 1
+  DEFAULT CHARSET = utf8mb4 COMMENT ='短信日志';
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/SmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/SmsClient.java
deleted file mode 100644
index bf1a44f57..000000000
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/SmsClient.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package cn.iocoder.dashboard.framework.sms;
-
-import org.apache.commons.lang3.StringUtils;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-
-/**
- * 短信父接口
- *
- * @author zzf
- * @date 2021/1/25 14:14
- */
-public interface SmsClient<R> {
-
-    /**
-     * 发送消息
-     *
-     * @param msgBody 消息内容
-     * @param targets 发送对象列表
-     * @return 是否发送成功
-     */
-    SmsResult<R> send(SmsBody msgBody, Collection<String> targets);
-
-    /**
-     * 发送消息
-     *
-     * @param msgBody 消息内容
-     * @param target  发送对象
-     * @return 是否发送成功
-     */
-    default SmsResult<R> send(SmsBody msgBody, String target) {
-        if (StringUtils.isBlank(target)) {
-            return failResult();
-        }
-
-        return send(msgBody, Collections.singletonList(target));
-    }
-
-    /**
-     * 发送消息
-     *
-     * @param msgBody 消息内容
-     * @param targets 发送对象列表
-     * @return 是否发送成功
-     */
-    default SmsResult<R> send(SmsBody msgBody, String... targets) {
-        if (targets == null) {
-            return failResult();
-        }
-
-        return send(msgBody, Arrays.asList(targets));
-    }
-
-
-    /**
-     * 异步发送消息
-     *
-     * @param msgBody 消息内容
-     * @param targets 发送对象列表
-     * @return 是否发送成功
-     */
-    SmsResult<R> sendAsync(SmsBody msgBody, Collection<String> targets);
-
-    /**
-     * 异步发送消息
-     *
-     * @param msgBody 消息内容
-     * @param target  发送对象
-     * @return 是否发送成功
-     */
-    default SmsResult<R> sendAsync(SmsBody msgBody, String target) {
-        if (StringUtils.isBlank(target)) {
-            return failResult("target must not null.");
-        }
-
-        return sendAsync(msgBody, Collections.singletonList(target));
-    }
-
-    /**
-     * 异步发送消息
-     *
-     * @param msgBody 消息内容
-     * @param targets 发送对象列表
-     * @return 是否发送成功
-     */
-    default SmsResult<R> sendAsync(SmsBody msgBody, String... targets) {
-        if (targets == null) {
-            return failResult("targets must not null.");
-        }
-
-        return sendAsync(msgBody, Arrays.asList(targets));
-    }
-
-    default SmsResult<R> failResult() {
-        SmsResult<R> resultBody = new SmsResult<>();
-        resultBody.setSuccess(false);
-        return resultBody;
-    }
-
-    default SmsResult<R> failResult(String message) {
-        SmsResult<R> resultBody = failResult();
-        resultBody.setMessage(message);
-        return resultBody;
-    }
-}
\ No newline at end of file
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/SmsClientAdapter.java b/src/main/java/cn/iocoder/dashboard/framework/sms/SmsClientAdapter.java
deleted file mode 100644
index 6edf7646f..000000000
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/SmsClientAdapter.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package cn.iocoder.dashboard.framework.sms;
-
-import cn.hutool.core.util.ObjectUtil;
-import cn.iocoder.dashboard.common.exception.ServiceException;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Map;
-
-import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.SMS_CHANNEL_NOT_INIT;
-
-/**
- * 抽象短信客户端工厂
- *
- * @author zzf
- * @date 2021/1/28 14:01
- */
-public class SmsClientAdapter {
-
-    private final Map<Long, SmsClient<?>> smsSenderMap;
-
-    public SmsClientAdapter(Map<Long, SmsClient<?>> smsSenderMap) {
-        if (ObjectUtil.isEmpty(smsSenderMap)) {
-            throw new ServiceException(SMS_CHANNEL_NOT_INIT);
-        }
-        this.smsSenderMap = smsSenderMap;
-    }
-
-    public void flushClient(Map<Long, SmsClient<?>> smsSenderMap) {
-        this.smsSenderMap.clear();
-        smsSenderMap.putAll(Collections.unmodifiableMap(smsSenderMap));
-    }
-
-    public SmsResult<?> send(Long channelId, SmsBody smsBody, Collection<String> targetPhone) {
-        SmsClient<?> smsClient = getSmsSender(channelId);
-        return smsClient.send(smsBody, targetPhone);
-    }
-
-    private SmsClient<?> getSmsSender(Long channelId) {
-        return smsSenderMap.get(channelId);
-    }
-}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java
new file mode 100644
index 000000000..043dd2a50
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java
@@ -0,0 +1,32 @@
+package cn.iocoder.dashboard.framework.sms.client;
+
+import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
+
+/**
+ * 抽象短息客户端
+ *
+ * @author zzf
+ * @date 2021/2/1 9:28
+ */
+public abstract class AbstractSmsClient<R> implements SmsClient<R> {
+
+    /**
+     * 短信渠道参数
+     */
+    protected final SmsChannelPropertyVO channelVO;
+
+    /**
+     * 构造阿里云短信发送处理
+     *
+     * @param channelVO 阿里云短信配置
+     */
+    public AbstractSmsClient(SmsChannelPropertyVO channelVO) {
+        this.channelVO = channelVO;
+    }
+
+
+    public SmsChannelPropertyVO getProperty() {
+        return channelVO;
+    }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/sms/client/AliSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java
similarity index 73%
rename from src/main/java/cn/iocoder/dashboard/modules/system/sms/client/AliSmsClient.java
rename to src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java
index f144ec380..39d359449 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/sms/client/AliSmsClient.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java
@@ -1,9 +1,8 @@
-package cn.iocoder.dashboard.modules.system.sms.client;
+package cn.iocoder.dashboard.framework.sms.client;
 
-import cn.iocoder.dashboard.framework.sms.SmsBody;
-import cn.iocoder.dashboard.framework.sms.SmsClient;
-import cn.iocoder.dashboard.framework.sms.SmsResult;
-import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
+import cn.iocoder.dashboard.framework.sms.core.SmsBody;
+import cn.iocoder.dashboard.framework.sms.core.SmsResult;
+import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
 import com.aliyuncs.DefaultAcsClient;
 import com.aliyuncs.IAcsClient;
 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
@@ -23,7 +22,7 @@ import java.util.Collection;
  * @date 2021/1/25 14:17
  */
 @Slf4j
-public class AliSmsClient implements SmsClient<SendSmsResponse> {
+public class AliyunSmsClient extends AbstractSmsClient<SendSmsResponse> {
 
     private static final String OK = "OK";
 
@@ -33,8 +32,6 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> {
 
     private static final String ENDPOINT = "cn-hangzhou";
 
-    private final SmsChannelAllVO channelVO;
-
     private final IAcsClient acsClient;
 
     /**
@@ -42,9 +39,8 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> {
      *
      * @param channelVO 阿里云短信配置
      */
-    public AliSmsClient(SmsChannelAllVO channelVO) {
-
-        this.channelVO = channelVO;
+    public AliyunSmsClient(SmsChannelPropertyVO channelVO) {
+        super(channelVO);
 
         String accessKeyId = channelVO.getApiKey();
         String accessKeySecret = channelVO.getApiSecret();
@@ -57,13 +53,13 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> {
 
 
     @Override
-    public SmsResult<SendSmsResponse> send(SmsBody msgBody, Collection<String> targets) {
+    public SmsResult<SendSmsResponse> send(SmsBody smsBody, Collection<String> targets) {
         SendSmsRequest request = new SendSmsRequest();
         request.setSysMethod(MethodType.POST);
         request.setPhoneNumbers(StringUtils.join(targets, ","));
         request.setSignName(channelVO.getApiSignatureId());
-        request.setTemplateCode(channelVO.getTemplateByTemplateCode(msgBody.getCode()).getApiTemplateId());
-        request.setTemplateParam(msgBody.getParamsStr());
+        request.setTemplateCode(channelVO.getTemplateByTemplateCode(smsBody.getTemplateCode()).getApiTemplateId());
+        request.setTemplateParam(smsBody.getParamsStr());
 
         try {
             SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
@@ -78,14 +74,15 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> {
             return resultBody;
         } catch (Exception e) {
             log.debug(e.getMessage(), e);
+            return failResult("发送异常: " + e.getMessage());
         }
-
-        return failResult();
     }
 
-    @Override
-    public SmsResult<SendSmsResponse> sendAsync(SmsBody msgBody, Collection<String> targets) {
-        return null;
+    SmsResult<SendSmsResponse> failResult(String message) {
+        SmsResult<SendSmsResponse> resultBody = new SmsResult<>();
+        resultBody.setSuccess(false);
+        resultBody.setMessage(message);
+        return resultBody;
     }
 
 }
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java
new file mode 100644
index 000000000..290d21712
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java
@@ -0,0 +1,25 @@
+package cn.iocoder.dashboard.framework.sms.client;
+
+import cn.iocoder.dashboard.framework.sms.core.SmsBody;
+import cn.iocoder.dashboard.framework.sms.core.SmsResult;
+
+import java.util.Collection;
+
+/**
+ * 短信父接口
+ *
+ * @author zzf
+ * @date 2021/1/25 14:14
+ */
+public interface SmsClient<R> {
+
+    /**
+     * 发送消息
+     *
+     * @param smsBody 消息内容
+     * @param targets 发送对象列表
+     * @return 是否发送成功
+     */
+    SmsResult<R> send(SmsBody smsBody, Collection<String> targets);
+
+}
\ No newline at end of file
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/SmsBody.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java
similarity index 67%
rename from src/main/java/cn/iocoder/dashboard/framework/sms/SmsBody.java
rename to src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java
index 48fb2253b..9b132431e 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/SmsBody.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java
@@ -1,9 +1,10 @@
-package cn.iocoder.dashboard.framework.sms;
+package cn.iocoder.dashboard.framework.sms.core;
 
 import cn.iocoder.dashboard.util.json.JsonUtils;
 import lombok.Data;
 
 import java.util.Map;
+import java.util.UUID;
 
 /**
  * 消息内容实体类
@@ -11,10 +12,15 @@ import java.util.Map;
 @Data
 public class SmsBody {
 
+    /**
+     * 消息日志id
+     */
+    private Long smsLogId;
+
     /**
      * 模板编码
      */
-    private String code;
+    private String templateCode;
 
     /**
      * 参数列表
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java
new file mode 100644
index 000000000..6c1eaf07c
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java
@@ -0,0 +1,71 @@
+package cn.iocoder.dashboard.framework.sms.core;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
+import cn.iocoder.dashboard.common.exception.ServiceException;
+import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil;
+import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
+import cn.iocoder.dashboard.framework.sms.client.AliyunSmsClient;
+import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
+import org.springframework.stereotype.Component;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
+
+/**
+ * 短信客户端工厂
+ *
+ * @author zzf
+ * @date 2021/1/28 14:01
+ */
+@Component
+public class SmsClientFactory {
+
+    private final Map<Long, AbstractSmsClient<?>> smsSenderMap = new ConcurrentHashMap<>(8);
+
+    /**
+     * 创建短信客户端
+     *
+     * @param propertyVO 参数对象
+     * @return 客户端id(默认channelId)
+     */
+    public Long createClient(SmsChannelPropertyVO propertyVO) {
+        if (StrUtil.isBlank(propertyVO.getCode())) {
+            throw ServiceExceptionUtil.exception(PARAM_VALUE_IS_NULL, "短信渠道编码");
+        }
+        if (ObjectUtil.isNull(propertyVO.getId())) {
+            throw ServiceExceptionUtil.exception(PARAM_VALUE_IS_NULL, "短信渠道ID");
+        }
+
+        AbstractSmsClient<?> sender = createClient(SmsChannelEnum.getByCode(propertyVO.getCode()), propertyVO);
+        smsSenderMap.put(propertyVO.getId(), sender);
+        return propertyVO.getId();
+    }
+
+    private AbstractSmsClient<?> createClient(SmsChannelEnum channelEnum, SmsChannelPropertyVO channelVO) {
+        if (channelEnum == null) {
+            throw new ServiceException(INVALID_CHANNEL_CODE);
+        }
+        switch (channelEnum) {
+            case ALI:
+                return new AliyunSmsClient(channelVO);
+            // TODO fill more channel
+            default:
+                break;
+        }
+        throw new ServiceException(SMS_SENDER_NOT_FOUND);
+    }
+
+    /**
+     * 获取短信客户端
+     *
+     * @param channelId 渠道id
+     * @return 短信id
+     */
+    public AbstractSmsClient<?> getClient(Long channelId) {
+        return smsSenderMap.get(channelId);
+    }
+}
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/SmsResult.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java
similarity index 86%
rename from src/main/java/cn/iocoder/dashboard/framework/sms/SmsResult.java
rename to src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java
index b994514b6..a626bb759 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/SmsResult.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java
@@ -1,4 +1,4 @@
-package cn.iocoder.dashboard.framework.sms;
+package cn.iocoder.dashboard.framework.sms.core;
 
 import lombok.Data;
 
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsChannelController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsChannelController.java
index a75f9e2eb..932097514 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsChannelController.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsChannelController.java
@@ -44,11 +44,5 @@ public class SmsChannelController {
         return success(service.createChannel(reqVO));
     }
 
-    @ApiOperation("刷新消息渠道信息")
-    @PutMapping("/flush")
-    public CommonResult<Boolean> flushChannel() {
-        return success(service.flushChannel());
-    }
-
 
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsChannelAllVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsChannelPropertyVO.java
similarity index 94%
rename from src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsChannelAllVO.java
rename to src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsChannelPropertyVO.java
index 042cddaa2..ab4d73594 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsChannelAllVO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsChannelPropertyVO.java
@@ -14,7 +14,7 @@ import java.util.List;
  */
 @Data
 @EqualsAndHashCode
-public class SmsChannelAllVO implements Serializable {
+public class SmsChannelPropertyVO implements Serializable {
 
     /**
      * id
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsTemplateVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsTemplateVO.java
index c5156a6ae..d9daf7578 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsTemplateVO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsTemplateVO.java
@@ -17,6 +17,7 @@ public class SmsTemplateVO {
      * 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)
      */
     private String bizCode;
+
     /**
      * 编码
      */
@@ -27,4 +28,11 @@ public class SmsTemplateVO {
      */
     private String apiTemplateId;
 
+    /**
+     * 内容
+     */
+    private String content;
+
+
+
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java b/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java
index af87d2253..985de7576 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java
@@ -2,7 +2,7 @@ package cn.iocoder.dashboard.modules.system.convert.sms;
 
 import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
 import cn.iocoder.dashboard.common.pojo.PageResult;
-import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
+import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO;
@@ -28,7 +28,7 @@ public interface SmsChannelConvert {
 
     List<SmsChannelEnumRespVO> convertEnum(List<SmsChannelEnum> bean);
 
-    List<SmsChannelAllVO> convert(List<SmsChannelDO> bean);
+    List<SmsChannelPropertyVO> convert(List<SmsChannelDO> bean);
 
 
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SmsLogMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SmsLogMapper.java
index a5dce01de..2e52b070e 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SmsLogMapper.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SmsLogMapper.java
@@ -1,10 +1,10 @@
 package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms;
 
-import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLog;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLogDO;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
-public interface SmsLogMapper extends BaseMapper<SmsLog> {
+public interface SmsLogMapper extends BaseMapper<SmsLogDO> {
 
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SmsLog.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SmsLogDO.java
similarity index 73%
rename from src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SmsLog.java
rename to src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SmsLogDO.java
index 169369b1e..10ffef478 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SmsLog.java
+++ b/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;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
 
 import java.io.Serializable;
 import java.util.Date;
@@ -15,8 +16,9 @@ import java.util.Date;
  */
 @Data
 @EqualsAndHashCode
+@Accessors(chain = true)
 @TableName(value = "sms_log", autoResultMap = true)
-public class SmsLog implements Serializable {
+public class SmsLogDO implements Serializable {
 
     /**
      * 自增编号
@@ -29,19 +31,19 @@ public class SmsLog implements Serializable {
     private String channelCode;
 
     /**
-     * 实际渠道短信唯一标识
+     * 短信渠道id
      */
-    private String apiSmsId;
+    private Long channelId;
 
     /**
      * 模板id
      */
-    private Long templateId;
+    private String templateCode;
 
     /**
-     * 手机号
+     * 手机号(数组json字符串)
      */
-    private String phone;
+    private String phones;
 
     /**
      * 内容
@@ -54,7 +56,7 @@ public class SmsLog implements Serializable {
     private String remark;
 
     /**
-     * 发送状态(0发送中 1成功 2失败)
+     * 发送状态(1异步推送中 2发送中 3失败 4成功)
      */
     private Integer sendStatus;
 
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java b/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
index 05e9e6393..a1fc081c5 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
@@ -83,5 +83,6 @@ public interface SysErrorCodeConstants {
     ErrorCode SMS_TEMPLATE_NOT_FOUND = new ErrorCode(1003001003, "没有短信模板信息, 请初始化sms_template表数据。");
     ErrorCode SMS_SENDER_NOT_FOUND = new ErrorCode(1003001004, "没有找到对应的短信发送对象,请检查sms_channel表和sms_template表数据");
     ErrorCode INVALID_CHANNEL_CODE = new ErrorCode(1003001005, "非法的短信渠道code,请检查sms_channel表的code值是否与SmsChannelEnum中的code值一致。");
+    ErrorCode PARAM_VALUE_IS_NULL = new ErrorCode(1003001006, "参数【{}】不能为空");
 
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java b/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java
new file mode 100644
index 000000000..426e4cb80
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java
@@ -0,0 +1,30 @@
+package cn.iocoder.dashboard.modules.system.enums.sms;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 短信发送状态
+ *
+ * @author zzf
+ * @date 2021/2/1 13:39
+ */
+@Getter
+@AllArgsConstructor
+public enum SmsSendStatusEnum {
+
+    //异步转发中
+    ASYNC(1),
+
+    //发送中
+    SENDING(2),
+
+    //失败
+    FAIL(3),
+
+    //成功
+    SUCCESS(4);
+
+    private final int status;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java b/src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java
new file mode 100644
index 000000000..5da9703ca
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java
@@ -0,0 +1,31 @@
+package cn.iocoder.dashboard.modules.system.mq.consumer.sms;
+
+import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener;
+import cn.iocoder.dashboard.framework.sms.core.SmsResult;
+import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage;
+import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage;
+import cn.iocoder.dashboard.modules.system.service.sms.SmsService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+
+/**
+ * 针对 {@link SysDeptRefreshMessage} 的消费者
+ *
+ * @author 芋道源码
+ */
+@Component
+@Slf4j
+public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> {
+
+    @Resource
+    private SmsService smsService;
+
+    @Override
+    public void onMessage(SmsSendMessage message) {
+        log.info("[onMessage][收到 发送短信 消息]");
+        SmsResult<?> send = smsService.send(message.getSmsBody(), message.getTargetPhones());
+    }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SmsSendMessage.java b/src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SmsSendMessage.java
new file mode 100644
index 000000000..8ca1207fa
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SmsSendMessage.java
@@ -0,0 +1,25 @@
+package cn.iocoder.dashboard.modules.system.mq.message.sms;
+
+import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage;
+import cn.iocoder.dashboard.framework.sms.core.SmsBody;
+import lombok.Data;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 部门数据刷新 Message
+ */
+@Data
+public class SmsSendMessage implements ChannelMessage {
+
+    private SmsBody smsBody;
+
+    private List<String> targetPhones;
+
+    @Override
+    public String getChannel() {
+        return "sms.send";
+    }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SmsProducer.java b/src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SmsProducer.java
new file mode 100644
index 000000000..892b333cf
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SmsProducer.java
@@ -0,0 +1,31 @@
+package cn.iocoder.dashboard.modules.system.mq.producer.sms;
+
+import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils;
+import cn.iocoder.dashboard.framework.sms.core.SmsBody;
+import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 短信的 Producer
+ */
+@Component
+public class SmsProducer {
+
+    @Resource
+    private StringRedisTemplate stringRedisTemplate;
+
+    /**
+     * 发送 {@link SmsSendMessage} 消息
+     */
+    public void sendSmsSendMessage(SmsBody smsBody, List<String> targetPhoneList) {
+        SmsSendMessage message = new SmsSendMessage();
+        message.setSmsBody(smsBody);
+        message.setTargetPhones(targetPhoneList);
+        RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message);
+    }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsChannelService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsChannelService.java
index 3040f3bdb..5fd39c2cf 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsChannelService.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsChannelService.java
@@ -1,7 +1,8 @@
 package cn.iocoder.dashboard.modules.system.service.sms;
 
 import cn.iocoder.dashboard.common.pojo.PageResult;
-import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
+import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
+import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
@@ -17,18 +18,46 @@ import java.util.List;
  */
 public interface SmsChannelService {
 
+    /**
+     * 初始化短信渠道
+     */
+    void initSmsClient();
+
+    /**
+     * 分页查询短信渠道信息
+     *
+     * @param reqVO 参数对象
+     * @return 短信渠道分页对象
+     */
     PageResult<SmsChannelDO> pageChannels(SmsChannelPageReqVO reqVO);
 
+    /**
+     * 创建新的渠道信息
+     *
+     * @param reqVO 参数对象
+     * @return 渠道id
+     */
     Long createChannel(SmsChannelCreateReqVO reqVO);
 
+    /**
+     * 获取短信渠道枚举/渠道编码
+     *
+     * @return 短信渠道枚举/渠道编码
+     */
     List<SmsChannelEnumRespVO> getChannelEnums();
 
+    /**
+     * 根据短信模板编码获取短信客户端
+     *
+     * @param templateCode 短信模板编码
+     * @return 短信客户端
+     */
+    AbstractSmsClient<?> getClient(String templateCode);
+
     /**
      * 查询渠道(包含名下模块)信息集合
      *
      * @return 渠道(包含名下模块)信息集合
      */
-    List<SmsChannelAllVO> listChannelAllEnabledInfo();
-
-    boolean flushChannel();
+    List<SmsChannelPropertyVO> listChannelAllEnabledInfo();
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsLogService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsLogService.java
index d29d7f884..2646dcbfa 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsLogService.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsLogService.java
@@ -1,5 +1,11 @@
 package cn.iocoder.dashboard.modules.system.service.sms;
 
+import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
+import cn.iocoder.dashboard.framework.sms.core.SmsBody;
+import cn.iocoder.dashboard.framework.sms.core.SmsResult;
+
+import java.util.List;
+
 /**
  * 短信渠道Service接口
  *
@@ -7,4 +13,23 @@ package cn.iocoder.dashboard.modules.system.service.sms;
  * @date 2021/1/25 9:24
  */
 public interface SmsLogService {
+    /**
+     * 发送短信前的日志处理
+     *
+     * @param smsBody      短信内容
+     * @param targetPhones 发送对象手机号集合
+     * @param client       短信客户端
+     * @param isAsync      是否异步发送
+     * @return 生成的日志id
+     */
+    Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient<?> client, Boolean isAsync);
+
+    /**
+     * 发送消息后的日志处理
+     *
+     * @param logId  日志id
+     * @param result 消息结果
+     */
+    void afterSendLog(Long logId, SmsResult<?> result);
+
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsService.java
new file mode 100644
index 000000000..56dc2dfa9
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsService.java
@@ -0,0 +1,101 @@
+package cn.iocoder.dashboard.modules.system.service.sms;
+
+import cn.iocoder.dashboard.framework.sms.core.SmsBody;
+import cn.iocoder.dashboard.framework.sms.core.SmsResult;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * 短信Service接口
+ *
+ * @author zzf
+ * @date 2021/1/25 9:24
+ */
+public interface SmsService {
+
+    /**
+     * 发送消息
+     *
+     * @param smsBody      消息内容
+     * @param targetPhones 发送对象手机号列表
+     * @return 是否发送成功
+     */
+    SmsResult<?> send(SmsBody smsBody, List<String> targetPhones);
+
+    /**
+     * 发送消息
+     *
+     * @param smsBody     消息内容
+     * @param targetPhone 发送对象手机号
+     * @return 是否发送成功
+     */
+    default SmsResult<?> send(SmsBody smsBody, String targetPhone) {
+        if (StringUtils.isBlank(targetPhone)) {
+            return failResult("targetPhone must not null.");
+        }
+
+        return send(smsBody, Collections.singletonList(targetPhone));
+    }
+
+    /**
+     * 发送消息
+     *
+     * @param smsBody      消息内容
+     * @param targetPhones 发送对象手机号数组
+     * @return 是否发送成功
+     */
+    default SmsResult<?> send(SmsBody smsBody, String... targetPhones) {
+        if (targetPhones == null) {
+            return failResult("targetPhones must not null.");
+        }
+
+        return send(smsBody, Arrays.asList(targetPhones));
+    }
+
+
+    /**
+     * 异步发送消息
+     *
+     * @param msgBody      消息内容
+     * @param targetPhones 发送对象列表
+     */
+    void sendAsync(SmsBody msgBody, List<String> targetPhones);
+
+    /**
+     * 异步发送消息
+     *
+     * @param msgBody     消息内容
+     * @param targetPhone 发送对象
+     */
+    default void sendAsync(SmsBody msgBody, String targetPhone) {
+        if (StringUtils.isBlank(targetPhone)) {
+            return;
+        }
+        sendAsync(msgBody, Collections.singletonList(targetPhone));
+    }
+
+    /**
+     * 异步发送消息
+     *
+     * @param msgBody      消息内容
+     * @param targetPhones 发送对象列表
+     */
+    default void sendAsync(SmsBody msgBody, String... targetPhones) {
+        if (targetPhones == null) {
+            return;
+        }
+        sendAsync(msgBody, Arrays.asList(targetPhones));
+    }
+
+
+    default SmsResult<?> failResult(String message) {
+        SmsResult<?> resultBody = new SmsResult<>();
+        resultBody.setSuccess(false);
+        resultBody.setMessage(message);
+        return resultBody;
+    }
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsChannelServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsChannelServiceImpl.java
index ccc47af3c..044a4e561 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsChannelServiceImpl.java
+++ b/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;
 import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
 import cn.iocoder.dashboard.common.pojo.PageResult;
-import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
+import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
+import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory;
+import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
@@ -14,12 +16,16 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsTemplateMapper;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsTemplateDO;
 import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * 短信渠道Service实现类
@@ -30,6 +36,30 @@ import java.util.List;
 @Service
 public class SmsChannelServiceImpl implements SmsChannelService {
 
+    private final Map<String, Long> templateCode2ChannelIdMap = new ConcurrentHashMap<>(32);
+
+    @Autowired
+    private SmsClientFactory smsClientFactory;
+
+    /**
+     * 初始化短信客户端
+     */
+    @PostConstruct
+    @Override
+    public void initSmsClient() {
+        List<SmsChannelPropertyVO> smsChannelPropertyVOList = listChannelAllEnabledInfo();
+        if (ObjectUtil.isEmpty(smsChannelPropertyVOList)) {
+            return;
+        }
+        smsChannelPropertyVOList.forEach(smsChannelPropertyVO -> {
+            Long clientId = smsClientFactory.createClient(smsChannelPropertyVO);
+            smsChannelPropertyVO.getTemplateList().forEach(smsTemplateVO -> {
+                templateCode2ChannelIdMap.put(smsTemplateVO.getCode(), clientId);
+            });
+        });
+    }
+
+
     @Resource
     private SmsChannelMapper mapper;
 
@@ -54,12 +84,17 @@ public class SmsChannelServiceImpl implements SmsChannelService {
     }
 
     @Override
-    public List<SmsChannelAllVO> listChannelAllEnabledInfo() {
+    public AbstractSmsClient<?> getClient(String templateCode) {
+        return smsClientFactory.getClient(templateCode2ChannelIdMap.get(templateCode));
+    }
+
+    @Override
+    public List<SmsChannelPropertyVO> listChannelAllEnabledInfo() {
         List<SmsChannelDO> channelDOList = mapper.selectEnabledList();
         if (ObjectUtil.isNull(channelDOList)) {
             return null;
         }
-        List<SmsChannelAllVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList);
+        List<SmsChannelPropertyVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList);
 
         channelAllVOList.forEach(smsChannelDO -> {
 
@@ -71,10 +106,4 @@ public class SmsChannelServiceImpl implements SmsChannelService {
         });
         return channelAllVOList;
     }
-
-    @Override
-    public boolean flushChannel() {
-
-        return true;
-    }
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsLogServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsLogServiceImpl.java
index e54d265c6..651917259 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsLogServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsLogServiceImpl.java
@@ -1,8 +1,21 @@
 package cn.iocoder.dashboard.modules.system.service.sms.impl;
 
+import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
+import cn.iocoder.dashboard.framework.sms.core.SmsBody;
+import cn.iocoder.dashboard.framework.sms.core.SmsResult;
+import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO;
+import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsLogMapper;
+import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLogDO;
+import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum;
 import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService;
+import cn.iocoder.dashboard.util.json.JsonUtils;
+import cn.iocoder.dashboard.util.string.StrUtils;
 import org.springframework.stereotype.Service;
 
+import javax.annotation.Resource;
+import java.util.List;
+
 /**
  * 短信日志Service实现类
  *
@@ -12,4 +25,48 @@ import org.springframework.stereotype.Service;
 @Service
 public class SmsLogServiceImpl implements SmsLogService {
 
+    @Resource
+    private SmsLogMapper smsLogMapper;
+
+    @Override
+    public Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient<?> client, Boolean isAsync) {
+        SmsLogDO smsLog = new SmsLogDO();
+        if (smsBody.getSmsLogId() != null) {
+            smsLog.setId(smsBody.getSmsLogId());
+            smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus());
+            smsLogMapper.updateById(smsLog);
+            return smsBody.getSmsLogId();
+        } else {
+            SmsChannelPropertyVO property = client.getProperty();
+            SmsTemplateVO smsTemplate = property.getTemplateByTemplateCode(smsBody.getTemplateCode());
+
+            smsLog.setChannelCode(property.getCode())
+                    .setChannelId(property.getId())
+                    .setTemplateCode(smsTemplate.getCode())
+                    .setPhones(JsonUtils.toJsonString(targetPhones))
+                    .setContent(StrUtils.replace(smsTemplate.getContent(), smsBody.getParams()));
+
+            if (isAsync) {
+                smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus());
+            } else {
+                smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus());
+            }
+            smsLogMapper.insert(smsLog);
+            return smsLog.getId();
+        }
+    }
+
+    @Override
+    public void afterSendLog(Long logId, SmsResult<?> result) {
+        SmsLogDO smsLog = new SmsLogDO();
+        smsLog.setId(logId);
+        if (result.getSuccess()) {
+            smsLog.setSendStatus(SmsSendStatusEnum.SUCCESS.getStatus());
+        } else {
+            smsLog.setSendStatus(SmsSendStatusEnum.FAIL.getStatus());
+            smsLog.setRemark(result.getMessage() + JsonUtils.toJsonString(result.getResult()));
+        }
+        smsLogMapper.updateById(smsLog);
+    }
+
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsServiceImpl.java
new file mode 100644
index 000000000..4dc090242
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsServiceImpl.java
@@ -0,0 +1,51 @@
+package cn.iocoder.dashboard.modules.system.service.sms.impl;
+
+import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;
+import cn.iocoder.dashboard.framework.sms.core.SmsBody;
+import cn.iocoder.dashboard.framework.sms.core.SmsResult;
+import cn.iocoder.dashboard.modules.system.mq.producer.sms.SmsProducer;
+import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
+import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService;
+import cn.iocoder.dashboard.modules.system.service.sms.SmsService;
+import org.springframework.stereotype.Service;
+
+import javax.annotation.Resource;
+import java.util.List;
+
+/**
+ * 短信日志Service实现类
+ *
+ * @author zzf
+ * @date 2021/1/25 9:25
+ */
+@Service
+public class SmsServiceImpl implements SmsService {
+
+    @Resource
+    private SmsChannelService channelService;
+
+    @Resource
+    private SmsLogService smsLogService;
+
+    @Resource
+    private SmsProducer smsProducer;
+
+    @Override
+    public SmsResult<?> send(SmsBody smsBody, List<String> targetPhones) {
+        AbstractSmsClient<?> client = channelService.getClient(smsBody.getTemplateCode());
+        Long logId = smsLogService.beforeSendLog(smsBody, targetPhones, client, false);
+
+        SmsResult<?> result = client.send(smsBody, targetPhones);
+
+        smsLogService.afterSendLog(logId, result);
+
+        return result;
+    }
+
+    @Override
+    public void sendAsync(SmsBody smsBody, List<String> targetPhones) {
+        AbstractSmsClient<?> client = channelService.getClient(smsBody.getTemplateCode());
+        smsLogService.beforeSendLog(smsBody, targetPhones, client, true);
+        smsProducer.sendSmsSendMessage(smsBody, targetPhones);
+    }
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsConfiguration.java b/src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsConfiguration.java
deleted file mode 100644
index ffb5887bd..000000000
--- a/src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsConfiguration.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package cn.iocoder.dashboard.modules.system.sms;
-
-import cn.iocoder.dashboard.framework.sms.SmsClient;
-import cn.iocoder.dashboard.framework.sms.SmsClientAdapter;
-import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
-import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-import javax.annotation.Resource;
-import java.util.List;
-import java.util.Map;
-
-/**
- * 短信服务配置
- *
- * @author guer
- */
-@Configuration
-@ConditionalOnProperty("sms.enabled")
-public class SmsConfiguration {
-
-    @Resource
-    private SmsChannelService channelService;
-
-    @Bean
-    public SmsClientAdapter smsClientWrapper() {
-        List<SmsChannelAllVO> smsChannelAllVOList = channelService.listChannelAllEnabledInfo();
-        Map<Long, SmsClient<?>> channelId2SmsClientMap = SmsSenderUtils.init(smsChannelAllVOList);
-        return new SmsClientAdapter(channelId2SmsClientMap);
-    }
-
-}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsSenderUtils.java b/src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsSenderUtils.java
deleted file mode 100644
index ea9c24489..000000000
--- a/src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsSenderUtils.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package cn.iocoder.dashboard.modules.system.sms;
-
-import cn.hutool.core.util.ObjectUtil;
-import cn.iocoder.dashboard.common.enums.SmsChannelEnum;
-import cn.iocoder.dashboard.common.exception.ServiceException;
-import cn.iocoder.dashboard.framework.sms.SmsBody;
-import cn.iocoder.dashboard.framework.sms.SmsClient;
-import cn.iocoder.dashboard.framework.sms.SmsClientAdapter;
-import cn.iocoder.dashboard.framework.sms.SmsResult;
-import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
-import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO;
-import cn.iocoder.dashboard.modules.system.sms.client.AliSmsClient;
-import cn.iocoder.dashboard.modules.system.sms.proxy.SmsClientLogProxy;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-
-import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
-
-/**
- * 短信发送者工厂
- *
- * @author zzf
- * @date 2021/1/25 16:18
- */
-public class SmsSenderUtils {
-
-    /**
-     * 短信渠道id:短信客户端map
-     * key: channelId
-     * val: SmsClient
-     */
-    private static final Map<Long, SmsClient<?>> smsSenderMap = new ConcurrentHashMap<>(8);
-
-    /**
-     * 短信模板code: 短信渠道id map
-     * key: templateCode
-     * val: channelId
-     */
-    private static final Map<String, Long> templateCode2ChannelIdMap = new HashMap<>();
-
-    /**
-     * 将短信渠道信息初始化成短信客户端
-     *
-     * @param smsChannelAllVOList 短信渠道信息
-     * @return 短信渠道id:短信客户端map
-     */
-    public synchronized static Map<Long, SmsClient<?>> init(List<SmsChannelAllVO> smsChannelAllVOList) {
-        if (ObjectUtil.isEmpty(smsChannelAllVOList)) {
-            throw new ServiceException(SMS_CHANNEL_NOT_FOUND);
-        }
-        addSender(smsChannelAllVOList);
-        return smsSenderMap;
-    }
-
-    /**
-     * 重置短信客户端信息
-     *
-     * @param smsClientAdapter    短信客户端适配器
-     * @param smsChannelAllVOList 短信渠道信息集合
-     */
-    public synchronized static void flush(SmsClientAdapter smsClientAdapter, List<SmsChannelAllVO> smsChannelAllVOList) {
-        smsSenderMap.clear();
-        smsClientAdapter.flushClient(init(smsChannelAllVOList));
-    }
-
-    /**
-     * 发送短信
-     *
-     * @param smsClientAdapter 短信客户端适配器
-     * @param smsBody          短信内容
-     * @param targetPhones     对象手机集合
-     * @return 短信发送结果
-     */
-    public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, Collection<String> targetPhones) {
-        Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode());
-        if (channelId == null) {
-            throw new ServiceException(SMS_SENDER_NOT_FOUND);
-        }
-        return smsClientAdapter.send(channelId, smsBody, targetPhones);
-    }
-
-    /**
-     * 发送短信
-     *
-     * @param smsClientAdapter 短信客户端适配器
-     * @param smsBody          短信内容
-     * @param targetPhone      对象手机
-     * @return 短信发送结果
-     */
-    public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, String targetPhone) {
-        Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode());
-        if (channelId == null) {
-            throw new ServiceException(SMS_SENDER_NOT_FOUND);
-        }
-        return smsClientAdapter.send(channelId, smsBody, Collections.singletonList(targetPhone));
-    }
-
-    /**
-     * 发送短信
-     *
-     * @param smsClientAdapter 短信客户端适配器
-     * @param smsBody          短信内容
-     * @param targetPhones     对象手机数组
-     * @return 短信发送结果
-     */
-    public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, String... targetPhones) {
-        Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode());
-        if (channelId == null) {
-            throw new ServiceException(SMS_SENDER_NOT_FOUND);
-        }
-        return smsClientAdapter.send(channelId, smsBody, Arrays.asList(targetPhones));
-    }
-
-
-    private static void addSender(List<SmsChannelAllVO> smsChannelAllVOList) {
-        smsChannelAllVOList.forEach(channelAllVO -> addSender(SmsChannelEnum.getByCode(channelAllVO.getCode()), channelAllVO));
-    }
-
-    private static void addSender(SmsChannelEnum channelEnum, SmsChannelAllVO channelAllVO) {
-        if (channelEnum == null) {
-            throw new ServiceException(INVALID_CHANNEL_CODE);
-        }
-        List<SmsTemplateVO> templateList = channelAllVO.getTemplateList();
-        if (ObjectUtil.isEmpty(templateList)) {
-            throw new ServiceException(SMS_TEMPLATE_NOT_FOUND);
-        }
-        SmsClient<?> aliSmsClient = getSender(channelEnum, channelAllVO);
-        templateList.forEach(smsTemplateVO -> templateCode2ChannelIdMap.put(smsTemplateVO.getCode(), channelAllVO.getId()));
-        smsSenderMap.put(channelAllVO.getId(), aliSmsClient);
-    }
-
-    private static SmsClient<?> getSender(SmsChannelEnum channelEnum, SmsChannelAllVO channelAllVO) {
-        switch (channelEnum) {
-            case ALI:
-                return new SmsClientLogProxy<>(new AliSmsClient(channelAllVO));
-            // TODO fill more channel
-            default:
-                break;
-        }
-        throw new ServiceException(SMS_SENDER_NOT_FOUND);
-    }
-}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/sms/proxy/SmsClientLogProxy.java b/src/main/java/cn/iocoder/dashboard/modules/system/sms/proxy/SmsClientLogProxy.java
deleted file mode 100644
index a7d201e6f..000000000
--- a/src/main/java/cn/iocoder/dashboard/modules/system/sms/proxy/SmsClientLogProxy.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package cn.iocoder.dashboard.modules.system.sms.proxy;
-
-import cn.iocoder.dashboard.framework.sms.SmsBody;
-import cn.iocoder.dashboard.framework.sms.SmsClient;
-import cn.iocoder.dashboard.framework.sms.SmsResult;
-import cn.iocoder.dashboard.util.json.JsonUtils;
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.Collection;
-
-/**
- * 消息父接口
- *
- * @author zzf
- * @date 2021/1/22 15:46
- */
-@Slf4j
-public class SmsClientLogProxy<R> implements SmsClient<R> {
-
-    private final SmsClient<R> smsClient;
-
-    @Override
-    public SmsResult<R> send(SmsBody msgBody, Collection<String> targets) {
-        log.debug("ready send sms, body: {}, target: {}", JsonUtils.toJsonString(msgBody), targets);
-
-        SmsResult<R> resultBody = smsClient.send(msgBody, targets);
-
-        if (resultBody.getSuccess()) {
-            //
-        } else {
-            log.warn("send sms fail, body: {}, target: {}, resultBody: {}",
-                    JsonUtils.toJsonString(msgBody),
-                    targets,
-                    JsonUtils.toJsonString(resultBody)
-            );
-        }
-        return resultBody;
-    }
-
-    @Override
-    public SmsResult<R> sendAsync(SmsBody msgBody, Collection<String> targets) {
-        return send(msgBody, targets);
-    }
-
-    public SmsClientLogProxy(SmsClient<R> smsClient) {
-        this.smsClient = smsClient;
-    }
-}
diff --git a/src/main/java/cn/iocoder/dashboard/util/string/StrUtils.java b/src/main/java/cn/iocoder/dashboard/util/string/StrUtils.java
index 5e98d915b..e0ca605a4 100644
--- a/src/main/java/cn/iocoder/dashboard/util/string/StrUtils.java
+++ b/src/main/java/cn/iocoder/dashboard/util/string/StrUtils.java
@@ -1,7 +1,10 @@
 package cn.iocoder.dashboard.util.string;
 
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 
+import java.util.Map;
+
 /**
  * 字符串工具类
  *
@@ -13,4 +16,22 @@ public class StrUtils {
         return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好
     }
 
+    /**
+     * 指定字符串的
+     * @param str
+     * @param replaceMap
+     * @return
+     */
+    public static String replace(String str, Map<String, String> replaceMap) {
+        assert StrUtil.isNotBlank(str);
+        if (ObjectUtil.isEmpty(replaceMap)) {
+            return str;
+        }
+        String result = null;
+        for (String key : replaceMap.keySet()) {
+            result = str.replace(key, replaceMap.get(key));
+        }
+        return result;
+    }
+
 }