Explorar el Código

【代码优化】SYSTEM:七牛云短信的接入

YunaiV hace 9 meses
padre
commit
d6ecc032c2

+ 45 - 47
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java

@@ -1,11 +1,9 @@
 package cn.iocoder.yudao.module.system.framework.sms.core.client.impl;
 
 import cn.hutool.core.collection.CollStreamUtil;
-import cn.hutool.core.collection.ListUtil;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.lang.Assert;
-import cn.hutool.core.util.ObjUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.crypto.SecureUtil;
 import cn.hutool.crypto.digest.HmacAlgorithm;
@@ -22,6 +20,9 @@ import com.google.common.annotations.VisibleForTesting;
 import lombok.extern.slf4j.Slf4j;
 
 import java.util.*;
+import java.util.function.Function;
+
+import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 
 /**
  * 七牛云短信客户端的实现类
@@ -34,19 +35,12 @@ public class QiniuSmsClient extends AbstractSmsClient {
 
     private static final String HOST = "sms.qiniuapi.com";
 
-    private static final String PATH = "/v1/message/single";
-
-    private static final String TEMPLATE_PATH  = "/v1/template";
-
     public QiniuSmsClient(SmsChannelProperties properties) {
         super(properties);
         Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
         Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
     }
 
-    protected void doInit() {
-    }
-
     public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
                                   List<KeyValue<String, Object>> templateParams) throws Throwable {
         // 1. 执行请求
@@ -56,16 +50,16 @@ public class QiniuSmsClient extends AbstractSmsClient {
         body.put("mobile", mobile);
         body.put("parameters", CollStreamUtil.toMap(templateParams, KeyValue::getKey, KeyValue::getValue));
         body.put("seq", Long.toString(sendLogId));
+        JSONObject response = request("POST", body, "/v1/message/single");
 
-        JSONObject response = request("POST", body, PATH);
         // 2. 解析请求
-        if (ObjectUtil.isNotEmpty(response.getStr("error"))){//短信请求失败
+        if (ObjectUtil.isNotEmpty(response.getStr("error"))) {
+            // 短信请求失败
             return new SmsSendRespDTO().setSuccess(false)
                     .setApiCode(response.getStr("error"))
                     .setApiRequestId(response.getStr("request_id"))
                     .setApiMsg(response.getStr("message"));
         }
-
         return new SmsSendRespDTO().setSuccess(response.containsKey("message_id"))
                 .setSerialNo(response.getStr("message_id"));
     }
@@ -81,62 +75,66 @@ public class QiniuSmsClient extends AbstractSmsClient {
      */
     private JSONObject request(String httpMethod, LinkedHashMap<String, Object> body, String path) {
         String signDate = DateUtil.date().setTimeZone(TimeZone.getTimeZone("UTC")).toString("yyyyMMdd'T'HHmmss'Z'");
-        //请求头
+        // 1. 请求头
         Map<String, String> header = new HashMap<>(4);
         header.put("HOST", HOST);
-        header.put("Authorization", getSignature(httpMethod, HOST, path, body != null ? JSONUtil.toJsonStr(body) : "", signDate));
+        header.put("Authorization", getSignature(httpMethod, path, body != null ? JSONUtil.toJsonStr(body) : "", signDate));
         header.put("Content-Type", "application/json");
         header.put("X-Qiniu-Date", signDate);
 
-        String responseBody ="";
-        if (Objects.equals(httpMethod, "POST")){// POST 发送短消息用POST请求
+        // 2. 发起请求
+        String responseBody;
+        if (Objects.equals(httpMethod, "POST")){
             responseBody = HttpUtils.post("https://" + HOST + path, header, JSONUtil.toJsonStr(body));
-        }else { // GET 查询template状态用GET请求
+        } else {
             responseBody = HttpUtils.get("https://" + HOST + path, header);
         }
         return JSONUtil.parseObj(responseBody);
     }
 
-    public String getSignature(String method, String host, String path, String body, String signDate) {
+    private String getSignature(String method, String path, String body, String signDate) {
         StringBuilder dataToSign = new StringBuilder();
-        dataToSign.append(method.toUpperCase()).append(" ").append(path);
-        dataToSign.append("\nHost: ").append(host);
-        dataToSign.append("\n").append("Content-Type").append(": ").append("application/json");
-        dataToSign.append("\n").append("X-Qiniu-Date").append(": ").append(signDate);
-        dataToSign.append("\n\n");
+        dataToSign.append(method.toUpperCase()).append(" ").append(path)
+                .append("\nHost: ").append(HOST)
+                .append("\n").append("Content-Type").append(": ").append("application/json")
+                .append("\n").append("X-Qiniu-Date").append(": ").append(signDate)
+                .append("\n\n");
         if (ObjectUtil.isNotEmpty(body)) {
             dataToSign.append(body);
         }
-        String encodedSignature = SecureUtil.hmac(HmacAlgorithm.HmacSHA1, properties.getApiSecret()).digestBase64(dataToSign.toString(), true);
-
-        return "Qiniu " + properties.getApiKey() + ":" + encodedSignature;
+        String signature = SecureUtil.hmac(HmacAlgorithm.HmacSHA1, properties.getApiSecret())
+                .digestBase64(dataToSign.toString(), true);
+        return "Qiniu " + properties.getApiKey() + ":" + signature;
     }
 
     @Override
     public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
         JSONObject status = JSONUtil.parseObj(text);
         // 字段参考 https://developer.qiniu.com/sms/5910/message-push
-        return ListUtil.of(new SmsReceiveRespDTO()
-                .setSuccess("DELIVRD".equals(status.getJSONArray("items").getJSONObject(0).getStr("status"))) // 是否接收成功
-                .setErrorMsg(status.getJSONArray("items").getJSONObject(0).getStr("status"))
-                .setMobile(status.getJSONArray("items").getJSONObject(0).getStr("mobile")) // 手机号
-                .setReceiveTime(LocalDateTimeUtil.of(status.getJSONArray("items").getJSONObject(0).getLong("delivrd_at")*1000L))
-                .setSerialNo(status.getJSONArray("items").getJSONObject(0).getStr("message_id")) // 发送序列号
-                .setLogId(Long.valueOf(status.getJSONArray("items").getJSONObject(0).getStr("seq")))); // logId
+        return convertList(status.getJSONArray("items"), new Function<Object, SmsReceiveRespDTO>() {
+
+            @Override
+            public SmsReceiveRespDTO apply(Object item) {
+                JSONObject statusObj = (JSONObject) item;
+                return new SmsReceiveRespDTO()
+                        .setSuccess("DELIVRD".equals(statusObj.getStr("status"))) // 是否接收成功
+                        .setErrorMsg(statusObj.getStr("status")) // 状态报告编码
+                        .setMobile(statusObj.getStr("mobile")) // 手机号
+                        .setReceiveTime(LocalDateTimeUtil.of(statusObj.getLong("delivrd_at") * 1000L)) // 状态报告时间
+                        .setSerialNo(statusObj.getStr("message_id")) // 发送序列号
+                        .setLogId(statusObj.getLong("seq")); // 用户序列号
+            }
+
+        });
     }
 
     @Override
     public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
         // 1. 执行请求
         // 参考链接 https://developer.qiniu.com/sms/5969/query-a-single-template
-        JSONObject response = request("GET", null, TEMPLATE_PATH + "/" + apiTemplateId);
-        // 2.1 请求失败
-        if (ObjUtil.notEqual(response.getStr("audit_status"), "passed")) {
-            log.error("[getSmsTemplate][模版编号({}) 响应不正确({})]", apiTemplateId, response);
-            return null;
-        }
+        JSONObject response = request("GET", null, "/v1/template/" + apiTemplateId);
 
-        // 2.2 请求成功
+        // 2.2 解析请求
         return new SmsTemplateRespDTO()
                 .setId(response.getStr("id"))
                 .setContent(response.getStr("template"))
@@ -146,12 +144,12 @@ public class QiniuSmsClient extends AbstractSmsClient {
 
     @VisibleForTesting
     Integer convertSmsTemplateAuditStatus(String templateStatus) {
-        return switch (templateStatus) {
-            case "passed" -> SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
-            case "reviewing" -> SmsTemplateAuditStatusEnum.CHECKING.getStatus();
-            case "rejected" -> SmsTemplateAuditStatusEnum.FAIL.getStatus();
-            case null, default ->
-                    throw new IllegalArgumentException(String.format("未知审核状态(%str)", templateStatus));
-        };
+        switch (templateStatus) {
+            case "passed": return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
+            case "reviewing": return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
+            case "rejected": return SmsTemplateAuditStatusEnum.FAIL.getStatus();
+            default:
+                throw new IllegalArgumentException(String.format("未知审核状态(%str)", templateStatus));
+        }
     }
 }

+ 1 - 0
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java

@@ -80,6 +80,7 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
             case DEBUG_DING_TALK: return new DebugDingTalkSmsClient(properties);
             case TENCENT: return new TencentSmsClient(properties);
             case HUAWEI: return  new HuaweiSmsClient(properties);
+            case QINIU: return new QiniuSmsClient(properties);
         }
         // 创建失败,错误日志 + 抛出异常
         log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties);

