Browse Source

sms:移除 SmsCodeMapping + SmsCommonResult,简化短信的封装

YunaiV 1 year ago
parent
commit
6f135303d8
25 changed files with 326 additions and 828 deletions
  1. 3 3
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsClient.java
  2. 0 17
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCodeMapping.java
  3. 0 68
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCommonResult.java
  4. 25 0
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsSendRespDTO.java
  5. 2 76
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/AbstractSmsClient.java
  6. 31 60
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java
  7. 0 42
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java
  8. 0 22
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkCodeMapping.java
  9. 13 13
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java
  10. 0 41
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsChannelProperties.java
  11. 62 145
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java
  12. 0 50
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMapping.java
  13. 45 83
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java
  14. 0 43
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMappingTest.java
  15. 63 55
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClientTest.java
  16. 0 50
      yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMappingTest.java
  17. 4 0
      yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
  18. 16 16
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java
  19. 6 5
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java
  20. 5 7
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java
  21. 14 6
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java
  22. 22 5
      yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java
  23. 4 7
      yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java
  24. 4 6
      yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java
  25. 7 8
      yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java

+ 3 - 3
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsClient.java

@@ -31,8 +31,8 @@ public interface SmsClient {
      * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序
      * @return 短信发送结果
      */
-    SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, String apiTemplateId,
-                                            List<KeyValue<String, Object>> templateParams);
+    SmsSendRespDTO sendSms(Long logId, String mobile, String apiTemplateId,
+                           List<KeyValue<String, Object>> templateParams) throws Throwable;
 
     /**
      * 解析接收短信的接收结果
@@ -49,6 +49,6 @@ public interface SmsClient {
      * @param apiTemplateId 短信 API 的模板编号
      * @return 短信模板
      */
-    SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId);
+    SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable;
 
 }

+ 0 - 17
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCodeMapping.java

@@ -1,17 +0,0 @@
-package cn.iocoder.yudao.framework.sms.core.client;
-
-import cn.iocoder.yudao.framework.common.exception.ErrorCode;
-import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
-
-import java.util.function.Function;
-
-/**
- * 将 API 的错误码,转换为通用的错误码
- *
- * @see SmsCommonResult
- * @see SmsFrameworkErrorCodeConstants
- *
- * @author 芋道源码
- */
-public interface SmsCodeMapping extends Function<String, ErrorCode> {
-}

+ 0 - 68
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCommonResult.java

@@ -1,68 +0,0 @@
-package cn.iocoder.yudao.framework.sms.core.client;
-
-import cn.hutool.core.exceptions.ExceptionUtil;
-import cn.hutool.core.lang.Assert;
-import cn.iocoder.yudao.framework.common.exception.ErrorCode;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
-import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
-import lombok.Data;
-import lombok.EqualsAndHashCode;
-import lombok.ToString;
-
-/**
- * 短信的 CommonResult 拓展类
- *
- * 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段
- *
- * 另外,一些短信平台(例如说阿里云、腾讯云)会返回一个请求编号,用于排查请求失败的问题,我们设置到 {@link #apiRequestId} 字段
- *
- * @author 芋道源码
- */
-@Data
-@EqualsAndHashCode(callSuper = true)
-@ToString(callSuper = true)
-public class SmsCommonResult<T> extends CommonResult<T> {
-
-    /**
-     * API 返回错误码
-     *
-     * 由于第三方的错误码可能是字符串,所以使用 String 类型
-     */
-    private String apiCode;
-    /**
-     * API 返回提示
-     */
-    private String apiMsg;
-
-    /**
-     * API 请求编号
-     */
-    private String apiRequestId;
-
-    private SmsCommonResult() {
-    }
-
-    public static <T> SmsCommonResult<T> build(String apiCode, String apiMsg, String apiRequestId,
-                                               T data, SmsCodeMapping codeMapping) {
-        Assert.notNull(codeMapping, "参数 codeMapping 不能为空");
-        SmsCommonResult<T> result = new SmsCommonResult<T>().setApiCode(apiCode).setApiMsg(apiMsg).setApiRequestId(apiRequestId);
-        result.setData(data);
-        // 翻译错误码
-        if (codeMapping != null) {
-            ErrorCode errorCode = codeMapping.apply(apiCode);
-            if (errorCode == null) {
-                errorCode = SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
-            }
-            result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg());
-        }
-        return result;
-    }
-
-    public static <T> SmsCommonResult<T> error(Throwable ex) {
-        SmsCommonResult<T> result = new SmsCommonResult<>();
-        result.setCode(SmsFrameworkErrorCodeConstants.EXCEPTION.getCode());
-        result.setMsg(ExceptionUtil.getRootCauseMessage(ex));
-        return result;
-    }
-
-}

+ 25 - 0
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsSendRespDTO.java

@@ -10,9 +10,34 @@ import lombok.Data;
 @Data
 public class SmsSendRespDTO {
 
+    /**
+     * 是否成功
+     */
+    private Boolean success;
+
+    /**
+     * API 请求编号
+     */
+    private String apiRequestId;
+
+    // ==================== 成功时字段 ====================
+
     /**
      * 短信 API 发送返回的序号
      */
     private String serialNo;
 
+    // ==================== 失败时字段 ====================
+
+    /**
+     * API 返回错误码
+     *
+     * 由于第三方的错误码可能是字符串,所以使用 String 类型
+     */
+    private String apiCode;
+    /**
+     * API 返回提示
+     */
+    private String apiMsg;
+
 }

+ 2 - 76
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/AbstractSmsClient.java

@@ -1,17 +1,9 @@
 package cn.iocoder.yudao.framework.sms.core.client.impl;
 
-import cn.iocoder.yudao.framework.common.core.KeyValue;
 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
-import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
-import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
-import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
 import lombok.extern.slf4j.Slf4j;
 
-import java.util.List;
-
 /**
  * 短信客户端的抽象类,提供模板方法,减少子类的冗余代码
  *
@@ -25,14 +17,9 @@ public abstract class AbstractSmsClient implements SmsClient {
      * 短信渠道配置
      */
     protected volatile SmsChannelProperties properties;
-    /**
-     * 错误码枚举类
-     */
-    protected final SmsCodeMapping codeMapping;
 
-    public AbstractSmsClient(SmsChannelProperties properties, SmsCodeMapping codeMapping) {
-        this.properties = prepareProperties(properties);
-        this.codeMapping = codeMapping;
+    public AbstractSmsClient(SmsChannelProperties properties) {
+        this.properties = properties;
     }
 
     /**
@@ -54,74 +41,13 @@ public abstract class AbstractSmsClient implements SmsClient {
             return;
         }
         log.info("[refresh][配置({})发生变化,重新初始化]", properties);
-        this.properties = prepareProperties(properties);
         // 初始化
         this.init();
     }
 
-    /**
-     * 在赋值给{@link this#properties}前,子类可根据需要预处理短信渠道配置
-     *
-     * @param properties 数据库中存储的短信渠道配置
-     * @return 满足子类实现的短信渠道配置
-     */
-    protected SmsChannelProperties prepareProperties(SmsChannelProperties properties) {
-        return properties;
-    }
-
     @Override
     public Long getId() {
         return properties.getId();
     }
 
-    @Override
-    public final SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile,
-                                                         String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
-        // 执行短信发送
-        SmsCommonResult<SmsSendRespDTO> result;
-        try {
-            result = doSendSms(logId, mobile, apiTemplateId, templateParams);
-        } catch (Throwable ex) {
-            // 打印异常日志
-            log.error("[sendSms][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]",
-                    logId, mobile, apiTemplateId, templateParams, ex);
-            // 封装返回
-            return SmsCommonResult.error(ex);
-        }
-        return result;
-    }
-
-    protected abstract SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
-                                                                 String apiTemplateId, List<KeyValue<String, Object>> templateParams)
-            throws Throwable;
-
-    @Override
-    public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable {
-        try {
-            return doParseSmsReceiveStatus(text);
-        } catch (Throwable ex) {
-            log.error("[parseSmsReceiveStatus][text({}) 解析发生异常]", text, ex);
-            throw ex;
-        }
-    }
-
-    protected abstract List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable;
-
-    @Override
-    public SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId) {
-        // 执行短信发送
-        SmsCommonResult<SmsTemplateRespDTO> result;
-        try {
-            result = doGetSmsTemplate(apiTemplateId);
-        } catch (Throwable ex) {
-            // 打印异常日志
-            log.error("[getSmsTemplate][获得短信模板({}) 发生异常]", apiTemplateId, ex);
-            // 封装返回
-            return SmsCommonResult.error(ex);
-        }
-        return result;
-    }
-
-    protected abstract SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable;
-
 }

