From d843d6a5a8329e89c0fa8f9e9f982b89091d0852 Mon Sep 17 00:00:00 2001
From: YunaiV <zhijiantianya@gmail.com>
Date: Sat, 3 Apr 2021 22:05:11 +0800
Subject: [PATCH] =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E6=8F=90=E4=BA=A4=202021-04-?=
 =?UTF-8?q?03=EF=BC=8C=E9=87=8D=E6=9E=84=E8=BF=94=E5=9B=9E=E7=9A=84?=
 =?UTF-8?q?=E7=BB=93=E6=9E=9C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../dashboard/common/core/KeyValue.java       | 20 +++++
 .../dashboard/common/pojo/CommonResult.java   |  7 +-
 .../framework/sms/core/client/SmsClient.java  |  7 +-
 .../sms/core/client/dto/SmsSendRespDTO.java   |  2 +-
 .../core/client/impl/AbstractSmsClient.java   | 16 ++--
 .../client/impl/aliyun/AliyunSmsClient.java   |  6 +-
 .../impl/aliyun/AliyunSmsCodeMapping.java     |  3 +-
 .../client/impl/yunpian/YunpianSmsClient.java |  7 +-
 .../enums/SmsFrameworkErrorCodeConstants.java |  1 -
 .../controller/sms/SmsChannelController.java  |  8 --
 .../sms/vo/resp/SmsChannelEnumRespVO.java     | 21 -----
 .../system/convert/sms/SmsChannelConvert.java |  4 -
 .../dal/dataobject/sms/SysSmsChannelDO.java   |  6 +-
 ...{SysSmsSendLogDO.java => SysSmsLogDO.java} | 43 ++++++-----
 .../dal/dataobject/sms/SysSmsTemplateDO.java  | 10 ++-
 ...endLogMapper.java => SysSmsLogMapper.java} |  4 +-
 .../enums/sms/SysSmsSendStatusEnum.java       |  1 +
 .../mq/message/sms/SysSmsSendMessage.java     | 11 +--
 .../mq/producer/sms/SysSmsProducer.java       | 12 +--
 .../service/sms/SysSmsChannelService.java     | 14 +---
 .../system/service/sms/SysSmsLogService.java  | 44 +++++++++++
 .../service/sms/SysSmsSendLogService.java     | 49 ------------
 .../sms/impl/SysSmsChannelServiceImpl.java    | 16 +---
 .../sms/impl/SysSmsSendLogServiceImpl.java    | 36 +++++----
 .../service/sms/impl/SysSmsServiceImpl.java   | 77 ++++++++++---------
 .../dashboard/util/collection/MapUtils.java   |  8 ++
 .../impl/aliyun/AliyunSmsClientTest.java      | 12 +--
 .../YunpianSmsClientIntegrationTest.java      | 14 ++--
 28 files changed, 232 insertions(+), 227 deletions(-)
 create mode 100644 src/main/java/cn/iocoder/dashboard/common/core/KeyValue.java
 delete mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/resp/SmsChannelEnumRespVO.java
 rename src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/{SysSmsSendLogDO.java => SysSmsLogDO.java} (76%)
 rename src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/{SysSmsSendLogMapper.java => SysSmsLogMapper.java} (73%)
 create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogService.java
 delete mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java

