ソースを参照

新增 阿里云短信测试样例
修改 阿里云短信返回响应解析

18339543638 2 年 前
コミット
3b5bd34ee3

+ 16 - 0
tr-plugins/tr-spring-boot-starter-plugin-sms/src/main/java/cn/tr/plugin/sms/bo/SmsCodeMapping.java

@@ -0,0 +1,16 @@
+package cn.tr.plugin.sms.bo;
+
+import cn.tr.core.exception.BaseCode;
+
+import java.util.function.Function;
+
+/**
+ * 将 API 的错误码,转换为通用的错误码
+ *
+ * @see SmsCommonResult
+ * @see
+ *
+ * @author 芋道源码
+ */
+public interface SmsCodeMapping extends Function<String, BaseCode> {
+}

+ 73 - 0
tr-plugins/tr-spring-boot-starter-plugin-sms/src/main/java/cn/tr/plugin/sms/bo/SmsCommonResult.java

@@ -0,0 +1,73 @@
+package cn.tr.plugin.sms.bo;
+
+import cn.hutool.core.exceptions.ExceptionUtil;
+import cn.hutool.core.lang.Assert;
+import cn.tr.core.exception.BaseCode;
+import cn.tr.core.pojo.CommonResult;
+import cn.tr.plugin.sms.enums.SmsFrameworkErrorCodeConstants;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+
+/**
+ * 短信的 CommonResult 拓展类
+ *
+ * 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段
+ *
+ * 另外,一些短信平台(例如说阿里云、腾讯云)会返回一个请求编号,用于排查请求失败的问题,我们设置到 {@link #apiRequestId} 字段
+ *
+ * @author 芋道源码
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+@Accessors(chain = 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);
+        result.setErrorMsg(apiMsg);
+        result.setApiRequestId(apiRequestId);
+        result.setData(data);
+        // 翻译错误码
+        if (codeMapping != null) {
+            BaseCode errorCode = codeMapping.apply(apiCode);
+            if (errorCode == null) {
+                errorCode = SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
+            }
+            result.setCode(errorCode.getErrCode());
+            result.setErrorMsg(errorCode.getErrCode());
+        }
+        return result;
+    }
+
+    public static <T> SmsCommonResult<T> error(Throwable ex) {
+        SmsCommonResult<T> result = new SmsCommonResult<>();
+        result.setCode(SmsFrameworkErrorCodeConstants.EXCEPTION.getErrCode());
+        result.setErrorMsg(ExceptionUtil.getRootCauseMessage(ex));
+        return result;
+    }
+
+}

+ 2 - 0
tr-plugins/tr-spring-boot-starter-plugin-sms/src/main/java/cn/tr/plugin/sms/bo/SmsSendRespBO.java

@@ -1,6 +1,7 @@
 package cn.tr.plugin.sms.bo;
 
 import lombok.Data;
+import lombok.experimental.Accessors;
 
 
 /**
@@ -8,6 +9,7 @@ import lombok.Data;
  *
  * @author 芋道源码
  */
+@Accessors(chain = true)
 @Data
 public class SmsSendRespBO {
 

+ 2 - 2
tr-plugins/tr-spring-boot-starter-plugin-sms/src/main/java/cn/tr/plugin/sms/config/SmsClient.java

@@ -1,6 +1,6 @@
 package cn.tr.plugin.sms.config;
 
-import cn.tr.core.pojo.CommonResult;
+import cn.tr.plugin.sms.bo.SmsCommonResult;
 import cn.tr.plugin.sms.bo.SmsSendRespBO;
 
 /**
@@ -23,5 +23,5 @@ public interface SmsClient<SendConfig extends SmsClientSendConfig> {
      * @param sendConfig 发送配置
      * @return 短信发送结果
      */
-     CommonResult<SmsSendRespBO> sendSms(SendConfig sendConfig);
+    SmsCommonResult<SmsSendRespBO> sendSms(SendConfig sendConfig);
 }

+ 16 - 16
tr-plugins/tr-spring-boot-starter-plugin-sms/src/main/java/cn/tr/plugin/sms/config/ali/AliSmsClient.java

@@ -1,23 +1,18 @@
 package cn.tr.plugin.sms.config.ali;
 
 import cn.hutool.core.util.StrUtil;
-import cn.tr.core.exception.TRExcCode;
-import cn.tr.core.pojo.CommonResult;
 import cn.tr.core.utils.JsonUtils;
+import cn.tr.plugin.sms.bo.SmsCommonResult;
 import cn.tr.plugin.sms.bo.SmsSendRespBO;
 import cn.tr.plugin.sms.config.AbstractSmsClient;
 import com.aliyuncs.DefaultAcsClient;
 import com.aliyuncs.IAcsClient;
 import com.aliyuncs.exceptions.ClientException;
 import com.aliyuncs.profile.DefaultProfile;
