Sfoglia il codice sorgente

magic 参数处理&AbstractSmsClient模版优化

FinallySays 2 anni fa
parent
commit
eb147a92ff

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

@@ -31,7 +31,7 @@ public abstract class AbstractSmsClient implements SmsClient {
     protected final SmsCodeMapping codeMapping;
 
     public AbstractSmsClient(SmsChannelProperties properties, SmsCodeMapping codeMapping) {
-        this.properties = properties;
+        this.properties = prepareProperties(properties);
         this.codeMapping = codeMapping;
     }
 
@@ -54,11 +54,21 @@ public abstract class AbstractSmsClient implements SmsClient {
             return;
         }
         log.info("[refresh][配置({})发生变化,重新初始化]", properties);
-        this.properties = 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();

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

@@ -14,6 +14,7 @@ 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.sms.core.property.TencentSmsChannelProperties;
 import com.fasterxml.jackson.annotation.JsonFormat;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.google.common.annotations.VisibleForTesting;
@@ -42,21 +43,33 @@ public class TencentSmsClient extends AbstractSmsClient {
 
     private SmsClient client;
 
+    /**
+     * 调用成功 code
+     */
+    public static final String API_SUCCESS_CODE = "Ok";
+
+    /**
+     * REGION, 使用南京
+     */
+    private static final String ENDPOINT = "ap-nanjing";
+
+    /**
+     * 是否国际/港澳台短信:
+     * 0:表示国内短信。
+     * 1:表示国际/港澳台短信。
+     */
+    private static final long INTERNATIONAL = 0L;
+
     public TencentSmsClient(SmsChannelProperties properties) {
-        // 腾讯云发放短信的时候,需要额外的参数 sdkAppId。考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
-        // 因此,这边需要使用 TencentSmsChannelProperties 做拆分,重新封装到 properties 内。
-        super(TencentSmsChannelProperties.build(properties), new TencentSmsCodeMapping());
+        super(properties, new TencentSmsCodeMapping());
         Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
     }
 
     @Override
     protected void doInit() {
-        // init 或者 refresh 时,需要重新封装 properties
-        properties = TencentSmsChannelProperties.build(properties);
         // 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId,secretKey
         Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret());
-        // TODO @FinallySays:那把 ap-nanjing 枚举下到这个类的静态变量里哈。
-        client = new SmsClient(credential, "ap-nanjing");
+        client = new SmsClient(credential, ENDPOINT);
     }
 
     @Override
@@ -73,6 +86,20 @@ public class TencentSmsClient extends AbstractSmsClient {
                 });
     }
 
+
+    /**
+     * 腾讯云发放短信的时候,需要额外的参数 sdkAppId。
+     * 考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
+     * 因此,这边需要使用 TencentSmsChannelProperties 做拆分,重新封装到 properties 内。
+     *
+     * @param properties 数据库中存储的短信渠道配置
+     * @return TencentSmsChannelProperties
+     */
+    @Override
+    protected SmsChannelProperties prepareProperties(SmsChannelProperties properties) {
+        return TencentSmsChannelProperties.build(properties);
+    }
+
     /**
      * 调用腾讯云 SDK 发送短信
      *
@@ -113,7 +140,7 @@ public class TencentSmsClient extends AbstractSmsClient {
         return CollectionUtils.convertList(callback, status -> {
             SmsReceiveRespDTO data = new SmsReceiveRespDTO();
             data.setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription());
-            data.setReceiveTime(status.getReceiveTime()).setSuccess("SUCCESS".equalsIgnoreCase(status.getStatus()));
+            data.setReceiveTime(status.getReceiveTime()).setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus()));
             data.setMobile(status.getMobile()).setSerialNo(status.getSerialNo());
             SessionContext context;
             Long logId;
@@ -130,7 +157,7 @@ public class TencentSmsClient extends AbstractSmsClient {
                 this::doGetSmsTemplate0,
                 response -> {
                     SmsTemplateRespDTO data = convertTemplateStatusDTO(response.getDescribeTemplateStatusSet()[0]);
-                    return SmsCommonResult.build("Ok", null, response.getRequestId(), data, codeMapping);
+                    return SmsCommonResult.build(API_SUCCESS_CODE, null, response.getRequestId(), data, codeMapping);
                 });
     }
 
@@ -171,8 +198,7 @@ public class TencentSmsClient extends AbstractSmsClient {
         DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest();
         request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)});
         // 地区 0:表示国内短信。1:表示国际/港澳台短信。
-        // TODO @FinallySays:那把 0L 枚举下到这个类的静态变量里哈。
-        request.setInternational(0L);
+        request.setInternational(INTERNATIONAL);
         return request;
     }
 
@@ -206,6 +232,11 @@ public class TencentSmsClient extends AbstractSmsClient {
     @Data
     private static class SmsReceiveStatus {
 
+        /**
+         * 短信接受成功 code
+         */
+        public static final String SUCCESS_CODE = "SUCCESS";
+
         /**
          * 用户实际接收到短信的时间
          */
