ソースを参照

【代码优化】SYSTEM:增加新阿里云 SmsClient 的单测

YunaiV 10 ヶ月 前
コミット
22686bafa2

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

@@ -5,6 +5,8 @@ import cn.hutool.core.map.TableMap;
 import cn.hutool.core.net.url.UrlBuilder;
 import cn.hutool.core.util.ReflectUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpResponse;
 import org.springframework.util.StringUtils;
 import org.springframework.web.util.UriComponents;
 import org.springframework.web.util.UriComponentsBuilder;
@@ -122,5 +124,23 @@ public class HttpUtils {
         return null;
     }
 
+    /**
+     * HTTP post 请求,基于 {@link cn.hutool.http.HttpUtil} 实现
+     *
+     * 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数
+     *
+     * @param url URL
+     * @param headers 请求头
+     * @param requestBody 请求体
+     * @return 请求结果
+     */
+    public static String post(String url, Map<String, String> headers, String requestBody) {
+        try (HttpResponse response = HttpRequest.post(url)
+                .addHeaders(headers)
+                .body(requestBody)
+                .execute()) {
+            return response.body();
+        }
+    }
 
 }

+ 7 - 8
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClient.java

@@ -6,13 +6,12 @@ import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.crypto.SecureUtil;
 import cn.hutool.crypto.digest.DigestUtil;
-import cn.hutool.http.HttpRequest;
-import cn.hutool.http.HttpResponse;
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import cn.iocoder.yudao.framework.common.core.KeyValue;
 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
+import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
@@ -58,10 +57,11 @@ public class AliyunSmsClient extends AbstractSmsClient {
     @Override
     public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
                                   List<KeyValue<String, Object>> templateParams) throws Throwable {
+        Assert.notBlank(properties.getSignature(), "短信签名不能为空");
         // 1. 执行请求
         // 参考链接 https://api.aliyun.com/document/Dysmsapi/2017-05-25/SendSms
         TreeMap<String, Object> queryParam = new TreeMap<>();
-        queryParam.put("PhoneNumbers",mobile);
+        queryParam.put("PhoneNumbers", mobile);
         queryParam.put("SignName", properties.getSignature());
         queryParam.put("TemplateCode", apiTemplateId);
         queryParam.put("TemplateParam", JsonUtils.toJsonString(MapUtils.convertMap(templateParams)));
@@ -111,7 +111,8 @@ public class AliyunSmsClient extends AbstractSmsClient {
             return null;
         }
         // 2.2 请求成功
-        return new SmsTemplateRespDTO().setId(apiTemplateId)
+        return new SmsTemplateRespDTO()
+                .setId(response.getStr("TemplateCode"))
                 .setContent(response.getStr("TemplateContent"))
                 .setAuditStatus(convertSmsTemplateAuditStatus(response.getInt("TemplateStatus")))
                 .setAuditReason(response.getStr("Reason"));
@@ -176,10 +177,8 @@ public class AliyunSmsClient extends AbstractSmsClient {
                 + ", " + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature);
 
         // 5. 发起请求
-        try (HttpResponse response = HttpRequest.post(URL + "?" + queryString)
-                .addHeaders(headers).body(requestBody).execute()) {
-            return JSONUtil.parseObj(response.body());
-        }
+        String responseBody = HttpUtils.post(URL + "?" + queryString, headers, requestBody);
+        return JSONUtil.parseObj(responseBody);
     }
 
     /**

+ 77 - 82
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/AliyunSmsClientTest.java

@@ -1,21 +1,30 @@
 package cn.iocoder.yudao.module.system.framework.sms.core.client.impl;
 
+import cn.iocoder.yudao.framework.common.core.KeyValue;
+import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
+import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
+import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO;
 import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
 import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties;
+import com.google.common.collect.Lists;
 import org.junit.jupiter.api.Test;
 import org.mockito.InjectMocks;
+import org.mockito.MockedStatic;
 
 import java.time.LocalDateTime;
 import java.util.List;
 
+import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
 import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.anyMap;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mockStatic;
 
-// TODO 芋艿:需要优化
 /**
- * {@link cn.iocoder.yudao.module.system.framework.sms.core.client.impl.AliyunSmsClient_old} 的单元测试
+ * {@link cn.iocoder.yudao.module.system.framework.sms.core.client.impl.AliyunSmsClient} 的单元测试
  *
  * @author 芋道源码
  */
@@ -38,64 +47,54 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
         smsClient.doInit();
     }
 