+ 31 - 60
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java

@@ -1,25 +1,21 @@
 package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
 
 import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.ReflectUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
 import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
-import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import com.aliyuncs.AcsRequest;
-import com.aliyuncs.AcsResponse;
 import com.aliyuncs.DefaultAcsClient;
 import com.aliyuncs.IAcsClient;
 import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
+import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse;
 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
-import com.aliyuncs.exceptions.ClientException;
+import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
 import com.aliyuncs.profile.DefaultProfile;
 import com.aliyuncs.profile.IClientProfile;
 import com.fasterxml.jackson.annotation.JsonFormat;
@@ -31,9 +27,8 @@ import lombok.extern.slf4j.Slf4j;
 import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Objects;
-import java.util.function.Function;
-import java.util.stream.Collectors;
 
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
 
@@ -46,6 +41,11 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DE
 @Slf4j
 public class AliyunSmsClient extends AbstractSmsClient {
 
+    /**
+     * 调用成功 code
+     */
+    public static final String API_CODE_SUCCESS = "OK";
+
     /**
      * REGION, 使用杭州
      */
@@ -57,7 +57,7 @@ public class AliyunSmsClient extends AbstractSmsClient {
     private volatile IAcsClient client;
 
     public AliyunSmsClient(SmsChannelProperties properties) {
-        super(properties, new AliyunSmsCodeMapping());
+        super(properties);
         Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
         Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
     }
@@ -69,9 +69,9 @@ public class AliyunSmsClient extends AbstractSmsClient {
     }
 
     @Override
-    protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
-                                                        String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
-        // 构建参数
+    public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
+                                  List<KeyValue<String, Object>> templateParams) throws Throwable {
+        // 构建请求
         SendSmsRequest request = new SendSmsRequest();
         request.setPhoneNumbers(mobile);
         request.setSignName(properties.getSignature());
@@ -79,34 +79,32 @@ public class AliyunSmsClient extends AbstractSmsClient {
         request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams)));
         request.setOutId(String.valueOf(sendLogId));
         // 执行请求
-        return invoke(request, response -> new SmsSendRespDTO().setSerialNo(response.getBizId()));
+        SendSmsResponse response = client.getAcsResponse(request);
+        return new SmsSendRespDTO().setSuccess(Objects.equals(response.getCode(), API_CODE_SUCCESS)).setSerialNo(response.getBizId())
+                .setApiRequestId(response.getRequestId()).setApiCode(response.getCode()).setApiMsg(response.getMessage());
     }
 
     @Override
-    protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
+    public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
         List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class);
-        return statuses.stream().map(status -> {
-            SmsReceiveRespDTO resp = new SmsReceiveRespDTO();
-            resp.setSuccess(status.getSuccess());
-            resp.setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg());
-            resp.setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime());
-            resp.setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId()));
-            return resp;
-        }).collect(Collectors.toList());
+        return convertList(statuses, status -> new SmsReceiveRespDTO().setSuccess(status.getSuccess())
+                .setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg())
+                .setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime())
+                .setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId())));
     }
 
     @Override
-    protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) {
-        // 构建参数
+    public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
+        // 构建请求
         QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
         request.setTemplateCode(apiTemplateId);
         // 执行请求
-        return invoke(request, response -> {
-            SmsTemplateRespDTO data = new SmsTemplateRespDTO();
-            data.setId(response.getTemplateCode()).setContent(response.getTemplateContent());
-            data.setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason());
-            return data;
-        });
+        QuerySmsTemplateResponse response = client.getAcsResponse(request);
+        if (response.getTemplateStatus() == null) {
+            return null;
+        }
+        return new SmsTemplateRespDTO().setId(response.getTemplateCode()).setContent(response.getTemplateContent())
+                .setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason());
     }
 
     @VisibleForTesting
@@ -119,37 +117,10 @@ public class AliyunSmsClient extends AbstractSmsClient {
         }
     }
 
-    @VisibleForTesting
-    <T extends AcsResponse, R> SmsCommonResult<R> invoke(AcsRequest<T> request, Function<T, R> responseConsumer) {
-        try {
-            // 执行发送. 由于阿里云 sms 短信没有统一的 Response,但是有统一的 code、message、requestId 属性,所以只好反射
-            T sendResult = client.getAcsResponse(request);
-            String code = (String) ReflectUtil.getFieldValue(sendResult, "code");
-            String message = (String) ReflectUtil.getFieldValue(sendResult, "message");
-            String requestId = (String) ReflectUtil.getFieldValue(sendResult, "requestId");
-            // 解析结果
-            R data = null;
-            if (Objects.equals(code, "OK")) { // 请求成功的情况下
-                data = responseConsumer.apply(sendResult);
-            }
-            // 拼接结果
-            return SmsCommonResult.build(code, message, requestId, data, codeMapping);
-        } catch (ClientException ex) {
-            return SmsCommonResult.build(ex.getErrCode(), formatResultMsg(ex), ex.getRequestId(), null, codeMapping);
-        }
-    }
-
-    private static String formatResultMsg(ClientException ex) {
-        if (StrUtil.isEmpty(ex.getErrorDescription())) {
-            return ex.getErrMsg();
-        }
-        return ex.getErrMsg() + " => " + ex.getErrorDescription();
-    }
-
     /**
      * 短信接收状态
      *
-     * 参见 https://help.aliyun.com/document_detail/101867.html 文档
+     * 参见 <a href="https://help.aliyun.com/document_detail/101867.html">文档</a>
      *
      * @author 芋道源码
      */

+ 0 - 42
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java

@@ -1,42 +0,0 @@
-package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
-
-import cn.iocoder.yudao.framework.common.exception.ErrorCode;
-import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
-import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
-
-/**
- * 阿里云的 SmsCodeMapping 实现类
- *
- * 参见 https://help.aliyun.com/document_detail/101346.htm 文档
- *
- * @author 芋道源码
- */
-public class AliyunSmsCodeMapping implements SmsCodeMapping {
-
-    @Override
-    public ErrorCode apply(String apiCode) {
-        switch (apiCode) {
-            case "OK": return GlobalErrorCodeConstants.SUCCESS;
-            case "isv.ACCOUNT_NOT_EXISTS":
-            case "isv.ACCOUNT_ABNORMAL":
-            case "MissingAccessKeyId": return SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID;
-            case "isp.RAM_PERMISSION_DENY": return SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY;
-            case "isv.INVALID_JSON_PARAM":
-            case "isv.INVALID_PARAMETERS": return SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR;
-            case "isv.BUSINESS_LIMIT_CONTROL": return SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL;
-            case "isv.DAY_LIMIT_CONTROL": return SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL;
-            case "isv.SMS_CONTENT_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID;
-            case "isv.SMS_TEMPLATE_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID;
-            case "isv.SMS_SIGNATURE_ILLEGAL":
-            case "isv.SIGN_NAME_ILLEGAL":
-            case "isv.SMS_SIGN_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID;
-            case "isv.AMOUNT_NOT_ENOUGH":
-            case "isv.OUT_OF_SERVICE": return SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH;
-            case "isv.MOBILE_NUMBER_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID;
-            case "isv.TEMPLATE_MISSING_PARAMETERS": return SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR;
-            default: return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
-        }
-    }
-
-}

+ 0 - 22
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkCodeMapping.java

@@ -1,22 +0,0 @@
-package cn.iocoder.yudao.framework.sms.core.client.impl.debug;
-
-import cn.iocoder.yudao.framework.common.exception.ErrorCode;
-import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
-import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
-
-import java.util.Objects;
-
-/**
- * 钉钉的 SmsCodeMapping 实现类
- *
- * @author 芋道源码
- */
-public class DebugDingTalkCodeMapping implements SmsCodeMapping {
-
-    @Override
-    public ErrorCode apply(String apiCode) {
-        return Objects.equals(apiCode, "0") ? GlobalErrorCodeConstants.SUCCESS : SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
-    }
-
-}