@@ -270,27 +301,4 @@ public class TencentSmsClient extends AbstractSmsClient {
         R apply(T t) throws TencentCloudSDKException;
     }
 
-    // TODO @FinallySays:要不单独一个类,不用作为内部类哈。这样,可能一看就知道,哟,腾讯短信是特殊的
-    @Data
-    private static class TencentSmsChannelProperties extends SmsChannelProperties {
-
-        private String 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;
-        }
-    }
-
-
 }

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

@@ -19,7 +19,7 @@ public class TencentSmsCodeMapping implements SmsCodeMapping {
     @Override
     public ErrorCode apply(String apiCode) {
         switch (apiCode) {
-            case "Ok": return GlobalErrorCodeConstants.SUCCESS;
+            case TencentSmsClient.API_SUCCESS_CODE: return GlobalErrorCodeConstants.SUCCESS;
             case "FailedOperation.ContainSensitiveWord": return SMS_SEND_CONTENT_INVALID;
             case "FailedOperation.JsonParseFail":
             case "MissingParameter.EmptyPhoneNumberSet":

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

@@ -0,0 +1,41 @@
+package cn.iocoder.yudao.framework.sms.core.property;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.lang.Assert;
+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;
+    }
+}

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

@@ -64,6 +64,19 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
         assertNotSame(client, ReflectUtil.getFieldValue(smsClient, "client"));
     }
 
+    @Test
+    public void testRefresh() {
+        // 准备参数
+        SmsChannelProperties p = new SmsChannelProperties()
+                .setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错
+                .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错
+                .setSignature("芋道源码");
+        // 调用
+        smsClient.refresh(p);
+        // 断言
+        assertNotSame(client, ReflectUtil.getFieldValue(smsClient, "client"));
+    }
+
     @Test
     public void testDoSendSms() throws Throwable {
         // 准备参数
@@ -81,7 +94,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
             o.setSendStatusSet(sendStatuses);
             SendStatus sendStatus = new SendStatus();
             sendStatuses[0] = sendStatus;
-            sendStatus.setCode("Ok");
+            sendStatus.setCode(TencentSmsClient.API_SUCCESS_CODE);
             sendStatus.setMessage("send success");
             sendStatus.setSerialNo(serialNo);
         });
@@ -162,7 +175,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
         // 调用
         SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId.toString());
         // 断言
-        assertEquals("Ok", result.getApiCode());
+        assertEquals(TencentSmsClient.API_SUCCESS_CODE, result.getApiCode());
         assertNull(result.getApiMsg());
         assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
         assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
@@ -174,12 +187,23 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
         assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getData().getAuditReason());
     }
 
-    // TODO @FinallySays:这个单测,按道理说应该是写成 4 个方法,每个对应一种情况。
     @Test
-    public void testConvertTemplateStatusDTO() {
+    public void testConvertSuccessTemplateStatus() {
         testTemplateStatus(SmsTemplateAuditStatusEnum.SUCCESS, 0L);
+    }
+
+    @Test
+    public void testConvertCheckingTemplateStatus() {
         testTemplateStatus(SmsTemplateAuditStatusEnum.CHECKING, 1L);
+    }
+
+    @Test
+    public void testConvertFailTemplateStatus() {
         testTemplateStatus(SmsTemplateAuditStatusEnum.FAIL, -1L);
+    }
+
+    @Test
+    public void testConvertUnknownTemplateStatus() {
         DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
         templateStatus.setStatusCode(3L);
         Long templateId = randomLongId();

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

@@ -20,7 +20,7 @@ public class TencentSmsCodeMappingTest extends BaseMockitoUnitTest {
 
     @Test
     public void testApply() {
-        assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply("Ok"));
+        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"));