diff --git a/src/main/java/cn/iocoder/dashboard/common/core/KeyValue.java b/src/main/java/cn/iocoder/dashboard/common/core/KeyValue.java
new file mode 100644
index 000000000..57fe08b5a
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/common/core/KeyValue.java
@@ -0,0 +1,20 @@
+package cn.iocoder.dashboard.common.core;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * Key Value 的键值对
+ *
+ * @author 芋道源码
+ */
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class KeyValue<K, V> {
+
+    private K key;
+    private V value;
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/common/pojo/CommonResult.java b/src/main/java/cn/iocoder/dashboard/common/pojo/CommonResult.java
index 73b4c7a76..0a56d334a 100644
--- a/src/main/java/cn/iocoder/dashboard/common/pojo/CommonResult.java
+++ b/src/main/java/cn/iocoder/dashboard/common/pojo/CommonResult.java
@@ -8,6 +8,7 @@ import lombok.Data;
 import org.springframework.util.Assert;
 
 import java.io.Serializable;
+import java.util.Objects;
 
 /**
  * 通用返回
@@ -67,9 +68,13 @@ public class CommonResult<T> implements Serializable {
         return result;
     }
 
+    public static boolean isSuccess(Integer code) {
+        return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.getCode());
+    }
+
     @JsonIgnore // 避免 jackson 序列化
     public boolean isSuccess() {
-        return GlobalErrorCodeConstants.SUCCESS.getCode().equals(code);
+        return isSuccess(GlobalErrorCodeConstants.SUCCESS.getCode());
     }
 
     @JsonIgnore // 避免 jackson 序列化
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java
index 5028e1b0f..f343b540f 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java
@@ -1,10 +1,11 @@
 package cn.iocoder.dashboard.framework.sms.core.client;
 
+import cn.iocoder.dashboard.common.core.KeyValue;
 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsResultDetail;
 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
 
 import javax.servlet.ServletRequest;
-import java.util.Map;
+import java.util.List;
 
 /**
  * 短信客户端接口
@@ -24,13 +25,13 @@ public interface SmsClient {
     /**
      * 发送消息
      *
-     * @param sendLogId 发送日志编号
+     * @param logId 日志编号
      * @param mobile 手机号
      * @param apiTemplateId 短信 API 的模板编号
      * @param templateParams 短信模板参数
      * @return 短信发送结果
      */
-    SmsCommonResult<SmsSendRespDTO> send(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams);
+    SmsCommonResult<SmsSendRespDTO> send(Long logId, String mobile, String apiTemplateId, List<KeyValue<String, Object>> templateParams);
 
     // TODO FROM 芋艿 to ZZF:是不是可以改成意图更明确的解析返回结果,例如说 parseXXXX
     /**
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsSendRespDTO.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsSendRespDTO.java
index 0f6777a3f..c3f6b51ae 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsSendRespDTO.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/dto/SmsSendRespDTO.java
@@ -3,7 +3,7 @@ package cn.iocoder.dashboard.framework.sms.core.client.dto;
 import lombok.Data;
 
 /**
- * 短信发送响应 DTO
+ * 短信发送 Response DTO
  *
  * @author 芋道源码
  */
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java
index 1c2dce0e6..6e334cf0a 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java
@@ -1,13 +1,14 @@
 package cn.iocoder.dashboard.framework.sms.core.client.impl;
 
+import cn.iocoder.dashboard.common.core.KeyValue;
+import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
 import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping;
 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
-import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
 import lombok.extern.slf4j.Slf4j;
 
-import java.util.Map;
+import java.util.List;
 
 /**
  * 短信客户端抽象类
@@ -67,16 +68,16 @@ public abstract class AbstractSmsClient implements SmsClient {
     }
 
     @Override
-    public final SmsCommonResult<SmsSendRespDTO> send(Long sendLogId, String mobile,
-                                                      String apiTemplateId, Map<String, Object> templateParams) {
+    public final SmsCommonResult<SmsSendRespDTO> send(Long logId, String mobile,
+                                                      String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
         // 执行短信发送
         SmsCommonResult<SmsSendRespDTO> result;
         try {
-            result = doSend(sendLogId, mobile, apiTemplateId, templateParams);
+            result = doSend(logId, mobile, apiTemplateId, templateParams);
         } catch (Throwable ex) {
             // 打印异常日志
             log.error("[send][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]",
-                    sendLogId, mobile, apiTemplateId, templateParams, ex);
+                    logId, mobile, apiTemplateId, templateParams, ex);
             // 封装返回
             return SmsCommonResult.error(ex);
         }
@@ -93,6 +94,7 @@ public abstract class AbstractSmsClient implements SmsClient {
      * @return 短信发送结果
      */
     protected abstract SmsCommonResult<SmsSendRespDTO> doSend(Long sendLogId, String mobile,
-                                                              String apiTemplateId, Map<String, Object> templateParams) throws Throwable;
+                                                              String apiTemplateId, List<KeyValue<String, Object>> templateParams)
+            throws Throwable;
 
 }
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java
index 4dbaffbf0..acc8a0193 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java
@@ -3,12 +3,14 @@ package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun;
 import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.dashboard.common.core.KeyValue;
 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsResultDetail;
 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient;
 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
+import cn.iocoder.dashboard.util.collection.MapUtils;
 import cn.iocoder.dashboard.util.json.JsonUtils;
 import com.aliyuncs.DefaultAcsClient;
 import com.aliyuncs.IAcsClient;