+ 13 - 13
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java

@@ -8,19 +8,19 @@ import cn.hutool.crypto.digest.DigestUtil;
 import cn.hutool.crypto.digest.HmacAlgorithm;
 import cn.hutool.http.HttpUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
 import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
-import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 
 /**
  * 基于钉钉 WebHook 实现的调试的短信客户端实现类
@@ -32,7 +32,7 @@ import java.util.Map;
 public class DebugDingTalkSmsClient extends AbstractSmsClient {
 
     public DebugDingTalkSmsClient(SmsChannelProperties properties) {
-        super(properties, new DebugDingTalkCodeMapping());
+        super(properties);
         Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
         Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
     }
@@ -42,8 +42,8 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
     }
 
     @Override
-    protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
-                                                        String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
+    public SmsSendRespDTO sendSms(Long sendLogId, String mobile,
+                                  String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
         // 构建请求
         String url = buildUrl("robot/send");
         Map<String, Object> params = new HashMap<>();
@@ -55,14 +55,15 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
         String responseText = HttpUtil.post(url, JsonUtils.toJsonString(params));
         // 解析结果
         Map<?, ?> responseObj = JsonUtils.parseObject(responseText, Map.class);
-        return SmsCommonResult.build(MapUtil.getStr(responseObj, "errcode"), MapUtil.getStr(responseObj, "errorMsg"),
-                null, new SmsSendRespDTO().setSerialNo(StrUtil.uuid()), codeMapping);
+        String errorCode = MapUtil.getStr(responseObj, "errcode");
+        return new SmsSendRespDTO().setSuccess(Objects.equals(errorCode, "0")).setSerialNo(StrUtil.uuid())
+                .setApiCode(errorCode).setApiMsg(MapUtil.getStr(responseObj, "errorMsg"));
     }
 
     /**
      * 构建请求地址
      *
-     * 参见 https://developers.dingtalk.com/document/app/custom-robot-access/title-nfv-794-g71 文档
+     * 参见 <a href="https://developers.dingtalk.com/document/app/custom-robot-access/title-nfv-794-g71">文档</a>
      *
      * @param path 请求路径
      * @return 请求地址
@@ -82,15 +83,14 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
     }
 
     @Override
-    protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
+    public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
         throw new UnsupportedOperationException("模拟短信客户端,暂时无需解析回调");
     }
 
     @Override
-    protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) {
-        SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
+    public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) {
+        return new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
                 .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason("");
-        return SmsCommonResult.build("0", "success", null, data, codeMapping);
     }
 
 }

+ 0 - 41
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsChannelProperties.java

@@ -1,41 +0,0 @@
-package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
-
-import cn.hutool.core.bean.BeanUtil;
-import cn.hutool.core.lang.Assert;
-import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
-import lombok.Data;
-
-/**
- * 腾讯云短信配置实现类
- * 腾讯云发送短信时,需要额外的参数 sdkAppId,
- *
- * @author shiwp
- */
-@Data
-public class TencentSmsChannelProperties extends SmsChannelProperties {
-
-    /**
-     * 应用 id
-     */
-    private String sdkAppId;
-
-    /**
-     * 考虑到不破坏原有的 apiKey + apiSecret 的结构,
-     * 所以腾讯云短信存储时,将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
-     * 因此在使用时,需要将 secretId 和 sdkAppId 解析出来,分别存储到对应字段中。
-     */
-    public static TencentSmsChannelProperties build(SmsChannelProperties properties) {
-        if (properties instanceof TencentSmsChannelProperties) {
-            return (TencentSmsChannelProperties) properties;
-        }
-        TencentSmsChannelProperties result = BeanUtil.toBean(properties, TencentSmsChannelProperties.class);
-        String combineKey = properties.getApiKey();
-        Assert.notEmpty(combineKey, "apiKey 不能为空");
-        String[] keys = combineKey.trim().split(" ");
-        Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]");
-        Assert.notBlank(keys[0], "腾讯云短信 secretId 不能为空");
-        Assert.notBlank(keys[1], "腾讯云短信 sdkAppId 不能为空");
-        result.setSdkAppId(keys[1]).setApiKey(keys[0]);
-        return result;
-    }
-}

+ 62 - 145
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java

@@ -4,9 +4,7 @@ import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
-import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
@@ -17,23 +15,22 @@ import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.annotations.VisibleForTesting;
 import com.tencentcloudapi.common.Credential;
-import com.tencentcloudapi.common.exception.TencentCloudSDKException;
 import com.tencentcloudapi.sms.v20210111.SmsClient;
 import com.tencentcloudapi.sms.v20210111.models.*;
 import lombok.Data;
 
 import java.time.LocalDateTime;
 import java.util.List;
-import java.util.function.Function;
-import java.util.function.Supplier;
+import java.util.Objects;
 
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
 
 /**
  * 腾讯云短信功能实现
- * <p>
- * 参见 https://cloud.tencent.com/document/product/382/52077
+ *
+ * 参见 <a href="https://cloud.tencent.com/document/product/382/52077">文档</a>
  *
  * @author shiwp
  */
@@ -42,7 +39,7 @@ public class TencentSmsClient extends AbstractSmsClient {
     /**
      * 调用成功 code
      */
-    public static final String API_SUCCESS_CODE = "Ok";
+    public static final String API_CODE_SUCCESS = "Ok";
 
     /**
      * REGION,使用南京
@@ -51,180 +48,103 @@ public class TencentSmsClient extends AbstractSmsClient {
 
     /**
      * 是否国际/港澳台短信:
+     *
      * 0:表示国内短信。
      * 1:表示国际/港澳台短信。
      */
-    private static final long INTERNATIONAL = 0L;
+    private static final long INTERNATIONAL_CHINA = 0L;
 
     private SmsClient client;
 
     public TencentSmsClient(SmsChannelProperties properties) {
-        super(properties, new TencentSmsCodeMapping());
+        super(properties);
         Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
+        validateSdkAppId(properties);
     }
 
     @Override
     protected void doInit() {
         // 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId,secretKey
-        Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret());
+        Credential credential = new Credential(getApiKey(), properties.getApiSecret());
         client = new SmsClient(credential, ENDPOINT);
     }
 
-    @Override
-    protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId,
-                                                        String mobile,
-                                                        String apiTemplateId,
-                                                        List<KeyValue<String, Object>> templateParams) throws Throwable {
-        return invoke(() -> buildSendSmsRequest(sendLogId, mobile, apiTemplateId, templateParams),
-                this::doSendSms0,
-                response -> {
-                    SendStatus sendStatus = response.getSendStatusSet()[0];
-                    return SmsCommonResult.build(sendStatus.getCode(), sendStatus.getMessage(), response.getRequestId(),
-                            new SmsSendRespDTO().setSerialNo(sendStatus.getSerialNo()), codeMapping);
-                });
-    }
-
-
     /**
-     * 腾讯云发放短信的时候,需要额外的参数 sdkAppId。
-     * 考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
-     * 因此,这边需要使用 TencentSmsChannelProperties 做拆分,重新封装到 properties 内。
+     * 参数校验腾讯云的 SDK AppId
+     *
+     * 原因是:腾讯云发放短信的时候,需要额外的参数 sdkAppId
+     *
+     * 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
      *
-     * @param properties 数据库中存储的短信渠道配置
-     * @return TencentSmsChannelProperties
+     * @param properties 配置
      */
-    @Override
-    protected SmsChannelProperties prepareProperties(SmsChannelProperties properties) {
-        return TencentSmsChannelProperties.build(properties);
+    private static void validateSdkAppId(SmsChannelProperties properties) {
+        String combineKey = properties.getApiKey();
+        Assert.notEmpty(combineKey, "apiKey 不能为空");
+        String[] keys = combineKey.trim().split(" ");
+        Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]");
     }
 