-import com.google.gson.Gson;
 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
 import lombok.extern.slf4j.Slf4j;
 
-import java.util.HashMap;
-import java.util.Map;
-
 /**
  * @ClassName : AliSmsClient
  * @Description :
@@ -27,8 +22,10 @@ import java.util.Map;
 @Slf4j
 public class AliSmsClient extends AbstractSmsClient<AliSmsClientConfig,AliSmsClientSendConfig> {
     private IAcsClient client;
+    private final AliyunSmsCodeMapping smsCodeMapping;
     public AliSmsClient(String id, AliSmsClientConfig clientConfig) {
         super(id, clientConfig);
+        this.smsCodeMapping=new AliyunSmsCodeMapping();
     }
 
     @Override
@@ -38,7 +35,7 @@ public class AliSmsClient extends AbstractSmsClient<AliSmsClientConfig,AliSmsCli
     }
 
     @Override
-    public CommonResult<SmsSendRespBO> sendSms(AliSmsClientSendConfig sendConfig) {
+    public SmsCommonResult<SmsSendRespBO> sendSms(AliSmsClientSendConfig sendConfig) {
         SendSmsRequest sendSmsRequest =new SendSmsRequest();
         sendSmsRequest.setPhoneNumbers(sendConfig.getPhoneNumber());
         sendSmsRequest.setSignName(sendConfig.getSignName());
@@ -46,18 +43,21 @@ public class AliSmsClient extends AbstractSmsClient<AliSmsClientConfig,AliSmsCli
         sendSmsRequest.setTemplateParam(JsonUtils.toJsonString(sendConfig.getTemplateParam()));
         sendSmsRequest.setSmsUpExtendCode(sendConfig.getSmsUpExtendCode());
         sendSmsRequest.setOutId(sendConfig.getOutId());
+        SendSmsResponse response=null;
         try {
-            SendSmsResponse sendSmsResponse = client.getAcsResponse(sendSmsRequest);
+            response = client.getAcsResponse(sendSmsRequest);
         } catch (ClientException e) {
-            if (StrUtil.equals(e.getErrCode(),"isv.ACCOUNT_NOT_EXISTS")) {
-                return CommonResult.error(TRExcCode.USER_ERROR_A0201,e.getErrMsg());
-            }
-            if (StrUtil.equals(e.getErrCode(), "isv.AMOUNT_NOT_ENOUGH")) {
-                return CommonResult.error(TRExcCode.USER_ERROR_A0601,e.getErrMsg());
-            }
-            log.warn("[AliSms] send msg confront a error,",e);
+            return SmsCommonResult.build(e.getErrCode(), formatResultMsg(e), e.getRequestId(), null, smsCodeMapping);
         }
-        return CommonResult.success();
 
+        return SmsCommonResult.build(response.getCode(), response.getMessage(), response.getRequestId(), new SmsSendRespBO().setSerialNo(response.getBizId()), smsCodeMapping);
+
+    }
+
+    private static String formatResultMsg(ClientException ex) {
+        if (StrUtil.isEmpty(ex.getErrorDescription())) {
+            return ex.getErrMsg();
+        }
+        return ex.getErrMsg() + " => " + ex.getErrorDescription();
     }
 }

+ 41 - 0
tr-plugins/tr-spring-boot-starter-plugin-sms/src/main/java/cn/tr/plugin/sms/config/ali/AliyunSmsCodeMapping.java

@@ -0,0 +1,41 @@
+package cn.tr.plugin.sms.config.ali;
+import cn.tr.core.exception.BaseCode;
+import cn.tr.core.exception.TRExcCode;
+import cn.tr.plugin.sms.bo.SmsCodeMapping;
+import cn.tr.plugin.sms.enums.SmsFrameworkErrorCodeConstants;
+
+/**
+ * 阿里云的 SmsCodeMapping 实现类
+ *
+ * 参见 https://help.aliyun.com/document_detail/101346.htm 文档
+ *
+ * @author 芋道源码
+ */
+public class AliyunSmsCodeMapping implements SmsCodeMapping {
+
+    @Override
+    public BaseCode apply(String apiCode) {
+        switch (apiCode) {
+            case "OK": return TRExcCode.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;
+        }
+    }
+
+}

+ 58 - 0
tr-plugins/tr-spring-boot-starter-plugin-sms/src/main/java/cn/tr/plugin/sms/enums/SmsFrameworkErrorCodeConstants.java