@@ -60,14 +62,14 @@ public class AliyunSmsClient extends AbstractSmsClient {
 
     @Override
     protected SmsCommonResult<SmsSendRespDTO> doSend(Long sendLogId, String mobile,
-                                                     String apiTemplateId, Map<String, Object> templateParams) throws Throwable {
+                                                     String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
         // 构建参数
         SendSmsRequest request = new SendSmsRequest();
         request.setSysMethod(MethodType.POST);
         request.setPhoneNumbers(mobile);
         request.setSignName(properties.getSignature());
         request.setTemplateCode(apiTemplateId);
-        request.setTemplateParam(JsonUtils.toJsonString(templateParams));
+        request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams)));
         request.setOutId(String.valueOf(sendLogId));
 
         try {
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java
index d11be8c1b..8a804de0c 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java
@@ -1,6 +1,7 @@
 package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun;
 
 import cn.iocoder.dashboard.common.exception.ErrorCode;
+import cn.iocoder.dashboard.common.exception.enums.GlobalErrorCodeConstants;
 import cn.iocoder.dashboard.framework.sms.core.client.SmsCodeMapping;
 
 import static cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.*;
@@ -15,7 +16,7 @@ public class AliyunSmsCodeMapping implements SmsCodeMapping {
     @Override
     public ErrorCode apply(String apiCode) {
         switch (apiCode) {
-            case "OK": return null;
+            case "OK": return GlobalErrorCodeConstants.SUCCESS;
             case "MissingAccessKeyId": return SMS_CHANNEL_API_KEY_MISSING;
             case "isp.RAM_PERMISSION_DENY": return SMS_CHANNEL_PERMISSION_DENY;
             case "isv.INVALID_PARAMETERS": return SMS_API_PARAM_ERROR;
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java
index 503aa32d5..249d8c481 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java
@@ -6,6 +6,7 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.CharsetUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.URLUtil;
+import cn.iocoder.dashboard.common.core.KeyValue;
 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsResultDetail;
 import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
@@ -65,7 +66,7 @@ public class YunpianSmsClient extends AbstractSmsClient {
 
     @Override
     protected SmsCommonResult<SmsSendRespDTO> doSend(Long sendLogId, String mobile,
-                                                     String apiTemplateId, Map<String, Object> templateParams) throws Throwable {
+                                                     String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
         // 构建参数
         Map<String, String> request = new HashMap<>();
         request.put(YunpianConstant.APIKEY, properties.getApiKey());
@@ -89,13 +90,13 @@ public class YunpianSmsClient extends AbstractSmsClient {
                 data, codeMapping);
     }
 
-    private static String formatTplValue(Map<String, Object> templateParams) {
+    private static String formatTplValue(List<KeyValue<String, Object>> templateParams) {
         if (CollUtil.isEmpty(templateParams)) {
             return "";
         }
         // 参考 https://www.yunpian.com/official/document/sms/zh_cn/introduction_demos_encode_sample 格式化
         StringJoiner joiner = new StringJoiner("&");
-        templateParams.forEach((key, value) -> joiner.add(String.format("#%s#=%s", key, URLUtil.encode(String.valueOf(value)))));
+        templateParams.forEach(param -> joiner.add(String.format("#%s#=%s", param.getKey(), URLUtil.encode(String.valueOf(param.getValue())))));
         return joiner.toString();
     }
 
diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsFrameworkErrorCodeConstants.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsFrameworkErrorCodeConstants.java
index f0cbb762f..741410944 100644
--- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsFrameworkErrorCodeConstants.java
+++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsFrameworkErrorCodeConstants.java
@@ -12,7 +12,6 @@ import cn.iocoder.dashboard.common.exception.ErrorCode;
 public interface SmsFrameworkErrorCodeConstants {
 
     // ========== 渠道相关 2001000100 ==========
-    ErrorCode SMS_CHANNEL_CLIENT_NOT_EXISTS = new ErrorCode(2001000100, "短信渠道的客户端不存在");
     ErrorCode SMS_CHANNEL_API_KEY_MISSING = new ErrorCode(2001000101, "API Key 不存在");
     ErrorCode SMS_CHANNEL_PERMISSION_DENY = new ErrorCode(2001000102, "没有发送短信的权限");
 
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 415e47405..3d6ccc575 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
@@ -4,7 +4,6 @@ import cn.iocoder.dashboard.common.pojo.CommonResult;
 import cn.iocoder.dashboard.common.pojo.PageResult;
 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;
 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
 import io.swagger.annotations.Api;
@@ -13,7 +12,6 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
-import java.util.List;
 
 import static cn.iocoder.dashboard.common.pojo.CommonResult.success;
 
@@ -31,12 +29,6 @@ public class SmsChannelController {
         return success(service.pageSmsChannels(reqVO));
     }
 
-    @ApiOperation("获取渠道枚举")
-    @GetMapping("/list/channel-enum")
-    public CommonResult<List<SmsChannelEnumRespVO>> getChannelEnums() {
-        return success(service.getSmsChannelEnums());
-    }
-
     @ApiOperation("添加消息渠道")
     @PostMapping("/create")
     public CommonResult<Long> add(@Validated @RequestBody SmsChannelCreateReqVO reqVO) {
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/resp/SmsChannelEnumRespVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/resp/SmsChannelEnumRespVO.java
deleted file mode 100644
index cb156781d..000000000
--- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/resp/SmsChannelEnumRespVO.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package cn.iocoder.dashboard.modules.system.controller.sms.vo.resp;
-
-import io.swagger.annotations.ApiModel;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.NoArgsConstructor;
-
-import java.io.Serializable;
-
-
-@ApiModel("用户分页 Request VO")
-@Data
-@NoArgsConstructor
-@EqualsAndHashCode
-public class SmsChannelEnumRespVO implements Serializable {
-
-    private String code;
-
-    private String name;
-
-}
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 43d72b471..c8a0f7591 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
@@ -1,10 +1,8 @@
 package cn.iocoder.dashboard.modules.system.convert.sms;
 
-import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
-import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
 import org.mapstruct.Mapper;
@@ -21,8 +19,6 @@ public interface SmsChannelConvert {
 
     SysSmsChannelDO convert(SysUserUpdateReqVO bean);
 
-    List<SmsChannelEnumRespVO> convertEnum(List<SmsChannelEnum> bean);
-
     List<SmsChannelAllVO> convert(List<SysSmsChannelDO> bean);
 
     List<SmsChannelProperties> convertProperty(List<SmsChannelAllVO> list);
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java
index 4bca7e517..7b0b3f072 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java
@@ -6,16 +6,18 @@ import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
 import com.baomidou.mybatisplus.annotation.TableName;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import lombok.ToString;
 
 /**
- * 短信渠道
+ * 短信渠道 DO
  *
  * @author zzf
  * @since 2021-01-25
  */
-@TableName(value = "sms_channel", autoResultMap = true)
+@TableName(value = "sys_sms_channel", autoResultMap = true)
 @Data
 @EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
 public class SysSmsChannelDO extends BaseDO {
 
     /**
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsLogDO.java
similarity index 76%
rename from src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDO.java
rename to src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsLogDO.java
index 53e725634..44d0da639 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsLogDO.java
@@ -2,28 +2,30 @@ package cn.iocoder.dashboard.modules.system.dal.dataobject.sms;
 
 import cn.iocoder.dashboard.common.enums.UserTypeEnum;
 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum;
+import cn.iocoder.dashboard.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import lombok.*;
 
 import java.util.Date;
 import java.util.Map;
 
 /**
- * 短信发送日志
+ * 短信日志 DO
  *
  * @author zzf
  * @since 2021-01-25
  */
-@TableName(value = "sms_send_log", autoResultMap = true)
+@TableName(value = "sys_sms_log", autoResultMap = true)
 @Data
 @EqualsAndHashCode(callSuper = true)
 @ToString(callSuper = true)
 @AllArgsConstructor
 @NoArgsConstructor
 @Builder
-public class SysSmsSendLogDO extends BaseDO {
+public class SysSmsLogDO extends BaseDO {
 
     /**
      * 自增编号
@@ -48,7 +50,7 @@ public class SysSmsSendLogDO extends BaseDO {
     // ========= 模板相关字段 =========
 
     /**
-     * 短信模板编号
+     * 模板编号
      *
      * 关联 {@link SysSmsTemplateDO#getId()}
      */
@@ -70,8 +72,9 @@ public class SysSmsSendLogDO extends BaseDO {
      */
     private String templateContent;
     /**
-     * 基于 {@link SysSmsTemplateDO#getParams()} 输入后的内容
+     * 基于 {@link SysSmsTemplateDO#getParams()} 输入后的参数
      */
+    @TableField(typeHandler = JacksonTypeHandler.class)
     private Map<String, Object> templateParams;
     /**
      * 短信 API 的模板编号
@@ -106,32 +109,32 @@ public class SysSmsSendLogDO extends BaseDO {
      */
     private Integer sendStatus;
     /**
-     * 时间发送时间
+     * 发送时间
      */
     private Date sendTime;
     /**
-     * 发送失败的类型
+     * 发送结果的编码
      *
-     * 枚举 {@link SmsSendFailureTypeEnum#getType()}
+     * 枚举 {@link SmsFrameworkErrorCodeConstants}
      */
-    private Integer sendFailureType;
+    private Integer sendCode;
     /**
-     * 发送失败的提示
+     * 发送结果的提示
      *
-     * 一般情况下,使用 {@link SmsSendFailureTypeEnum#getMsg()}
+     * 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants}
      * 异常情况下,通过格式化 Exception 的提示存储
      */
-    private String sendFailureMsg;
+    private String sendMsg;
     /**
-     * 短信 API 发送失败的类型
+     * 短信 API 发送结果的编码
      *
      * 由于第三方的错误码可能是字符串,所以使用 String 类型
      */
-    private String apiSendFailureType;
+    private String apiSendCode;
     /**
      * 短信 API 发送失败的提示
      */
-    private String apiSendFailureMsg;
+    private String apiSendMsg;
     /**
      * 短信 API 发送返回的唯一请求 ID
      *
@@ -147,9 +150,9 @@ public class SysSmsSendLogDO extends BaseDO {
 
     // ========= 接收相关字段 =========
 
-    /**
-     * 是否获取过结果[0否 1是]
-     */
-    private Integer gotResult;
+//    /**
+//     * 是否获取过结果[0否 1是]
+//     */
+//    private Integer gotResult;
 
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsTemplateDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsTemplateDO.java
index ba2b9940e..9316358df 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsTemplateDO.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsTemplateDO.java
@@ -8,18 +8,20 @@ import com.baomidou.mybatisplus.annotation.TableName;
 import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
 import lombok.Data;
 import lombok.EqualsAndHashCode;
+import lombok.ToString;
 
 import java.util.List;
 
 /**
- * 短信模板
+ * 短信模板 DO
  *
  * @author zzf
  * @since 2021-01-25
  */
+@TableName(value = "sys_sms_template", autoResultMap = true)
 @Data
 @EqualsAndHashCode(callSuper = true)
-@TableName(value = "sms_template", autoResultMap = true)
+@ToString(callSuper = true)
 public class SysSmsTemplateDO extends BaseDO {
 
     /**
@@ -46,11 +48,11 @@ public class SysSmsTemplateDO extends BaseDO {
      */
     private String code;
     /**
-     * 名称
+     * 模板名称
      */
     private String name;
     /**
-     * 内容
+     * 模板内容
      *
      * 内容的参数,使用 {} 包括,例如说 {name}
      */
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsSendLogMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsLogMapper.java
similarity index 73%
rename from src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsSendLogMapper.java
rename to src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsLogMapper.java
index 74d713d48..2255c444e 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsSendLogMapper.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsLogMapper.java
@@ -1,9 +1,9 @@
 package cn.iocoder.dashboard.modules.system.dal.mysql.sms;
 
 import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
-import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO;
 import org.apache.ibatis.annotations.Mapper;
 
 @Mapper
-public interface SysSmsSendLogMapper extends BaseMapperX<SysSmsSendLogDO> {
+public interface SysSmsLogMapper extends BaseMapperX<SysSmsLogDO> {
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SysSmsSendStatusEnum.java b/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SysSmsSendStatusEnum.java
index 083b11c98..1d505ee02 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SysSmsSendStatusEnum.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SysSmsSendStatusEnum.java
@@ -16,6 +16,7 @@ public enum SysSmsSendStatusEnum {
     INIT(0), // 初始化
     SUCCESS(10), // 发送成功
     FAILURE(20), // 发送失败
+    IGNORE(30), // 忽略,即不发送
     ;
 
     private final int status;
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsSendMessage.java b/src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsSendMessage.java
index 40cc80ba5..9bb30514a 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsSendMessage.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SysSmsSendMessage.java
@@ -1,10 +1,11 @@
 package cn.iocoder.dashboard.modules.system.mq.message.sms;
 
+import cn.iocoder.dashboard.common.core.KeyValue;
 import cn.iocoder.dashboard.framework.redis.core.stream.StreamMessage;
 import lombok.Data;
 
 import javax.validation.constraints.NotNull;
-import java.util.Map;
+import java.util.List;
 
 /**
  * 短信发送消息
@@ -15,10 +16,10 @@ import java.util.Map;
 public class SysSmsSendMessage implements StreamMessage {
 
     /**
-     * 发送日志编号
+     * 短信日志编号
      */
-    @NotNull(message = "发送日志编号不能为空")
-    private Long sendLogId;
+    @NotNull(message = "短信日志编号不能为空")
+    private Long logId;
     /**
      * 手机号
      */
@@ -37,7 +38,7 @@ public class SysSmsSendMessage implements StreamMessage {
     /**
      * 短信模板参数
      */
-    private Map<String, Object> templateParams;
+    private List<KeyValue<String, Object>> templateParams;
 
     @Override
     public String getStreamKey() {
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SysSmsProducer.java b/src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SysSmsProducer.java
index 257de549c..8e72bcf38 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SysSmsProducer.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SysSmsProducer.java
@@ -1,5 +1,6 @@
 package cn.iocoder.dashboard.modules.system.mq.producer.sms;
 
+import cn.iocoder.dashboard.common.core.KeyValue;
 import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils;
 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage;
 import lombok.extern.slf4j.Slf4j;
@@ -7,7 +8,7 @@ import org.springframework.data.redis.core.StringRedisTemplate;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.Resource;
-import java.util.Map;
+import java.util.List;
 
 /**
  * 短信发送流消息监听器
@@ -25,16 +26,15 @@ public class SysSmsProducer {
     /**
      * 发送短信 Message
      *
-     * @param sendLogId 发送日志编号
+     * @param logId 短信日志编号
      * @param mobile 手机号
      * @param channelId 渠道编号
      * @param apiTemplateId 短信模板编号
      * @param templateParams 短信模板参数
-     * @param sendLogId 发送日志编号
      */
-    public void sendSmsSendMessage(Long sendLogId, String mobile,
-                                   Long channelId, String apiTemplateId, Map<String, Object> templateParams) {
-        SysSmsSendMessage message = new SysSmsSendMessage().setSendLogId(sendLogId).setMobile(mobile);
+    public void sendSmsSendMessage(Long logId, String mobile,
+                                   Long channelId, String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
+        SysSmsSendMessage message = new SysSmsSendMessage().setLogId(logId).setMobile(mobile);
         message.setChannelId(channelId).setApiTemplateId(apiTemplateId).setTemplateParams(templateParams);
         RedisMessageUtils.sendStreamMessage(stringRedisTemplate, message);
     }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java
index e720e363f..142812b3c 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java
@@ -3,11 +3,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.req.SmsChannelCreateReqVO;
 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
-import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
 
-import java.util.List;
-
 /**
  * 短信渠道Service接口
  *
@@ -17,9 +14,9 @@ import java.util.List;
 public interface SysSmsChannelService {
 
     /**
-     * 初始化短信渠道并缓存短信模板信息
+     * 初始化短信客户端
      */
-    void initSmsClientAndCacheSmsTemplate();
+    void initSmsClients();
 
     /**
      * 分页查询短信渠道信息
@@ -37,11 +34,4 @@ public interface SysSmsChannelService {
      */
     Long createSmsChannel(SmsChannelCreateReqVO reqVO);
 
-    /**
-     * 获取短信渠道枚举/渠道编码
-     *
-     * @return 短信渠道枚举/渠道编码
-     */
-    List<SmsChannelEnumRespVO> getSmsChannelEnums();
-
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogService.java
new file mode 100644
index 000000000..fd04a62b5
--- /dev/null
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogService.java
@@ -0,0 +1,44 @@
+package cn.iocoder.dashboard.modules.system.service.sms;
+
+import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
+
+import java.util.Map;
+
+/**
+ * 短信日志服务接口
+ *
+ * @author zzf
+ * @date 13:48 2021/3/2
+ */
+public interface SysSmsLogService {
+
+    /**
+     * 创建短信日志
+     *
+     * @param mobile 手机号
+     * @param userId 用户编号
+     * @param userType 用户类型
+     * @param isSend 是否发送
+     * @param template 短信模板
+     * @param templateContent 短信内容
+     * @param templateParams 短信参数
+     * @return 发送日志编号
+     */
+    Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend,
+                      SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams);
+
+    /**
+     * 更新发送日志的结果
+     *
+     * @param id 日志编号
+     * @param sendCode 发送结果的编码
+     * @param sendMsg 发送结果的提示
+     * @param apiSendCode 短信 API 发送结果的编码
+     * @param apiSendMsg 短信 API 发送失败的提示
+     * @param apiRequestId 短信 API 发送返回的唯一请求 ID
+     * @param apiSerialNo 短信 API 发送返回的序号
+     */
+    void updateSmsSendResult(Long id, Integer sendCode, String sendMsg,
+                             String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo);
+
+}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java
deleted file mode 100644
index 7c91b55cb..000000000
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package cn.iocoder.dashboard.modules.system.service.sms;
-
-import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
-
-import java.util.Map;
-
-/**
- * 短信发送日志服务接口
- *
- * @author zzf
- * @date 13:48 2021/3/2
- */
-public interface SysSmsSendLogService {
-
-    /**
-     * 创建发送日志
-     *
-     * @param mobile 手机号
-     * @param userId 用户编号
-     * @param userType 用户类型
-     * @param template 短信模板
-     * @param templateContent 短信内容
-     * @param templateParams 短信参数
-     * @return
-     */
-    Long createSmsSendLog(String mobile, Long userId, Integer userType,
-                          SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams);
-
-    /**
-     * 更新发送日志的结果
-     *
-     * @param id 日志编号
-     * @param success 是否成功
-     * @param sendFailureType 发送失败的类型
-     * @param sendFailureMsg 发送失败的提示
-     * @param apiSendFailureType 短信 API 发送失败的类型
-     * @param apiSendFailureMsg 短信 API 发送失败的提示
-     * @param apiRequestId 短信 API 发送返回的唯一请求 ID
-     * @param apiSerialNo 短信 API 发送返回的序号
-     */
-    void updateSmsSendLogResult(Long id, Boolean success, Integer sendFailureType, String sendFailureMsg,
-                                String apiSendFailureType, String apiSendFailureMsg, String apiRequestId, String apiSerialNo);
-
-    default void updateSmsSendLogFailure(Long id, SmsSendFailureTypeEnum sendFailureType) {
-        updateSmsSendLogResult(id, false, sendFailureType.getType(), sendFailureType.getMsg(),
-                null, null, null, null);
-    }
-
-}
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsChannelServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsChannelServiceImpl.java
index 930a7db9c..a34d511bb 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsChannelServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsChannelServiceImpl.java
@@ -3,21 +3,17 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl;
 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
 import cn.iocoder.dashboard.common.pojo.PageResult;
 import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
-import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
 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;
 import cn.iocoder.dashboard.modules.system.convert.sms.SmsChannelConvert;
 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
 import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsChannelMapper;
-import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsTemplateMapper;
 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.PostConstruct;
 import javax.annotation.Resource;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -35,12 +31,9 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService {
     @Resource
     private SysSmsChannelMapper channelMapper;
 
-    @Resource
-    private SysSmsTemplateMapper templateMapper;
-
     @Override
     @PostConstruct
-    public void initSmsClientAndCacheSmsTemplate() {
+    public void initSmsClients() {
         // 查询有效渠道信息
         List<SysSmsChannelDO> channelDOList = channelMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
         // 创建渠道 Client
@@ -48,6 +41,8 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService {
         propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
     }
 
+    // TODO 芋艿:刷新缓存
+
     @Override
     public PageResult<SysSmsChannelDO> pageSmsChannels(SmsChannelPageReqVO reqVO) {
         return channelMapper.selectChannelPage(reqVO);
@@ -60,11 +55,6 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService {
         return channelDO.getId();
     }
 
-    @Override
-    public List<SmsChannelEnumRespVO> getSmsChannelEnums() {
-        return SmsChannelConvert.INSTANCE.convertEnum(Arrays.asList(SmsChannelEnum.values()));
-    }
-
 //    @Override
 //    public List<SmsChannelAllVO> listSmsChannelAllEnabledInfo() {
 //        List<SysSmsChannelDO> channelDOList = channelMapper.selectListByStatus();
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java
index b57912bd4..00c43e8f1 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java
@@ -1,10 +1,11 @@
 package cn.iocoder.dashboard.modules.system.service.sms.impl;
 
-import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO;
+import cn.iocoder.dashboard.common.pojo.CommonResult;
+import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsLogDO;
 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
-import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsSendLogMapper;
+import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsLogMapper;
 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
-import cn.iocoder.dashboard.modules.system.service.sms.SysSmsSendLogService;
+import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
@@ -21,15 +22,18 @@ import java.util.Objects;
  */
 @Slf4j
 @Service
-public class SysSmsSendLogServiceImpl implements SysSmsSendLogService {
+public class SysSmsSendLogServiceImpl implements SysSmsLogService {
 
     @Resource
-    private SysSmsSendLogMapper smsSendLogMapper;
+    private SysSmsLogMapper smsLogMapper;
 
     @Override
-    public Long createSmsSendLog(String mobile, Long userId, Integer userType,
-                                 SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams) {
-        SysSmsSendLogDO.SysSmsSendLogDOBuilder logBuilder = SysSmsSendLogDO.builder();
+    public Long createSmsLog(String mobile, Long userId, Integer userType, Boolean isSend,
+                             SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams) {
+        SysSmsLogDO.SysSmsLogDOBuilder logBuilder = SysSmsLogDO.builder();
+        // 根据是否要发送,设置状态
+        logBuilder.sendStatus(Objects.equals(isSend, true) ? SysSmsSendStatusEnum.INIT.getStatus()
+                : SysSmsSendStatusEnum.IGNORE.getStatus());
         // 设置手机相关字段
         logBuilder.mobile(mobile).userId(userId).userType(userType);
         // 设置模板相关字段
@@ -39,18 +43,18 @@ public class SysSmsSendLogServiceImpl implements SysSmsSendLogService {
         logBuilder.channelId(template.getChannelId()).channelCode(template.getChannelCode());
 
         // 插入数据库
-        SysSmsSendLogDO logDO = logBuilder.build();
-        smsSendLogMapper.insert(logDO);
+        SysSmsLogDO logDO = logBuilder.build();
+        smsLogMapper.insert(logDO);
         return logDO.getId();
     }
 
     @Override
-    public void updateSmsSendLogResult(Long id, Boolean success, Integer sendFailureType, String sendFailureMsg,
-                                       String apiSendFailureType, String apiSendFailureMsg, String apiRequestId, String apiSerialNo) {
-        SysSmsSendStatusEnum sendStatus = Objects.equals(success, true) ? SysSmsSendStatusEnum.SUCCESS : SysSmsSendStatusEnum.FAILURE;
-        smsSendLogMapper.updateById(new SysSmsSendLogDO().setId(id).setSendStatus(sendStatus.getStatus()).setSendTime(new Date())
-                .setSendFailureType(sendFailureType).setSendFailureMsg(sendFailureMsg)
-                .setApiSendFailureType(apiSendFailureType).setApiSendFailureMsg(apiSendFailureMsg).setApiRequestId(apiRequestId).setApiSerialNo(apiSerialNo));
+    public void updateSmsSendResult(Long id, Integer sendCode, String sendMsg,
+                                    String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo) {
+        SysSmsSendStatusEnum sendStatus = CommonResult.isSuccess(sendCode) ? SysSmsSendStatusEnum.SUCCESS : SysSmsSendStatusEnum.FAILURE;
+        smsLogMapper.updateById(SysSmsLogDO.builder().id(id).sendStatus(sendStatus.getStatus()).sendTime(new Date())
+                .sendCode(sendCode).sendMsg(sendMsg).apiSendCode(apiSendCode).apiSendMsg(apiSendMsg)
+                .apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build());
     }
 
 }
diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java
index e722b4499..959a997b0 100644
--- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java
+++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java
@@ -1,29 +1,32 @@
 package cn.iocoder.dashboard.modules.system.service.sms.impl;
 
-import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.dashboard.common.core.KeyValue;
 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
 import cn.iocoder.dashboard.common.enums.UserTypeEnum;
-import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
 import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
 import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
-import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum;
+import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
+import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
 import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage;
 import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer;
-import cn.iocoder.dashboard.modules.system.service.sms.SysSmsSendLogService;
+import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
+import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService;
 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;
 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService;
 import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
 
 import javax.annotation.Resource;
 import javax.servlet.ServletRequest;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.stream.Collectors;
 
 import static cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
@@ -38,10 +41,12 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
 @Slf4j
 public class SysSmsServiceImpl implements SysSmsService {
 
+    @Resource
+    private SysSmsChannelService smsChannelService;
     @Resource
     private SysSmsTemplateService smsTemplateService;
     @Resource
-    private SysSmsSendLogService smsSendLogService;
+    private SysSmsLogService smsLogService;
     @Resource
     private SysSmsProducer smsProducer;
     @Resource
@@ -54,53 +59,58 @@ public class SysSmsServiceImpl implements SysSmsService {
     public void sendSingleSms(String mobile, Long userId, Integer userType,
                               String templateCode, Map<String, Object> templateParams) {
         // 校验短信模板是否合法
-        SysSmsTemplateDO template = this.checkSmsTemplateValid(templateCode, templateParams);
+        SysSmsTemplateDO template = this.checkSmsTemplateValid(templateCode);
         // 校验手机号码是否存在
         mobile = this.checkMobile(mobile, userId, userType);
 
         // 创建发送日志
+        Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()); // 如果模板被禁用,则不发送短信,只记录日志
         String content = smsTemplateService.formatSmsTemplateContent(template.getContent(), templateParams);
-        Long sendLogId = smsSendLogService.createSmsSendLog(mobile, userId, userType, template, content, templateParams);
+        Long sendLogId = smsLogService.createSmsLog(mobile, userId, userType, isSend, template, content, templateParams);
 
-        // 如果模板被禁用,则直接标记发送失败。也就说,不发短信,嘿嘿。
-        if (CommonStatusEnum.DISABLE.getStatus().equals(template.getStatus())) {
-            smsSendLogService.updateSmsSendLogFailure(sendLogId, SmsSendFailureTypeEnum.SMS_TEMPLATE_DISABLE);
+        // 发送 MQ 消息,异步执行发送短信
+        if (!isSend) {
             return;
         }
-        // 如果模板未禁用,发送 MQ 消息。目的是,异步化调用短信平台
-        smsProducer.sendSmsSendMessage(sendLogId, mobile, template.getChannelId(), template.getApiTemplateId(), templateParams);
+        List<KeyValue<String, Object>> newTemplateParams = this.buildTemplateParams(template, templateParams);
+        smsProducer.sendSmsSendMessage(sendLogId, mobile, template.getChannelId(), template.getApiTemplateId(), newTemplateParams);
     }
 
     @Override
     public void sendBatchSms(List<String> mobiles, List<Long> userIds, Integer userType,
                              String templateCode, Map<String, Object> templateParams) {
         // 校验短信模板是否存在
-        SysSmsTemplateDO template = this.checkSmsTemplateValid(templateCode, templateParams);
+        SysSmsTemplateDO template = this.checkSmsTemplateValid(templateCode);
     }
 
-    private SysSmsTemplateDO checkSmsTemplateValid(String templateCode, Map<String, Object> templateParams) {
+    private SysSmsTemplateDO checkSmsTemplateValid(String templateCode) {
         // 短信模板不存在
         SysSmsTemplateDO template = smsTemplateService.getSmsTemplateByCode(templateCode);
         if (template == null) {
             throw exception(SMS_TEMPLATE_NOT_EXISTS);
         }
-        // 参数不够
-        if (CollUtil.isNotEmpty(template.getParams())) {
-            template.getParams().forEach(param -> {
-                if (!templateParams.containsKey(param)) {
-                    throw exception(SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS);
-                }
-            });
-        }
-        // 移除多余参数
-        if (CollUtil.isEmpty(template.getParams())) {
-            templateParams.clear();
-        } else {
-            templateParams.entrySet().removeIf(entry -> !template.getParams().contains(entry.getKey()));
-        }
         return template;
     }
 
+    /**
+     * 将参数模板,处理成有序的 KeyValue 数组
+     *
+     * 原因是,部分短信平台并不是使用 key 作为参数,而是数组下标,例如说腾讯云 https://cloud.tencent.com/document/product/382/39023
+     *
+     * @param template 短信模板
+     * @param templateParams 原始参数
+     * @return 处理后的参数
+     */
+    private List<KeyValue<String, Object>> buildTemplateParams(SysSmsTemplateDO template, Map<String, Object> templateParams) {
+        return template.getParams().stream().map(key -> {
+            Object value = templateParams.get(key);
+            if (value == null) {
+                throw exception(SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, key);
+            }
+            return new KeyValue<>(key, value);
+        }).collect(Collectors.toList());
+    }
+
     private String checkMobile(String mobile, Long userId, Integer userType) {
         mobile = getMobile(mobile, userId, userType);
         if (StrUtil.isEmpty(mobile)) {
@@ -130,15 +140,12 @@ public class SysSmsServiceImpl implements SysSmsService {
     public void doSendSms(SysSmsSendMessage message) {
         // 获得渠道对应的 SmsClient 客户端
         SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId());
-        if (smsClient == null) {
-            log.error("[doSendSms][短信 message({}) 找不到对应的客户端]", message);
-            smsSendLogService.updateSmsSendLogFailure(message.getSendLogId(), SmsSendFailureTypeEnum.SMS_CHANNEL_CLIENT_NOT_EXISTS);
-            return;
-        }
-
+        Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", message.getChannelId()));
         // 发送短信
-        SmsCommonResult sendResult = smsClient.send(message.getSendLogId(), message.getMobile(),
+        SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.send(message.getLogId(), message.getMobile(),
                 message.getApiTemplateId(), message.getTemplateParams());
+        smsLogService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(),
+                sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(), sendResult.getData().getSerialNo());
     }
 
     @Override
diff --git a/src/main/java/cn/iocoder/dashboard/util/collection/MapUtils.java b/src/main/java/cn/iocoder/dashboard/util/collection/MapUtils.java
index ebe29648f..ce5805db8 100644
--- a/src/main/java/cn/iocoder/dashboard/util/collection/MapUtils.java
+++ b/src/main/java/cn/iocoder/dashboard/util/collection/MapUtils.java
@@ -2,6 +2,8 @@ package cn.iocoder.dashboard.util.collection;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
+import cn.iocoder.dashboard.common.core.KeyValue;
+import com.google.common.collect.Maps;
 import com.google.common.collect.Multimap;
 
 import java.util.ArrayList;
@@ -55,4 +57,10 @@ public class MapUtils {
         consumer.accept(value);
     }
 
+    public static <K, V> Map<K, V> convertMap(List<KeyValue<K, V>> keyValues) {
+        Map<K, V> map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size());
+        keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue()));
+        return map;
+    }
+
 }
diff --git a/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java b/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java
index 1c85031b6..baae08886 100644
--- a/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java
+++ b/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java
@@ -1,12 +1,14 @@
 package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun;
 
+import cn.iocoder.dashboard.common.core.KeyValue;
 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
+import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
 import org.junit.jupiter.api.Test;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * {@link AliyunSmsClient} 的集成测试
@@ -26,11 +28,11 @@ public class AliyunSmsClientTest {
         AliyunSmsClient smsClient = new AliyunSmsClient(properties);
         smsClient.init();
         // 发送短信
-        Map<String, Object> templateParams = new HashMap<>();
-        templateParams.put("code", "1024");
+        List<KeyValue<String, Object>> templateParams = new ArrayList<>();
+        templateParams.add(new KeyValue<>("code", "1024"));
 //        templateParams.put("operation", "嘿嘿");
 //        SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams);
-        SmsCommonResult result = smsClient.send(1L, "15601691399", "SMS_207945135", templateParams);
+        SmsCommonResult<SmsSendRespDTO> result = smsClient.send(1L, "15601691399", "SMS_207945135", templateParams);
         System.out.println(result);
     }
 
diff --git a/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java b/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java
index 028cd6c77..6e4d4315c 100644
--- a/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java
+++ b/src/test-integration/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClientIntegrationTest.java
@@ -1,12 +1,14 @@
 package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian;
 
+import cn.iocoder.dashboard.common.core.KeyValue;
 import cn.iocoder.dashboard.framework.sms.core.client.SmsCommonResult;
+import cn.iocoder.dashboard.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
 import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
 import org.junit.jupiter.api.Test;
 
-import java.util.HashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * {@link YunpianSmsClient} 的集成测试
@@ -25,11 +27,11 @@ public class YunpianSmsClientIntegrationTest {
         YunpianSmsClient smsClient = new YunpianSmsClient(properties);
         smsClient.init();
         // 发送短信
-        Map<String, Object> templateParams = new HashMap<>();
-        templateParams.put("code", "1024");
-        templateParams.put("operation", "嘿嘿");
+        List<KeyValue<String, Object>> templateParams = new ArrayList<>();
+        templateParams.add(new KeyValue<>("code", "1024"));
+        templateParams.add(new KeyValue<>("operation", "嘿嘿"));
 //        SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams);
-        SmsCommonResult result = smsClient.send(1L, "15601691399", "4383920", templateParams);
+        SmsCommonResult<SmsSendRespDTO> result = smsClient.send(1L, "15601691399", "4383920", templateParams);
         System.out.println(result);
     }