+ 1 - 1
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClient.java

@@ -162,7 +162,7 @@ public class TencentSmsClient extends AbstractSmsClient {
      * @param body 请求参数
      * @return 请求结果
      */
-    private JSONObject request(String action, TreeMap<String, Object> body) throws Exception {
+    private JSONObject request(String action, TreeMap<String, Object> body) {
         // 1.1 请求 Header
         Map<String, String> headers = new HashMap<>();
         headers.put("Content-Type", "application/json; charset=utf-8");

+ 8 - 12
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java

@@ -29,6 +29,7 @@ import static org.mockito.Mockito.mockStatic;
  * @author scholar
  */
 public class QiniuSmsClientTest extends BaseMockitoUnitTest {
+
     private final SmsChannelProperties properties = new SmsChannelProperties()
             .setApiKey(randomString())// 随机一个 apiKey,避免构建报错
             .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错
@@ -37,12 +38,6 @@ public class QiniuSmsClientTest extends BaseMockitoUnitTest {
     @InjectMocks
     private QiniuSmsClient smsClient = new QiniuSmsClient(properties);
 
-    @Test
-    public void testDoInit() {
-        // 调用
-        smsClient.doInit();
-    }
-
     @Test
     public void testDoSendSms_success() throws Throwable {
         try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) {
@@ -113,12 +108,13 @@ public class QiniuSmsClientTest extends BaseMockitoUnitTest {
         List<SmsReceiveRespDTO> statuses = smsClient.parseSmsReceiveStatus(text);
         // 断言
         assertEquals(1, statuses.size());
-        assertTrue(statuses.getFirst().getSuccess());
-        assertEquals("DELIVRD", statuses.getFirst().getErrorMsg());
-        assertEquals(LocalDateTime.of(2024, 8, 25, 21, 14, 26), statuses.getFirst().getReceiveTime());
-        assertEquals("18881234567", statuses.getFirst().getMobile());
-        assertEquals("10135515063508004167", statuses.getFirst().getSerialNo());
-        assertEquals(123, statuses.getFirst().getLogId());
+        SmsReceiveRespDTO status = statuses.get(0);
+        assertTrue(status.getSuccess());
+        assertEquals("DELIVRD", status.getErrorMsg());
+        assertEquals(LocalDateTime.of(2024, 8, 25, 21, 14, 26), status.getReceiveTime());
+        assertEquals("18881234567", status.getMobile());
+        assertEquals("10135515063508004167", status.getSerialNo());
+        assertEquals(123, status.getLogId());
     }
 
     @Test

+ 5 - 4
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java

@@ -1,5 +1,6 @@
 package cn.iocoder.yudao.module.system.framework.sms.core.client.impl;
 
+import cn.hutool.core.collection.ListUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
 import cn.iocoder.yudao.module.system.framework.sms.core.client.SmsClient;
 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
@@ -47,7 +48,7 @@ public class SmsClientTests {
         String mobile = "15601691323";
         String apiTemplateId = "SMS_207945135";
         // 调用
-        SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, List.of(new KeyValue<>("code", "1024")));
+        SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, ListUtil.of(new KeyValue<>("code", "1024")));
         // 打印结果
         System.out.println(sendRespDTO);
     }
@@ -68,7 +69,7 @@ public class SmsClientTests {
         String mobile = "15601691323";
         String apiTemplateId = "358212";
         // 调用
-        SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, List.of(new KeyValue<>("code", "1024")));
+        SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, ListUtil.of(new KeyValue<>("code", "1024")));
         // 打印结果
         System.out.println(sendRespDTO);
     }
@@ -105,7 +106,7 @@ public class SmsClientTests {
         Long sendLogId = System.currentTimeMillis();
         String mobile = "17321315478";
         String apiTemplateId = "3644cdab863546a3b718d488659a99ef";
-        List<KeyValue<String, Object>> templateParams = List.of(new KeyValue<>("code", "1024"));
+        List<KeyValue<String, Object>> templateParams = ListUtil.of(new KeyValue<>("code", "1024"));
         // 调用
         SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
         // 打印结果
@@ -125,7 +126,7 @@ public class SmsClientTests {
         Long sendLogId = System.currentTimeMillis();
         String mobile = "17321315478";
         String apiTemplateId = "3644cdab863546a3b718d488659a99ef";
-        List<KeyValue<String, Object>> templateParams = List.of(new KeyValue<>("code", "1122"));
+        List<KeyValue<String, Object>> templateParams = ListUtil.of(new KeyValue<>("code", "1122"));
         // 调用
         SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
         // 打印结果