@@ -0,0 +1,58 @@
+package cn.tr.plugin.sms.enums;
+
+
+import cn.tr.core.exception.BaseCode;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 短信框架的错误码枚举
+ *
+ * 短信框架,使用 2-001-000-000 段
+ *
+ * @author 芋道源码
+ */
+@Getter
+@AllArgsConstructor
+public enum  SmsFrameworkErrorCodeConstants implements BaseCode {
+
+    SMS_UNKNOWN ("2001000000", "未知错误,需要解析"),
+
+    // ========== 权限 / 限流等相关 2001000100 ==========
+
+    SMS_PERMISSION_DENY("2001000100", "没有发送短信的权限"),
+     SMS_IP_DENY ("2001000100", "IP 不允许发送短信"),
+
+    // 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。
+     SMS_SEND_BUSINESS_LIMIT_CONTROL("2001000102", "指定手机的发送限流"),
+    // 阿里云:已经达到您在控制台设置的短信日发送量限额值。在国内消息设置 > 安全设置,修改发送总量阈值。
+     SMS_SEND_DAY_LIMIT_CONTROL("2001000103", "每天的发送限流"),
+
+     SMS_SEND_CONTENT_INVALID("2001000104", "短信内容有敏感词"),
+
+    // 腾讯云:为避免骚扰用户,营销短信只允许在8点到22点发送。
+     SMS_SEND_MARKET_LIMIT_CONTROL("2001000105", "营销短信发送时间限制"),
+
+    // ========== 模板相关 2001000200 ==========
+     SMS_TEMPLATE_INVALID ("2001000200", "短信模板不合法"), // 包括短信模板不存在
+     SMS_TEMPLATE_PARAM_ERROR("2001000201", "模板参数不正确"),
+
+    // ========== 签名相关 2001000300 ==========
+     SMS_SIGN_INVALID ("2001000300", "短信签名不可用"),
+
+    // ========== 账户相关 2001000400 ==========
+     SMS_ACCOUNT_MONEY_NOT_ENOUGH ("2001000400", "账户余额不足"),
+     SMS_ACCOUNT_INVALID ("2001000401", "apiKey 不存在"),
+
+    // ========== 其它相关 2001000900 开头 ==========
+     SMS_API_PARAM_ERROR ("2001000900", "请求参数缺失"),
+     SMS_MOBILE_INVALID("2001000901", "手机格式不正确"),
+     SMS_MOBILE_BLACK("2001000902", "手机号在黑名单中"),
+    SMS_APP_ID_INVALID("2001000903", "SdkAppId不合法"),
+
+    EXCEPTION ("2001000999", "调用异常");
+
+    private String errCode;
+    private String errMsg;
+
+}

+ 11 - 4
tr-plugins/tr-spring-boot-starter-plugin-sms/src/test/java/cn/tr/plugin/sms/SmsTest.java

@@ -2,6 +2,8 @@ package cn.tr.plugin.sms;
 
 import cn.hutool.core.map.MapUtil;
 import cn.tr.core.pojo.CommonResult;
+import cn.tr.core.utils.JsonUtils;
+import cn.tr.plugin.sms.bo.SmsCommonResult;
 import cn.tr.plugin.sms.bo.SmsSendRespBO;
 import cn.tr.plugin.sms.config.ali.AliSmsClient;
 import cn.tr.plugin.sms.config.ali.AliSmsClientConfig;
@@ -10,6 +12,8 @@ import cn.tr.plugin.test.ut.BaseMockitoUnitTest;
 import org.junit.jupiter.api.Test;
 
 import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
 
 /**
  * @ClassName : SmsTest
@@ -25,16 +29,19 @@ public class SmsTest extends BaseMockitoUnitTest {
         AliSmsClientConfig aliSmsClientConfig = new AliSmsClientConfig();
         aliSmsClientConfig.setAccessKeyId("LTAI4G7FA9ytMc76oNkJ45YJ");
         aliSmsClientConfig.setAccessKeySecret("R7hOvMfiHb0PYroDqUDXAYgB9htQss");
+        aliSmsClientConfig.setRegionId("cn-hangzhou");
         AliSmsClient aliSmsClient = new AliSmsClient("123",aliSmsClientConfig);
+        aliSmsClient.doInit(aliSmsClientConfig);
         AliSmsClientSendConfig sendConfig = new AliSmsClientSendConfig();
         sendConfig.setPhoneNumber("18339543638");
         sendConfig.setSignName("驼人医疗");
         sendConfig.setTemplateCode("SMS_247815102");
-        HashMap<String, String> map = new HashMap<>();
-        map.put("code","456");
+        Map<String, String> map = new HashMap<>();
+        map.put("patientCode","1234");
+        map.put("code","1234");
         sendConfig.setTemplateParam(map);
 
-        CommonResult<SmsSendRespBO> response = aliSmsClient.sendSms(sendConfig);
-
+        SmsCommonResult<SmsSendRespBO> result = aliSmsClient.sendSms(sendConfig);
+        System.out.println(JsonUtils.toJsonString(result));
     }
 }