-//    @Test
-//    public void tesSendSms_success() 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("OK"));
-//        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);
-//        // 断言
-//        assertTrue(result.getSuccess());
-//        assertEquals(response.getRequestId(), result.getApiRequestId());
-//        assertEquals(response.getCode(), result.getApiCode());
-//        assertEquals(response.getMessage(), result.getApiMsg());
-//        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.getCode(), result.getApiCode());
-//        assertEquals(response.getMessage(), result.getApiMsg());
-//        assertEquals(response.getBizId(), result.getSerialNo());
-//    }
+    @Test
+    public void tesSendSms_success() throws Throwable {
+        try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) {
+            // 准备参数
+            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 方法
+            httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
+                    .thenReturn("{\"Message\":\"OK\",\"RequestId\":\"30067CE9-3710-5984-8881-909B21D8DB28\",\"Code\":\"OK\",\"BizId\":\"800025323183427988\"}");
+
+            // 调用
+            SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
+                    apiTemplateId, templateParams);
+            // 断言
+            assertTrue(result.getSuccess());
+            assertEquals("30067CE9-3710-5984-8881-909B21D8DB28", result.getApiRequestId());
+            assertEquals("OK", result.getApiCode());
+            assertEquals("OK", result.getApiMsg());
+            assertEquals("800025323183427988", result.getSerialNo());
+        }
+    }
+
+    @Test
+    public void tesSendSms_fail() throws Throwable {
+        try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) {
+            // 准备参数
+            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 方法
+            httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
+                    .thenReturn("{\"Message\":\"手机号码格式错误\",\"RequestId\":\"B7700B8E-227E-5886-9564-26036172F01F\",\"Code\":\"isv.MOBILE_NUMBER_ILLEGAL\"}");
+
+            // 调用
+            SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
+            // 断言
+            assertFalse(result.getSuccess());
+            assertEquals("B7700B8E-227E-5886-9564-26036172F01F", result.getApiRequestId());
+            assertEquals("isv.MOBILE_NUMBER_ILLEGAL", result.getApiCode());
+            assertEquals("手机号码格式错误", result.getApiMsg());
+            assertNull(result.getSerialNo());
+        }
+    }
 
     @Test
     public void testParseSmsReceiveStatus() {
@@ -129,28 +128,24 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
         assertEquals(67890L, statuses.get(0).getLogId());
     }
 
-//    @Test
-//    public void testGetSmsTemplate() throws Throwable {
-//        // 准备参数
-//        String apiTemplateId = randomString();
-//        // mock 方法
-//        QuerySmsTemplateResponse response = randomPojo(QuerySmsTemplateResponse.class, o -> {
-//            o.setCode("OK");
-//            o.setTemplateStatus(1); // 设置模板通过
-//        });
-//        when(client.getAcsResponse(argThat((ArgumentMatcher<QuerySmsTemplateRequest>) acsRequest -> {
-//            assertEquals(apiTemplateId, acsRequest.getTemplateCode());
-//            return true;
-//        }))).thenReturn(response);
-//
-//        // 调用
-//        SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId);
-//        // 断言
-//        assertEquals(response.getTemplateCode(), result.getId());
-//        assertEquals(response.getTemplateContent(), result.getContent());
-//        assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus());
-//        assertEquals(response.getReason(), result.getAuditReason());
-//    }
+    @Test
+    public void testGetSmsTemplate() throws Throwable {
+        try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) {
+            // 准备参数
+            String apiTemplateId = randomString();
+            // mock 方法
+            httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
+                    .thenReturn("{\"TemplateCode\":\"SMS_207945135\",\"RequestId\":\"6F4CC077-29C8-5BA5-AB62-5FF95068A5AC\",\"Message\":\"OK\",\"TemplateContent\":\"您的验证码${code},该验证码5分钟内有效,请勿泄漏于他人!\",\"TemplateName\":\"公告通知\",\"TemplateType\":0,\"Code\":\"OK\",\"CreateDate\":\"2020-12-23 17:34:42\",\"Reason\":\"无审批备注\",\"TemplateStatus\":1}");
+
+            // 调用
+            SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId);
+            // 断言
+            assertEquals("SMS_207945135", result.getId());
+            assertEquals("您的验证码${code},该验证码5分钟内有效,请勿泄漏于他人!", result.getContent());
+            assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus());
+            assertEquals("无审批备注", result.getAuditReason());
+        }
+    }
 
     @Test
     public void testConvertSmsTemplateAuditStatus() {

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

@@ -57,11 +57,12 @@ public class SmsClientTests {
     public void testAliyunSmsClient_sendSms() throws Throwable {
         SmsChannelProperties properties = new SmsChannelProperties()
                 .setApiKey("LTAI5tAicJAxaSFiZuGGeXHR")
-                .setApiSecret("Fdr9vadxnDvS6GJU0W1tijQ0VmLhYz");
+                .setApiSecret("Fdr9vadxnDvS6GJU0W1tijQ0VmLhYz")
+                .setSignature("Ballcat");
         AliyunSmsClient client = new AliyunSmsClient(properties);
         // 准备参数
         Long sendLogId = System.currentTimeMillis();
-        String mobile = "17321315478";
+        String mobile = "173213154791";
         String apiTemplateId = "SMS_207945135";
         // 调用
         SmsSendRespDTO sendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, List.of(new KeyValue<>("code", "1024")));