-    /**
-     * 调用腾讯云 SDK 发送短信
-     *
-     * @param request 发送短信请求
-     * @return 发送短信响应
-     * @throws TencentCloudSDKException SDK 用来封装发送短信失败
-     */
-    private SendSmsResponse doSendSms0(SendSmsRequest request) throws TencentCloudSDKException {
-        return client.SendSms(request);
+    private String getSdkAppId() {
+        return StrUtil.subAfter(properties.getApiKey(), " ", true);
     }
 
-    /**
-     * 封装腾讯云发送短信请求
-     *
-     * @param sendLogId      日志编号
-     * @param mobile         手机号
-     * @param apiTemplateId  短信 API 的模板编号
-     * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序
-     * @return 腾讯云发送短信请求
-     */
-    private SendSmsRequest buildSendSmsRequest(Long sendLogId,
-                                               String mobile,
-                                               String apiTemplateId,
-                                               List<KeyValue<String, Object>> templateParams) {
+    private String getApiKey() {
+        return StrUtil.subBefore(properties.getApiKey(), " ", true);
+    }
+
+    @Override
+    public SmsSendRespDTO sendSms(Long sendLogId, String mobile,
+                                  String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
+        // 构建请求
         SendSmsRequest request = new SendSmsRequest();
-        request.setSmsSdkAppId(((TencentSmsChannelProperties) properties).getSdkAppId());
+        request.setSmsSdkAppId(getSdkAppId());
         request.setPhoneNumberSet(new String[]{mobile});
         request.setSignName(properties.getSignature());
         request.setTemplateId(apiTemplateId);
         request.setTemplateParamSet(ArrayUtils.toArray(templateParams, e -> String.valueOf(e.getValue())));
         request.setSessionContext(JsonUtils.toJsonString(new SessionContext().setLogId(sendLogId)));
-        return request;
+        // 执行请求
+        SendSmsResponse response = client.SendSms(request);
+        SendStatus status = response.getSendStatusSet()[0];
+        return new SmsSendRespDTO().setSuccess(Objects.equals(status.getCode(), API_CODE_SUCCESS)).setSerialNo(status.getSerialNo())
+                .setApiRequestId(response.getRequestId()).setApiCode(status.getCode()).setApiMsg(status.getMessage());
     }
 
     @Override
-    protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
+    public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
         List<SmsReceiveStatus> callback = JsonUtils.parseArray(text, SmsReceiveStatus.class);
-        return CollectionUtils.convertList(callback, status -> {
-            SmsReceiveRespDTO data = new SmsReceiveRespDTO();
-            data.setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription());
-            data.setReceiveTime(status.getReceiveTime()).setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus()));
-            data.setMobile(status.getMobile()).setSerialNo(status.getSerialNo());
-            SessionContext context;
-            Long logId;
-            Assert.notNull(context = status.getSessionContext(), "回执信息中未解析出 context,请联系腾讯云小助手");
-            Assert.notNull(logId = context.getLogId(), "回执信息中未解析出 logId,请联系腾讯云小助手");
-            data.setLogId(logId);
-            return data;
-        });
+        return convertList(callback, status -> new SmsReceiveRespDTO()
+                .setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus()))
+                .setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription())
+                .setMobile(status.getMobile()).setReceiveTime(status.getReceiveTime())
+                .setSerialNo(status.getSerialNo()).setLogId(status.getSessionContext().getLogId()));
     }
 
     @Override
-    protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable {
-        return invoke(() -> this.buildSmsTemplateStatusRequest(apiTemplateId),
-                this::doGetSmsTemplate0,
-                response -> {
-                    SmsTemplateRespDTO data = convertTemplateStatusDTO(response.getDescribeTemplateStatusSet()[0]);
-                    return SmsCommonResult.build(API_SUCCESS_CODE, null, response.getRequestId(), data, codeMapping);
-                });
-    }
-
-    @VisibleForTesting
-    SmsTemplateRespDTO convertTemplateStatusDTO(DescribeTemplateListStatus templateStatus) {
-        if (templateStatus == null) {
-            return null;
-        }
-        SmsTemplateAuditStatusEnum auditStatus;
-        Assert.notNull(templateStatus.getStatusCode(),
-                StrUtil.format("短信模版审核状态为 null,模版 id{}", templateStatus.getTemplateId()));
-        switch (templateStatus.getStatusCode().intValue()) {
-            case -1:
-                auditStatus = SmsTemplateAuditStatusEnum.FAIL;
-                break;
-            case 0:
-                auditStatus = SmsTemplateAuditStatusEnum.SUCCESS;
-                break;
-            case 1:
-                auditStatus = SmsTemplateAuditStatusEnum.CHECKING;
-                break;
-            default:
-                throw new IllegalStateException(StrUtil.format("不能解析短信模版审核状态{},模版 id{}",
-                        templateStatus.getStatusCode(), templateStatus.getTemplateId()));
-        }
-        SmsTemplateRespDTO data = new SmsTemplateRespDTO();
-        data.setId(String.valueOf(templateStatus.getTemplateId())).setContent(templateStatus.getTemplateContent());
-        data.setAuditStatus(auditStatus.getStatus()).setAuditReason(templateStatus.getReviewReply());
-        return data;
-    }
-
-    /**
-     * 封装查询模版审核状态请求
-     * @param apiTemplateId api 的模版 id
-     * @return 查询模版审核状态请求
-     */
-    private DescribeSmsTemplateListRequest buildSmsTemplateStatusRequest(String apiTemplateId) {
+    public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
+        // 构建请求
         DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest();
         request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)});
-        // 地区 0:表示国内短信。1:表示国际/港澳台短信。
-        request.setInternational(INTERNATIONAL);
-        return request;
-    }
-
-    /**
-     * 调用腾讯云 SDK 查询短信模版状态
-     *
-     * @param request 查询短信模版状态请求
-     * @return 查询短信模版状态响应
-     * @throws TencentCloudSDKException SDK 用来封装查询短信模版状态失败
-     */
-    private DescribeSmsTemplateListResponse doGetSmsTemplate0(DescribeSmsTemplateListRequest request) throws TencentCloudSDKException {
-        return client.DescribeSmsTemplateList(request);
+        request.setInternational(INTERNATIONAL_CHINA);
+        // 执行请求
+        DescribeSmsTemplateListResponse response = client.DescribeSmsTemplateList(request);
+        DescribeTemplateListStatus status = response.getDescribeTemplateStatusSet()[0];
+        if (status == null || status.getStatusCode() == null) {
+            return null;
+        }
+        return new SmsTemplateRespDTO().setId(status.getTemplateId().toString()).setContent(status.getTemplateContent())
+                .setAuditStatus(convertSmsTemplateAuditStatus(status.getStatusCode().intValue())).setAuditReason(status.getReviewReply());
     }
 
