Browse Source

七牛云短信实现,评审意见修改

scholar 8 months ago
parent
commit
1c1abae5bb

+ 0 - 2
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java

@@ -135,7 +135,6 @@ public class HttpUtils {
      * @return 请求结果
      */
     public static String post(String url, Map<String, String> headers, String requestBody) {
-
         try (HttpResponse response = HttpRequest.post(url)
                 .addHeaders(headers)
                 .body(requestBody)
@@ -154,7 +153,6 @@ public class HttpUtils {
      * @return 请求结果
      */
     public static String get(String url, Map<String, String> headers) {
-
         try (HttpResponse response = HttpRequest.get(url)
                 .addHeaders(headers)
                 .execute()) {

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

@@ -1,10 +1,13 @@
 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.digest.HMac;
+import cn.hutool.crypto.SecureUtil;
 import cn.hutool.crypto.digest.HmacAlgorithm;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
@@ -18,11 +21,7 @@ import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProp
 import com.google.common.annotations.VisibleForTesting;
 import lombok.extern.slf4j.Slf4j;
 
-import java.nio.charset.StandardCharsets;
-import java.text.SimpleDateFormat;
-
 import java.util.*;
-import java.util.stream.Collectors;
 
 /**
  * 七牛云短信客户端的实现类
@@ -45,69 +44,60 @@ public class QiniuSmsClient extends AbstractSmsClient {
         Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
     }
 
-    @Override
     protected void doInit() {
     }
-    @Override
+
     public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
                                   List<KeyValue<String, Object>> templateParams) throws Throwable {
-
         // 1. 执行请求
         // 参考链接 https://developer.qiniu.com/sms/5824/through-the-api-send-text-messages
         LinkedHashMap<String, Object> body = new LinkedHashMap<>();
-        Map<String, Object> paramsMap = templateParams.stream()
-                .collect(Collectors.toMap(KeyValue::getKey, KeyValue::getValue));
-
         body.put("template_id", apiTemplateId);
         body.put("mobile", mobile);
-        body.put("parameters", paramsMap);
+        body.put("parameters", CollStreamUtil.toMap(templateParams, KeyValue::getKey, KeyValue::getValue));
         body.put("seq", Long.toString(sendLogId));
 
-        JSONObject response = request("POST", body, null);
+        JSONObject response = request("POST", body, PATH);
         // 2. 解析请求
+        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"));
     }
 
-
     /**
      * 请求七牛云短信
      *
      * @see <a href="https://developer.qiniu.com/sms/5842/sms-api-authentication"</>
      * @param httpMethod http请求方法
-     * @param queryParams 请求参数
+     * @param body http请求消息体
+     * @param path URL path
      * @return 请求结果
      */
-    private JSONObject request(String httpMethod, LinkedHashMap<String, Object> body, Map<String, Object> queryParams) {
-
-        String signature = "";
-        String templateIdPath = "";
-
-        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
-        dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
-        String signDate = dateFormat.format(new Date());
-
+    private JSONObject request(String httpMethod, LinkedHashMap<String, Object> body, String path) {
+        String signDate = DateUtil.date().setTimeZone(TimeZone.getTimeZone("UTC")).toString("yyyyMMdd'T'HHmmss'Z'");
         //请求头
         Map<String, String> header = new HashMap<>(4);
         header.put("HOST", HOST);
-        header.put("Authorization", signature);
+        header.put("Authorization", getSignature(httpMethod, HOST, 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")){
-            header.put("Authorization", getSignature(httpMethod, HOST, PATH, JSONUtil.toJsonStr(body), signDate));
-            responseBody = HttpUtils.post("https://" + HOST + PATH, header, JSONUtil.toJsonStr(body));
-        }else { // GET
-            templateIdPath = TEMPLATE_PATH + "/" + queryParams.get("template_id");
-            header.put("Authorization", getSignature(httpMethod, HOST, templateIdPath, null, signDate));
-            responseBody = HttpUtils.get("https://" + HOST + templateIdPath, header);
+        if (Objects.equals(httpMethod, "POST")){// POST 发送短消息用POST请求
+            responseBody = HttpUtils.post("https://" + HOST + path, header, JSONUtil.toJsonStr(body));
+        }else { // GET 查询template状态用GET请求
+            responseBody = HttpUtils.get("https://" + HOST + path, header);
         }
         return JSONUtil.parseObj(responseBody);
     }
 
     public String getSignature(String method, String host, String path, String body, String signDate) {
-
         StringBuilder dataToSign = new StringBuilder();
         dataToSign.append(method.toUpperCase()).append(" ").append(path);
         dataToSign.append("\nHost: ").append(host);
@@ -117,18 +107,15 @@ public class QiniuSmsClient extends AbstractSmsClient {
         if (ObjectUtil.isNotEmpty(body)) {
             dataToSign.append(body);
         }
-        HMac hMac = new HMac(HmacAlgorithm.HmacSHA1, properties.getApiSecret().getBytes(StandardCharsets.UTF_8));
-        byte[] signData = hMac.digest(dataToSign.toString().getBytes(StandardCharsets.UTF_8));
-        String encodedSignature = Base64.getEncoder().encodeToString(signData);
+        String encodedSignature = SecureUtil.hmac(HmacAlgorithm.HmacSHA1, properties.getApiSecret()).digestBase64(dataToSign.toString(), true);
 
         return "Qiniu " + properties.getApiKey() + ":" + encodedSignature;
     }
 
     @Override
     public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
-
         JSONObject status = JSONUtil.parseObj(text);
-        //字段参考 https://developer.qiniu.com/sms/5910/message-push
+        // 字段参考 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"))
@@ -142,16 +129,13 @@ public class QiniuSmsClient extends AbstractSmsClient {
     public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
         // 1. 执行请求
         // 参考链接 https://developer.qiniu.com/sms/5969/query-a-single-template
-        HashMap<String, Object> queryParam = new HashMap<>();
-        queryParam.put("template_id", apiTemplateId);
-        JSONObject response = request("GET", null, queryParam);
-
+        JSONObject response = request("GET", null, TEMPLATE_PATH + "/" + apiTemplateId);
         // 2.1 请求失败
-        String status = response.getStr("audit_status");
-        if (!Objects.equals(status, "passed")) {
+        if (ObjUtil.notEqual(response.getStr("audit_status"), "passed")) {
             log.error("[getSmsTemplate][模版编号({}) 响应不正确({})]", apiTemplateId, response);
             return null;
         }
+
         // 2.2 请求成功
         return new SmsTemplateRespDTO()
                 .setId(response.getStr("id"))
@@ -162,11 +146,12 @@ public class QiniuSmsClient extends AbstractSmsClient {
 
     @VisibleForTesting
     Integer convertSmsTemplateAuditStatus(String templateStatus) {
-
-        if(Objects.equals(templateStatus, "passed")){
-            return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
-        }else {
-            throw new IllegalArgumentException(String.format("未知审核状态(%str)", 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));
+        };
     }
 }

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

@@ -29,7 +29,6 @@ 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,避免构建报错
@@ -46,7 +45,6 @@ public class QiniuSmsClientTest extends BaseMockitoUnitTest {
 
     @Test
     public void testDoSendSms_success() throws Throwable {
-
         try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) {
             // 准备参数
             Long sendLogId = randomLongId();
@@ -56,9 +54,7 @@ public class QiniuSmsClientTest extends BaseMockitoUnitTest {
                     new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
             // mock 方法
             httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
-                    .thenReturn(
-                            "{\"message_id\":\"17245678901\"}"
-                    );
+                    .thenReturn("{\"message_id\":\"17245678901\"}");
             // 调用
             SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
                     apiTemplateId, templateParams);
@@ -77,17 +73,17 @@ public class QiniuSmsClientTest extends BaseMockitoUnitTest {
             String apiTemplateId = randomString() + " " + randomString();
             List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
                     new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
-
             // mock 方法
             httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
-                    .thenReturn(
-                            "{\"error\":\"BadToken\",\"message\":\"Your authorization token is invalid\",\"request_id\":\"etziWcJFo1C8Ne8X\"}"
-                    );
+                    .thenReturn("{\"error\":\"BadToken\",\"message\":\"Your authorization token is invalid\",\"request_id\":\"etziWcJFo1C8Ne8X\"}");
             // 调用
             SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
                     apiTemplateId, templateParams);
             // 断言
             assertFalse(result.getSuccess());
+            assertEquals("BadToken", result.getApiCode());
+            assertEquals("Your authorization token is invalid", result.getApiMsg());
+            assertEquals("etziWcJFo1C8Ne8X", result.getApiRequestId());
         }
     }
 
@@ -125,4 +121,15 @@ public class QiniuSmsClientTest extends BaseMockitoUnitTest {
         assertEquals(123, statuses.getFirst().getLogId());
     }
 
+    @Test
+    public void testConvertSmsTemplateAuditStatus() {
+        assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(),
+                smsClient.convertSmsTemplateAuditStatus("passed"));
+        assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(),
+                smsClient.convertSmsTemplateAuditStatus("reviewing"));
+        assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(),
+                smsClient.convertSmsTemplateAuditStatus("rejected"));
+        assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus("unknown"),
+                "未知审核状态(3)");
+    }
 }

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

@@ -116,7 +116,6 @@ public class SmsClientTests {
     @Test
     @Disabled
     public void testQiniuSmsClient_sendSms() throws Throwable {
-
         SmsChannelProperties properties = new SmsChannelProperties()
                 .setApiKey("SMS_QINIU_ACCESS_KEY")
                 .setApiSecret("SMS_QINIU_SECRET_KEY");
@@ -135,7 +134,6 @@ public class SmsClientTests {
     @Test
     @Disabled
     public void testQiniuSmsClient_getSmsTemplate() throws Throwable {
-
         SmsChannelProperties properties = new SmsChannelProperties()
                 .setApiKey("SMS_QINIU_ACCESS_KEY")
                 .setApiSecret("SMS_QINIU_SECRET_KEY");