-    <Q, P, R> SmsCommonResult<R> invoke(Supplier<Q> requestSupplier,
-                                        SdkFunction<Q, P> responseSupplier,
-                                        Function<P, SmsCommonResult<R>> resultGen) {
-        // 构建请求body
-        Q request = requestSupplier.get();
-        P response;
-        // 调用腾讯云发送短信
-        try {
-            response = responseSupplier.apply(request);
-        } catch (TencentCloudSDKException e) {
-            // 调用异常,封装结果
-            return SmsCommonResult.build(e.getErrorCode(), e.getMessage(), e.getRequestId(), null, codeMapping);
+    @VisibleForTesting
+    Integer convertSmsTemplateAuditStatus(int templateStatus) {
+        switch (templateStatus) {
+            case 1: return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
+            case 0: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
+            case -1: return SmsTemplateAuditStatusEnum.FAIL.getStatus();
+            default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
         }
-        return resultGen.apply(response);
     }
 
     @Data
@@ -278,7 +198,7 @@ public class TencentSmsClient extends AbstractSmsClient {
         private String serialNo;
 
         /**
-         * 用户的 session 内容(与发送接口的请求参数SessionContext一致)
+         * 用户的 session 内容(与发送接口的请求参数 SessionContext 一致)
          */
         @JsonProperty("ext")
         private SessionContext sessionContext;
@@ -293,10 +213,7 @@ public class TencentSmsClient extends AbstractSmsClient {
          * 发送短信记录id
          */
         private Long logId;
-    }
 
-    private interface SdkFunction<T, R> {
-        R apply(T t) throws TencentCloudSDKException;
     }
 
 }

+ 0 - 50
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMapping.java

@@ -1,50 +0,0 @@
-package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
-
-import cn.iocoder.yudao.framework.common.exception.ErrorCode;
-import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
-import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
-
-import static cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.*;
-
-/**
- * 腾讯云的 SmsCodeMapping 实现类
- *
- * 参见 https://cloud.tencent.com/document/api/382/52075#.E5.85.AC.E5.85.B1.E9.94.99.E8.AF.AF.E7.A0.81
- *
- * @author : shiwp
- */
-public class TencentSmsCodeMapping implements SmsCodeMapping {
-
-    @Override
-    public ErrorCode apply(String apiCode) {
-        switch (apiCode) {
-            case TencentSmsClient.API_SUCCESS_CODE: return GlobalErrorCodeConstants.SUCCESS;
-            case "FailedOperation.ContainSensitiveWord": return SMS_SEND_CONTENT_INVALID;
-            case "FailedOperation.JsonParseFail":
-            case "MissingParameter.EmptyPhoneNumberSet":
-            case "LimitExceeded.PhoneNumberCountLimit":
-            case "FailedOperation.FailResolvePacket": return GlobalErrorCodeConstants.BAD_REQUEST;
-            case "FailedOperation.InsufficientBalanceInSmsPackage": return SMS_ACCOUNT_MONEY_NOT_ENOUGH;
-            case "FailedOperation.MarketingSendTimeConstraint": return SMS_SEND_MARKET_LIMIT_CONTROL;
-            case "FailedOperation.PhoneNumberInBlacklist": return SMS_MOBILE_BLACK;
-            case "FailedOperation.SignatureIncorrectOrUnapproved": return SMS_SIGN_INVALID;
-            case "FailedOperation.MissingTemplateToModify":
-            case "FailedOperation.TemplateIncorrectOrUnapproved": return SMS_TEMPLATE_INVALID;
-            case "InvalidParameterValue.IncorrectPhoneNumber": return SMS_MOBILE_INVALID;
-            case "InvalidParameterValue.SdkAppIdNotExist": return SMS_APP_ID_INVALID;
-            case "InvalidParameterValue.TemplateParameterLengthLimit":
-            case "InvalidParameterValue.TemplateParameterFormatError": return SMS_TEMPLATE_PARAM_ERROR;
-            case "LimitExceeded.PhoneNumberDailyLimit": return SMS_SEND_DAY_LIMIT_CONTROL;
-            case "LimitExceeded.PhoneNumberThirtySecondLimit":
-            case "LimitExceeded.PhoneNumberOneHourLimit": return SMS_SEND_BUSINESS_LIMIT_CONTROL;
-            case "UnauthorizedOperation.RequestPermissionDeny":
-            case "FailedOperation.ForbidAddMarketingTemplates":
-            case "FailedOperation.NotEnterpriseCertification":
-            case "UnauthorizedOperation.IndividualUserMarketingSmsPermissionDeny": return SMS_PERMISSION_DENY;
-            case "UnauthorizedOperation.RequestIpNotInWhitelist": return SMS_IP_DENY;
-            case "AuthFailure.SecretIdNotFound": return SMS_ACCOUNT_INVALID;
-        }
-        return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
-    }
-}

+ 45 - 83
yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java

@@ -1,26 +1,20 @@
 package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
 
 import cn.hutool.core.util.ReflectUtil;
-import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
-import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
+import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
 import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
-import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
-import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
-import com.aliyuncs.AcsRequest;
+import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import com.aliyuncs.IAcsClient;
 import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
 import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse;
 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
-import com.aliyuncs.exceptions.ClientException;
 import com.google.common.collect.Lists;
-import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
 import org.mockito.ArgumentMatcher;
 import org.mockito.InjectMocks;
@@ -28,12 +22,10 @@ import org.mockito.Mock;
 
 import java.time.LocalDateTime;
 import java.util.List;
-import java.util.function.Function;
 
 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
 import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.when;
 
@@ -67,8 +59,7 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
     }
 
     @Test
-    @SuppressWarnings("unchecked")
-    public void testDoSendSms() throws ClientException {
+    public void tesSendSms_success() throws Throwable {
         // 准备参数
         Long sendLogId = randomLongId();
         String mobile = randomString();
@@ -87,20 +78,47 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
         }))).thenReturn(response);
 
         // 调用
-        SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile,
+        SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
                 apiTemplateId, templateParams);
         // 断言
+        assertTrue(result.getSuccess());
+        assertEquals(response.getRequestId(), result.getApiRequestId());
         assertEquals(response.getCode(), result.getApiCode());
         assertEquals(response.getMessage(), result.getApiMsg());
-        assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
-        assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
+        assertEquals(response.getBizId(), result.getSerialNo());
+    }
+
+    @Test
+    public void tesSendSms_fail() throws Throwable {
+        // 准备参数
+        Long sendLogId = randomLongId();
+        String mobile = randomString();
+        String apiTemplateId = randomString();
+        List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
+                new KeyValue<>("code", 1234), new KeyValue<>("op", "login"));
+        // mock 方法
+        SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> o.setCode("ERROR"));
+        when(client.getAcsResponse(argThat((ArgumentMatcher<SendSmsRequest>) acsRequest -> {
+            assertEquals(mobile, acsRequest.getPhoneNumbers());
+            assertEquals(properties.getSignature(), acsRequest.getSignName());
+            assertEquals(apiTemplateId, acsRequest.getTemplateCode());
+            assertEquals(toJsonString(MapUtils.convertMap(templateParams)), acsRequest.getTemplateParam());
+            assertEquals(sendLogId.toString(), acsRequest.getOutId());
+            return true;
+        }))).thenReturn(response);
+
+        // 调用
+        SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
+        // 断言
+        assertFalse(result.getSuccess());
         assertEquals(response.getRequestId(), result.getApiRequestId());
-        // 断言结果
-        assertEquals(response.getBizId(), result.getData().getSerialNo());
+        assertEquals(response.getCode(), result.getApiCode());
+        assertEquals(response.getMessage(), result.getApiMsg());
+        assertEquals(response.getBizId(), result.getSerialNo());
     }
 
     @Test
-    public void testDoTParseSmsReceiveStatus() throws Throwable {
+    public void testParseSmsReceiveStatus() {
         // 准备参数
         String text = "[\n" +
                 "  {\n" +
@@ -118,20 +136,21 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
         // mock 方法
 
         // 调用
-        List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text);
+        List<SmsReceiveRespDTO> statuses = smsClient.parseSmsReceiveStatus(text);
         // 断言
         assertEquals(1, statuses.size());
         assertTrue(statuses.get(0).getSuccess());
         assertEquals("DELIVERED", statuses.get(0).getErrorCode());
         assertEquals("用户接收成功", statuses.get(0).getErrorMsg());
         assertEquals("13900000001", statuses.get(0).getMobile());
-        assertEquals(LocalDateTime.of(2017, 2, 2, 22, 23, 24), statuses.get(0).getReceiveTime());
+        assertEquals(LocalDateTime.of(2017, 2, 2, 22, 23, 24),
+                statuses.get(0).getReceiveTime());
         assertEquals("12345", statuses.get(0).getSerialNo());
         assertEquals(67890L, statuses.get(0).getLogId());
     }
 
     @Test
-    public void testDoGetSmsTemplate() throws ClientException {
+    public void testGetSmsTemplate() throws Throwable {
         // 准备参数
         String apiTemplateId = randomString();
         // mock 方法
@@ -145,18 +164,12 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
         }))).thenReturn(response);
 
         // 调用
-        SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId);
+        SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId);
         // 断言
-        assertEquals(response.getCode(), result.getApiCode());
-        assertEquals(response.getMessage(), result.getApiMsg());
-        assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
-        assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
-        assertEquals(response.getRequestId(), result.getApiRequestId());
-        // 断言结果
-        assertEquals(response.getTemplateCode(), result.getData().getId());
-        assertEquals(response.getTemplateContent(), result.getData().getContent());
-        assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
-        assertEquals(response.getReason(), result.getData().getAuditReason());
+        assertEquals(response.getTemplateCode(), result.getId());
+        assertEquals(response.getTemplateContent(), result.getContent());
+        assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus());
+        assertEquals(response.getReason(), result.getAuditReason());
     }
 
     @Test
@@ -171,55 +184,4 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
                 "未知审核状态(3)");
     }
 
-    @Test
-    @SuppressWarnings("unchecked")
-    public void testInvoke_throwable() throws ClientException {
-        // 准备参数
-        QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
-        // mock 方法
-        ClientException ex = new ClientException("isv.INVALID_PARAMETERS", "参数不正确", randomString());
-        when(client.getAcsResponse(any(AcsRequest.class))).thenThrow(ex);
-
-        // 调用,并断言异常
-        SmsCommonResult<?> result = smsClient.invoke(request, null);
-        // 断言
-        assertEquals(ex.getErrCode(), result.getApiCode());
-        assertEquals(ex.getErrMsg(), result.getApiMsg());
-        Assertions.assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR.getCode(), result.getCode());
-        Assertions.assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR.getMsg(), result.getMsg());
-        assertEquals(ex.getRequestId(), result.getApiRequestId());
-    }
-
-    @Test
-    public void testInvoke_success() throws ClientException {
-        // 准备参数
-        QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
-        Function<QuerySmsTemplateResponse, SmsTemplateRespDTO> responseConsumer = response -> {
-            SmsTemplateRespDTO data = new SmsTemplateRespDTO();
-            data.setId(response.getTemplateCode()).setContent(response.getTemplateContent());
-            data.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(response.getReason());
-            return data;
-        };
-        // mock 方法
-        QuerySmsTemplateResponse response = randomPojo(QuerySmsTemplateResponse.class, o -> {
-            o.setCode("OK");
-            o.setTemplateStatus(1); // 设置模板通过
-        });
-        when(client.getAcsResponse(any(AcsRequest.class))).thenReturn(response);
-
-        // 调用
-        SmsCommonResult<SmsTemplateRespDTO> result = smsClient.invoke(request, responseConsumer);
-        // 断言
-        assertEquals(response.getCode(), result.getApiCode());
-        assertEquals(response.getMessage(), result.getApiMsg());
-        assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
-        assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
-        assertEquals(response.getRequestId(), result.getApiRequestId());
-        // 断言结果
-        assertEquals(response.getTemplateCode(), result.getData().getId());
-        assertEquals(response.getTemplateContent(), result.getData().getContent());
-        assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
-        assertEquals(response.getReason(), result.getData().getAuditReason());
-    }
-
 }

+ 0 - 43
yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMappingTest.java

@@ -1,43 +0,0 @@
-package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
-
-import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
-import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
-import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
-import org.junit.jupiter.api.Test;
-import org.mockito.InjectMocks;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * {@link AliyunSmsCodeMapping} 的单元测试
- *
- * @author 芋道源码
- */
-public class AliyunSmsCodeMappingTest extends BaseMockitoUnitTest {
-
-    @InjectMocks
-    private AliyunSmsCodeMapping codeMapping;
-
-    @Test
-    public void testApply() {
-        assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply("OK"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("MissingAccessKeyId"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("isv.ACCOUNT_NOT_EXISTS"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("isv.ACCOUNT_ABNORMAL"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL, codeMapping.apply("isv.DAY_LIMIT_CONTROL"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID, codeMapping.apply("isv.SMS_CONTENT_ILLEGAL"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SMS_SIGN_ILLEGAL"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SIGN_NAME_ILLEGAL"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("isp.RAM_PERMISSION_DENY"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("isv.OUT_OF_SERVICE"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("isv.AMOUNT_NOT_ENOUGH"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("isv.SMS_TEMPLATE_ILLEGAL"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SMS_SIGNATURE_ILLEGAL"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply("isv.INVALID_PARAMETERS"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply("isv.INVALID_JSON_PARAM"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID, codeMapping.apply("isv.MOBILE_NUMBER_ILLEGAL"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("isv.TEMPLATE_MISSING_PARAMETERS"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("isv.BUSINESS_LIMIT_CONTROL"));
-    }
-
-}

+ 63 - 55
yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClientTest.java

@@ -1,13 +1,10 @@
 package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
 
 import cn.hutool.core.util.ReflectUtil;
-import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
-import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
@@ -31,7 +28,6 @@ import java.util.List;
 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
 import static org.junit.jupiter.api.Assertions.*;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.Mockito.when;
 
@@ -78,7 +74,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
     }
 
     @Test
-    public void testDoSendSms() throws Throwable {
+    public void testDoSendSms_success() throws Throwable {
         // 准备参数
         Long sendLogId = randomLongId();
         String mobile = randomString();
@@ -94,7 +90,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
             o.setSendStatusSet(sendStatuses);
             SendStatus sendStatus = new SendStatus();
             sendStatuses[0] = sendStatus;
-            sendStatus.setCode(TencentSmsClient.API_SUCCESS_CODE);
+            sendStatus.setCode(TencentSmsClient.API_CODE_SUCCESS);
             sendStatus.setMessage("send success");
             sendStatus.setSerialNo(serialNo);
         });
@@ -109,20 +105,58 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
         }))).thenReturn(response);
 
         // 调用
-        SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile,
-                apiTemplateId, templateParams);
+        SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
         // 断言
+        assertTrue(result.getSuccess());
+        assertEquals(response.getRequestId(), result.getApiRequestId());
         assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode());
         assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg());
-        assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
-        assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
+        assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo());
+    }
+
+    @Test
+    public void testDoSendSms_fail() throws Throwable {
+        // 准备参数
+        Long sendLogId = randomLongId();
+        String mobile = randomString();
+        String apiTemplateId = randomString();
+        List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
+                new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
+        String requestId = randomString();
+        String serialNo = randomString();
+        // mock 方法
+        SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> {
+            o.setRequestId(requestId);
+            SendStatus[] sendStatuses = new SendStatus[1];
+            o.setSendStatusSet(sendStatuses);
+            SendStatus sendStatus = new SendStatus();
+            sendStatuses[0] = sendStatus;
+            sendStatus.setCode("ERROR");
+            sendStatus.setMessage("send success");
+            sendStatus.setSerialNo(serialNo);
+        });
+        when(client.SendSms(argThat(request -> {
+            assertEquals(mobile, request.getPhoneNumberSet()[0]);
+            assertEquals(properties.getSignature(), request.getSignName());
+            assertEquals(apiTemplateId, request.getTemplateId());
+            assertEquals(toJsonString(ArrayUtils.toArray(new ArrayList<>(MapUtils.convertMap(templateParams).values()), String::valueOf)),
+                    toJsonString(request.getTemplateParamSet()));
+            assertEquals(sendLogId, ReflectUtil.getFieldValue(JsonUtils.parseObject(request.getSessionContext(), TencentSmsClient.SessionContext.class), "logId"));
+            return true;
+        }))).thenReturn(response);
+
+        // 调用
+        SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
+        // 断言
+        assertFalse(result.getSuccess());
         assertEquals(response.getRequestId(), result.getApiRequestId());
-        // 断言结果
-        assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getData().getSerialNo());
+        assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode());
+        assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg());
+        assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo());
     }
 
     @Test
-    public void testDoTParseSmsReceiveStatus() throws Throwable {
+    public void testParseSmsReceiveStatus() {
         // 准备参数
         String text = "[\n" +
                 "    {\n" +
@@ -139,7 +173,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
         // mock 方法
 
         // 调用
-        List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text);
+        List<SmsReceiveRespDTO> statuses = smsClient.parseSmsReceiveStatus(text);
         // 断言
         assertEquals(1, statuses.size());
         assertTrue(statuses.get(0).getSuccess());
@@ -152,7 +186,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
     }
 
     @Test
-    public void testDoGetSmsTemplate() throws Throwable {
+    public void testGetSmsTemplate() throws Throwable {
         // 准备参数
         Long apiTemplateId = randomLongId();
         String requestId = randomString();
@@ -173,50 +207,24 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
         }))).thenReturn(response);
 
         // 调用
-        SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId.toString());
+        SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId.toString());
         // 断言
-        assertEquals(TencentSmsClient.API_SUCCESS_CODE, result.getApiCode());
-        assertNull(result.getApiMsg());
-        assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
-        assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
-        assertEquals(response.getRequestId(), result.getApiRequestId());
-        // 断言结果
-        assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateId().toString(), result.getData().getId());
-        assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateContent(), result.getData().getContent());
-        assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
-        assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getData().getAuditReason());
-    }
-
-    @Test
-    public void testConvertSuccessTemplateStatus() {
-        testTemplateStatus(SmsTemplateAuditStatusEnum.SUCCESS, 0L);
-    }
-
-    @Test
-    public void testConvertCheckingTemplateStatus() {
-        testTemplateStatus(SmsTemplateAuditStatusEnum.CHECKING, 1L);
-    }
-
-    @Test
-    public void testConvertFailTemplateStatus() {
-        testTemplateStatus(SmsTemplateAuditStatusEnum.FAIL, -1L);
+        assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateId().toString(), result.getId());
+        assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateContent(), result.getContent());
+        assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus());
+        assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getAuditReason());
     }
 
     @Test
-    public void testConvertUnknownTemplateStatus() {
-        DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
-        templateStatus.setStatusCode(3L);
-        Long templateId = randomLongId();
-        // 调用,并断言结果
-        assertThrows(IllegalStateException.class, () -> smsClient.convertTemplateStatusDTO(templateStatus),
-                StrUtil.format("不能解析短信模版审核状态[3],模版id[{}]", templateId));
-    }
-
-    private void testTemplateStatus(SmsTemplateAuditStatusEnum expected, Long value) {
-        DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
-        templateStatus.setStatusCode(value);
-        SmsTemplateRespDTO result = smsClient.convertTemplateStatusDTO(templateStatus);
-        assertEquals(expected.getStatus(), result.getAuditStatus());
+    public void testConvertSmsTemplateAuditStatus() {
+        assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(),
+                smsClient.convertSmsTemplateAuditStatus(0));
+        assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(),
+                smsClient.convertSmsTemplateAuditStatus(1));
+        assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(),
+                smsClient.convertSmsTemplateAuditStatus(-1));
+        assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3),
+                "未知审核状态(3)");
     }
 
 }

+ 0 - 50
yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMappingTest.java

@@ -1,50 +0,0 @@
-package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
-
-import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
-import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
-import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
-import org.junit.jupiter.api.Test;
-import org.mockito.InjectMocks;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * {@link TencentSmsCodeMapping} 的单元测试
- *
- * @author : shiwp
- */
-public class TencentSmsCodeMappingTest extends BaseMockitoUnitTest {
-
-    @InjectMocks
-    private TencentSmsCodeMapping codeMapping;
-
-    @Test
-    public void testApply() {
-        assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply(TencentSmsClient.API_SUCCESS_CODE));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID, codeMapping.apply("FailedOperation.ContainSensitiveWord"));
-        assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("FailedOperation.JsonParseFail"));
-        assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("MissingParameter.EmptyPhoneNumberSet"));
-        assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("LimitExceeded.PhoneNumberCountLimit"));
-        assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("FailedOperation.FailResolvePacket"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("FailedOperation.InsufficientBalanceInSmsPackage"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_MARKET_LIMIT_CONTROL, codeMapping.apply("FailedOperation.MarketingSendTimeConstraint"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_BLACK, codeMapping.apply("FailedOperation.PhoneNumberInBlacklist"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("FailedOperation.SignatureIncorrectOrUnapproved"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("FailedOperation.MissingTemplateToModify"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("FailedOperation.TemplateIncorrectOrUnapproved"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID, codeMapping.apply("InvalidParameterValue.IncorrectPhoneNumber"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_APP_ID_INVALID, codeMapping.apply("InvalidParameterValue.SdkAppIdNotExist"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("InvalidParameterValue.TemplateParameterLengthLimit"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("InvalidParameterValue.TemplateParameterFormatError"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL, codeMapping.apply("LimitExceeded.PhoneNumberDailyLimit"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("LimitExceeded.PhoneNumberThirtySecondLimit"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("LimitExceeded.PhoneNumberOneHourLimit"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("UnauthorizedOperation.RequestPermissionDeny"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("FailedOperation.ForbidAddMarketingTemplates"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("FailedOperation.NotEnterpriseCertification"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("UnauthorizedOperation.IndividualUserMarketingSmsPermissionDeny"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_IP_DENY, codeMapping.apply("UnauthorizedOperation.RequestIpNotInWhitelist"));
-        assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("AuthFailure.SecretIdNotFound"));
-    }
-
-}

+ 4 - 0
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java

@@ -82,6 +82,10 @@ public interface ErrorCodeConstants {
     // ========== 短信模板 1-002-012-000 ==========
     ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在");
     ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_012_001, "已经存在编码为【{}】的短信模板");
+    ErrorCode SMS_TEMPLATE_API_ERROR = new ErrorCode(1_002_012_002, "短信 API 模板调用失败,原因是:{}");
+    ErrorCode SMS_TEMPLATE_API_AUDIT_CHECKING = new ErrorCode(1_002_012_003, "短信 API 模版无法使用,原因:审批中");
+    ErrorCode SMS_TEMPLATE_API_AUDIT_FAIL = new ErrorCode(1_002_012_004, "短信 API 模版无法使用,原因:审批不通过,{}");
+    ErrorCode SMS_TEMPLATE_API_NOT_FOUND = new ErrorCode(1_002_012_005, "短信 API 模版无法使用,原因:模版不存在");
 
     // ========== 短信发送 1-002-013-000 ==========
     ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在");

+ 16 - 16
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java

@@ -1,10 +1,9 @@
 package cn.iocoder.yudao.module.system.dal.dataobject.sms;
 
-import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum;
-import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
-import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
+import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum;
+import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
 import com.baomidou.mybatisplus.annotation.KeySequence;
 import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableName;
@@ -115,19 +114,20 @@ public class SmsLogDO extends BaseDO {
      * 发送时间
      */
     private LocalDateTime sendTime;
-    /**
-     * 发送结果的编码
-     *
-     * 枚举 {@link SmsFrameworkErrorCodeConstants}
-     */
-    private Integer sendCode;
-    /**
-     * 发送结果的提示
-     *
-     * 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants}
-     * 异常情况下,通过格式化 Exception 的提示存储
-     */
-    private String sendMsg;
+    // TODO 芋艿:短信
+//    /**
+//     * 发送结果的编码
+//     *
+//     * 枚举 {@link SmsFrameworkErrorCodeConstants}
+//     */
+//    private Integer sendCode;
+//    /**
+//     * 发送结果的提示
+//     *
+//     * 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants}
+//     * 异常情况下,通过格式化 Exception 的提示存储
+//     */
+//    private String sendMsg;
     /**
      * 短信 API 发送结果的编码
      *

+ 6 - 5
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java

@@ -37,15 +37,15 @@ public interface SmsLogService {
      * 更新日志的发送结果
      *
      * @param id 日志编号
-     * @param sendCode 发送结果的编码
-     * @param sendMsg 发送结果的提示
+     * @param success 发送是否成功
      * @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);
+    void updateSmsSendResult(Long id, Boolean success,
+                             String apiSendCode, String apiSendMsg,
+                             String apiRequestId, String apiSerialNo);
 
     /**
      * 更新日志的接收结果
@@ -56,7 +56,8 @@ public interface SmsLogService {
      * @param apiReceiveCode API 接收结果的编码
      * @param apiReceiveMsg API 接收结果的说明
      */
-    void updateSmsReceiveResult(Long id, Boolean success, LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg);
+    void updateSmsReceiveResult(Long id, Boolean success,
+                                LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg);
 
     /**
      * 获得短信日志分页

+ 5 - 7
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java

@@ -1,12 +1,11 @@
 package cn.iocoder.yudao.module.system.service.sms;
 
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
+import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsLogMapper;
-import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum;
 import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
 import lombok.extern.slf4j.Slf4j;
@@ -55,13 +54,12 @@ public class SmsLogServiceImpl implements SmsLogService {
     }
 
     @Override
-    public void updateSmsSendResult(Long id, Integer sendCode, String sendMsg,
+    public void updateSmsSendResult(Long id, Boolean success,
                                     String apiSendCode, String apiSendMsg,
                                     String apiRequestId, String apiSerialNo) {
-        SmsSendStatusEnum sendStatus = CommonResult.isSuccess(sendCode) ?
-                SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE;
-        smsLogMapper.updateById(SmsLogDO.builder().id(id).sendStatus(sendStatus.getStatus())
-                .sendTime(LocalDateTime.now()).sendCode(sendCode).sendMsg(sendMsg)
+        SmsSendStatusEnum sendStatus = success ? SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE;
+        smsLogMapper.updateById(SmsLogDO.builder().id(id)
+                .sendStatus(sendStatus.getStatus()).sendTime(LocalDateTime.now())
                 .apiSendCode(apiSendCode).apiSendMsg(apiSendMsg)
                 .apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build());
     }

+ 14 - 6
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.system.service.sms;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.exceptions.ExceptionUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
@@ -8,7 +9,6 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
@@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
 import cn.iocoder.yudao.module.system.service.member.MemberService;
 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
 import com.google.common.annotations.VisibleForTesting;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
 import javax.annotation.Resource;
@@ -35,6 +36,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
  * @author 芋道源码
  */
 @Service
+@Slf4j
 public class SmsSendServiceImpl implements SmsSendService {
 
     @Resource
@@ -158,11 +160,17 @@ public class SmsSendServiceImpl implements SmsSendService {
         SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId());
         Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId());
         // 发送短信
-        SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(),
-                message.getApiTemplateId(), message.getTemplateParams());
-        smsLogService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(),
-                sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(),
-                sendResult.getData() != null ? sendResult.getData().getSerialNo() : null);
+        try {
+            SmsSendRespDTO sendResponse = smsClient.sendSms(message.getLogId(), message.getMobile(),
+                    message.getApiTemplateId(), message.getTemplateParams());
+            smsLogService.updateSmsSendResult(message.getLogId(), sendResponse.getSuccess(),
+                    sendResponse.getApiCode(), sendResponse.getApiMsg(),
+                    sendResponse.getApiRequestId(), sendResponse.getSerialNo());
+        } catch (Throwable ex) {
+            log.error("[doSendSms][发送短信异常,日志编号({})]", message.getLogId(), ex);
+            smsLogService.updateSmsSendResult(message.getLogId(), false,
+                    "EXCEPTION", ExceptionUtil.getRootCauseMessage(ex), null, null);
+        }
     }
 
     @Override

+ 22 - 5
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java

@@ -1,12 +1,14 @@
 package cn.iocoder.yudao.module.system.service.sms;
 
+import cn.hutool.core.exceptions.ExceptionUtil;
+import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.ReUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
+import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
@@ -21,11 +23,11 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.cache.annotation.Cacheable;
 import org.springframework.stereotype.Service;
-import org.springframework.util.Assert;
 
 import javax.annotation.Resource;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.regex.Pattern;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -171,9 +173,24 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
         // 获得短信模板
         SmsClient smsClient = smsChannelService.getSmsClient(channelId);
         Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId));
-        SmsCommonResult<SmsTemplateRespDTO> templateResult = smsClient.getSmsTemplate(apiTemplateId);
-        // 校验短信模板是否正确
-        templateResult.checkError();
+        SmsTemplateRespDTO template;
+        try {
+            template = smsClient.getSmsTemplate(apiTemplateId);
+        } catch (Throwable ex) {
+            throw exception(SMS_TEMPLATE_API_ERROR, ExceptionUtil.getRootCauseMessage(ex));
+        }
+        // 校验短信模版
+        if (template == null) {
+            throw exception(SMS_TEMPLATE_API_NOT_FOUND);
+        }
+        if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.CHECKING.getStatus())) {
+            throw exception(SMS_TEMPLATE_API_AUDIT_CHECKING);
+        }
+        if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.FAIL.getStatus())) {
+            throw exception(SMS_TEMPLATE_API_AUDIT_FAIL, template.getAuditReason());
+        }
+        Assert.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.SUCCESS.getStatus(),
+                String.format("短信模板(%s) 审核状态(%d) 不正确", apiTemplateId, template.getAuditStatus()));
     }
 
     @Override

+ 4 - 7
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java

@@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.service.sms;
 
 import cn.hutool.core.map.MapUtil;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
-import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
@@ -172,22 +171,20 @@ public class SmsLogServiceImplTest extends BaseDbUnitTest {
         smsLogMapper.insert(dbSmsLog);
         // 准备参数
         Long id = dbSmsLog.getId();
-        Integer sendCode = randomInteger();
-        String sendMsg = randomString();
+        Boolean success = randomBoolean();
         String apiSendCode = randomString();
         String apiSendMsg = randomString();
         String apiRequestId = randomString();
         String apiSerialNo = randomString();
 
         // 调用
-        smsLogService.updateSmsSendResult(id, sendCode, sendMsg,
+        smsLogService.updateSmsSendResult(id, success,
                 apiSendCode, apiSendMsg, apiRequestId, apiSerialNo);
         // 断言
         dbSmsLog = smsLogMapper.selectById(id);
-        assertEquals(CommonResult.isSuccess(sendCode) ? SmsSendStatusEnum.SUCCESS.getStatus()
-                : SmsSendStatusEnum.FAILURE.getStatus(), dbSmsLog.getSendStatus());
+        assertEquals(success ? SmsSendStatusEnum.SUCCESS.getStatus() : SmsSendStatusEnum.FAILURE.getStatus(),
+                dbSmsLog.getSendStatus());
         assertNotNull(dbSmsLog.getSendTime());
-        assertEquals(sendMsg, dbSmsLog.getSendMsg());
         assertEquals(apiSendCode, dbSmsLog.getApiSendCode());
         assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg());
         assertEquals(apiRequestId, dbSmsLog.getApiRequestId());

+ 4 - 6
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java

@@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
@@ -244,15 +243,14 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
 
     @Test
     @SuppressWarnings("unchecked")
-    public void testDoSendSms() {
+    public void testDoSendSms() throws Throwable {
         // 准备参数
         SmsSendMessage message = randomPojo(SmsSendMessage.class);
         // mock SmsClientFactory 的方法
         SmsClient smsClient = spy(SmsClient.class);
         when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
         // mock SmsClient 的方法
-        SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class);
-        sendResult.setData(randomPojo(SmsSendRespDTO.class));
+        SmsSendRespDTO sendResult = randomPojo(SmsSendRespDTO.class);
         when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()),
                 eq(message.getTemplateParams()))).thenReturn(sendResult);
 
@@ -260,8 +258,8 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
         smsService.doSendSms(message);
         // 断言
         verify(smsLogService).updateSmsSendResult(eq(message.getLogId()),
-                eq(sendResult.getCode()), eq(sendResult.getMsg()), eq(sendResult.getApiCode()),
-                eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getData().getSerialNo()));
+                eq(sendResult.getSuccess()), eq(sendResult.getApiCode()),
+                eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getSerialNo()));
     }
 
     @Test

+ 7 - 8
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java

@@ -1,13 +1,12 @@
 package cn.iocoder.yudao.module.system.service.sms;
 
 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
-import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
-import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
+import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO;
 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
@@ -65,7 +64,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
 
     @Test
     @SuppressWarnings("unchecked")
-    public void testCreateSmsTemplate_success() {
+    public void testCreateSmsTemplate_success() throws Throwable {
         // 准备参数
         SmsTemplateCreateReqVO reqVO = randomPojo(SmsTemplateCreateReqVO.class, o -> {
             o.setContent("正在进行登录操作{operation},您的验证码是{code}");
@@ -80,8 +79,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
         when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO);
         // mock 获得 API 短信模板成功
         when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient);
-        when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class,
-                o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode())));
+        when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(
+                randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus())));
 
         // 调用
         Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO);
@@ -96,7 +95,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
 
     @Test
     @SuppressWarnings("unchecked")
-    public void testUpdateSmsTemplate_success() {
+    public void testUpdateSmsTemplate_success() throws Throwable {
         // mock 数据
         SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
         smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
@@ -115,8 +114,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
         when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO);
         // mock 获得 API 短信模板成功
         when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient);
-        when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class,
-                o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode())));
+        when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(
+                randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus())));
 
         // 调用
         smsTemplateService.updateSmsTemplate(reqVO);