Commit 8cce737523806c3bf4a1aa65a716664c032460f9
Merge branch 'develop' of https://gitee.com/scholarli/ruoyi-vue-pro_1 into develop
# Conflicts: # yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java
Showing
3 changed files
with
185 additions
and
101 deletions
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsCallbackController.java
| ... | ... | @@ -4,16 +4,17 @@ import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 4 | import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils; |
| 5 | 5 | import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsChannelEnum; |
| 6 | 6 | import cn.iocoder.yudao.module.system.service.sms.SmsSendService; |
| 7 | +import com.xingyuv.captcha.util.StreamUtils; | |
| 7 | 8 | import io.swagger.v3.oas.annotations.Operation; |
| 8 | 9 | import io.swagger.v3.oas.annotations.tags.Tag; |
| 9 | -import org.springframework.web.bind.annotation.PostMapping; | |
| 10 | -import org.springframework.web.bind.annotation.RequestMapping; | |
| 11 | -import org.springframework.web.bind.annotation.RestController; | |
| 10 | +import org.springframework.web.bind.annotation.*; | |
| 12 | 11 | |
| 13 | 12 | import jakarta.annotation.Resource; |
| 14 | 13 | import jakarta.annotation.security.PermitAll; |
| 15 | 14 | import jakarta.servlet.http.HttpServletRequest; |
| 16 | 15 | |
| 16 | +import java.nio.charset.Charset; | |
| 17 | + | |
| 17 | 18 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 18 | 19 | |
| 19 | 20 | @Tag(name = "管理后台 - 短信回调") |
| ... | ... | @@ -26,7 +27,7 @@ public class SmsCallbackController { |
| 26 | 27 | |
| 27 | 28 | @PostMapping("/aliyun") |
| 28 | 29 | @PermitAll |
| 29 | - @Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/zh/sms/developer-reference/configure-delivery-receipts-1 文档") | |
| 30 | + @Operation(summary = "阿里云短信的回调", description = "参见 https://help.aliyun.com/document_detail/120998.html 文档") | |
| 30 | 31 | public CommonResult<Boolean> receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable { |
| 31 | 32 | String text = ServletUtils.getBody(request); |
| 32 | 33 | smsSendService.receiveSmsStatus(SmsChannelEnum.ALIYUN.getCode(), text); |
| ... | ... | @@ -35,7 +36,7 @@ public class SmsCallbackController { |
| 35 | 36 | |
| 36 | 37 | @PostMapping("/tencent") |
| 37 | 38 | @PermitAll |
| 38 | - @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/59178 文档") | |
| 39 | + @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/52077 文档") | |
| 39 | 40 | public CommonResult<Boolean> receiveTencentSmsStatus(HttpServletRequest request) throws Throwable { |
| 40 | 41 | String text = ServletUtils.getBody(request); |
| 41 | 42 | smsSendService.receiveSmsStatus(SmsChannelEnum.TENCENT.getCode(), text); |
| ... | ... | @@ -46,9 +47,8 @@ public class SmsCallbackController { |
| 46 | 47 | @PostMapping("/huawei") |
| 47 | 48 | @PermitAll |
| 48 | 49 | @Operation(summary = "华为云短信的回调", description = "参见 https://support.huaweicloud.com/api-msgsms/sms_05_0003.html 文档") |
| 49 | - public CommonResult<Boolean> receiveHuaweiSmsStatus(HttpServletRequest request) throws Throwable { | |
| 50 | - String text = ServletUtils.getBody(request); | |
| 51 | - smsSendService.receiveSmsStatus(SmsChannelEnum.HUAWEI.getCode(), text); | |
| 50 | + public CommonResult<Boolean> receiveHuaweiSmsStatus(@RequestBody String requestBody) throws Throwable { | |
| 51 | + smsSendService.receiveSmsStatus(SmsChannelEnum.HUAWEI.getCode(), requestBody); | |
| 52 | 52 | return success(true); |
| 53 | 53 | } |
| 54 | 54 | ... | ... |
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java
| ... | ... | @@ -4,12 +4,11 @@ import cn.hutool.core.lang.Assert; |
| 4 | 4 | import cn.hutool.core.util.StrUtil; |
| 5 | 5 | |
| 6 | 6 | import cn.hutool.crypto.SecureUtil; |
| 7 | -import cn.hutool.http.HttpRequest; | |
| 8 | -import cn.hutool.http.HttpResponse; | |
| 9 | 7 | import cn.hutool.json.JSONObject; |
| 10 | 8 | import cn.hutool.json.JSONUtil; |
| 11 | 9 | import cn.iocoder.yudao.framework.common.core.KeyValue; |
| 12 | 10 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 11 | +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; | |
| 13 | 12 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 14 | 13 | import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; |
| 15 | 14 | import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; |
| ... | ... | @@ -17,23 +16,20 @@ import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateR |
| 17 | 16 | import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; |
| 18 | 17 | import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; |
| 19 | 18 | |
| 20 | -import com.fasterxml.jackson.annotation.JsonFormat; | |
| 21 | -import com.fasterxml.jackson.annotation.JsonProperty; | |
| 22 | -import lombok.Data; | |
| 23 | 19 | import lombok.extern.slf4j.Slf4j; |
| 24 | 20 | |
| 25 | 21 | import java.io.UnsupportedEncodingException; |
| 22 | +import java.net.URLDecoder; | |
| 26 | 23 | import java.net.URLEncoder; |
| 27 | 24 | import java.text.SimpleDateFormat; |
| 25 | +import java.time.Instant; | |
| 26 | +import java.time.ZoneId; | |
| 28 | 27 | import java.util.*; |
| 29 | 28 | |
| 30 | 29 | |
| 31 | 30 | import java.time.LocalDateTime; |
| 32 | 31 | |
| 33 | 32 | import static cn.hutool.crypto.digest.DigestUtil.sha256Hex; |
| 34 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 35 | -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; | |
| 36 | -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; | |
| 37 | 33 | |
| 38 | 34 | // todo @scholar:参考阿里云在优化下 |
| 39 | 35 | /** |
| ... | ... | @@ -45,9 +41,6 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DE |
| 45 | 41 | @Slf4j |
| 46 | 42 | public class HuaweiSmsClient extends AbstractSmsClient { |
| 47 | 43 | |
| 48 | - /** | |
| 49 | - * 调用成功 code | |
| 50 | - */ | |
| 51 | 44 | public static final String URL = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1";//APP接入地址+接口访问URI |
| 52 | 45 | public static final String HOST = "smsapi.cn-north-4.myhuaweicloud.com:443"; |
| 53 | 46 | public static final String SIGNEDHEADERS = "content-type;host;x-sdk-date"; |
| ... | ... | @@ -76,13 +69,14 @@ public class HuaweiSmsClient extends AbstractSmsClient { |
| 76 | 69 | |
| 77 | 70 | List<String> templateParas = CollectionUtils.convertList(templateParams, kv -> String.valueOf(kv.getValue())); |
| 78 | 71 | |
| 79 | - JSONObject JsonResponse = sendSmsRequest(sender,mobile,templateId,templateParas,statusCallBack); | |
| 80 | - SmsResponse smsResponse = getSmsSendResponse(JsonResponse); | |
| 72 | + JSONObject JsonResponse = request(sendLogId,sender,mobile,templateId,templateParas,statusCallBack); | |
| 81 | 73 | |
| 82 | - return new SmsSendRespDTO().setSuccess(smsResponse.success).setApiMsg(smsResponse.data.toString()); | |
| 74 | + return new SmsSendRespDTO().setSuccess("000000".equals(JsonResponse.getStr("code"))) | |
| 75 | + .setSerialNo(JsonResponse.getJSONArray("result").getJSONObject(0).getStr("smsMsgId")) | |
| 76 | + .setApiCode(JsonResponse.getJSONArray("result").getJSONObject(0).getStr("status")); | |
| 83 | 77 | } |
| 84 | 78 | |
| 85 | - JSONObject sendSmsRequest(String sender,String mobile,String templateId,List<String> templateParas,String statusCallBack) throws UnsupportedEncodingException { | |
| 79 | + JSONObject request(Long sendLogId,String sender,String mobile,String templateId,List<String> templateParas,String statusCallBack) throws UnsupportedEncodingException { | |
| 86 | 80 | |
| 87 | 81 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.ENGLISH); |
| 88 | 82 | sdf.setTimeZone(TimeZone.getTimeZone("UTC")); |
| ... | ... | @@ -95,8 +89,7 @@ public class HuaweiSmsClient extends AbstractSmsClient { |
| 95 | 89 | String canonicalHeaders = "content-type:application/x-www-form-urlencoded\n" |
| 96 | 90 | + "host:"+ HOST +"\n" |
| 97 | 91 | + "x-sdk-date:" + sdkDate + "\n"; |
| 98 | - //请求Body,不携带签名名称时,signature请填null | |
| 99 | - String body = buildRequestBody(sender, mobile, templateId, templateParas, statusCallBack, null); | |
| 92 | + String body = buildRequestBody(sender, mobile, templateId, templateParas, statusCallBack, sendLogId); | |
| 100 | 93 | if (null == body || body.isEmpty()) { |
| 101 | 94 | return null; |
| 102 | 95 | } |
| ... | ... | @@ -116,26 +109,29 @@ public class HuaweiSmsClient extends AbstractSmsClient { |
| 116 | 109 | + "SignedHeaders=" + SIGNEDHEADERS + ", " + "Signature=" + signature; |
| 117 | 110 | |
| 118 | 111 | // ************* 步骤 5:构造HttpRequest 并执行request请求,获得response ************* |
| 119 | - HttpResponse response = HttpRequest.post(URL) | |
| 120 | - .header("Content-Type", "application/x-www-form-urlencoded") | |
| 121 | - .header("X-Sdk-Date", sdkDate) | |
| 122 | - .header("host",HOST) | |
| 123 | - .header("Authorization", authorization) | |
| 124 | - .body(body) | |
| 125 | - .execute(); | |
| 126 | - | |
| 127 | - return JSONUtil.parseObj(response.body()); | |
| 128 | - } | |
| 129 | - | |
| 130 | - private SmsResponse getSmsSendResponse(JSONObject resJson) { | |
| 131 | - SmsResponse smsResponse = new SmsResponse(); | |
| 132 | - smsResponse.setSuccess("000000".equals(resJson.getStr("code"))); | |
| 133 | - smsResponse.setData(resJson); | |
| 134 | - return smsResponse; | |
| 112 | + TreeMap<String, String> headers = new TreeMap<>(); | |
| 113 | + headers.put("Content-Type", "application/x-www-form-urlencoded"); | |
| 114 | + headers.put("X-Sdk-Date", sdkDate); | |
| 115 | + headers.put("host", HOST); | |
| 116 | + headers.put("Authorization", authorization); | |
| 117 | + | |
| 118 | + String responseBody = HttpUtils.post(URL, headers, body); | |
| 119 | + return JSONUtil.parseObj(responseBody); | |
| 120 | +// | |
| 121 | +// | |
| 122 | +// HttpResponse response = HttpRequest.post(URL) | |
| 123 | +// .header("Content-Type", "application/x-www-form-urlencoded") | |
| 124 | +// .header("X-Sdk-Date", sdkDate) | |
| 125 | +// .header("host",HOST) | |
| 126 | +// .header("Authorization", authorization) | |
| 127 | +// .body(body) | |
| 128 | +// .execute(); | |
| 129 | +// | |
| 130 | +// return JSONUtil.parseObj(response.body()); | |
| 135 | 131 | } |
| 136 | 132 | |
| 137 | 133 | static String buildRequestBody(String sender, String receiver, String templateId, List<String> templateParas, |
| 138 | - String statusCallBack, String signature) throws UnsupportedEncodingException { | |
| 134 | + String statusCallBack, Long sendLogId) throws UnsupportedEncodingException { | |
| 139 | 135 | if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty() |
| 140 | 136 | || templateId.isEmpty()) { |
| 141 | 137 | System.out.println("buildRequestBody(): sender, receiver or templateId is null."); |
| ... | ... | @@ -148,7 +144,9 @@ public class HuaweiSmsClient extends AbstractSmsClient { |
| 148 | 144 | appendToBody(body, "&templateId=", templateId); |
| 149 | 145 | appendToBody(body, "&templateParas=", JsonUtils.toJsonString(templateParas)); |
| 150 | 146 | appendToBody(body, "&statusCallback=", statusCallBack); |
| 151 | - appendToBody(body, "&signature=", signature); | |
| 147 | + appendToBody(body, "&signature=", null); | |
| 148 | + appendToBody(body, "&extend=", String.valueOf(sendLogId)); | |
| 149 | + | |
| 152 | 150 | return body.toString(); |
| 153 | 151 | } |
| 154 | 152 | |
| ... | ... | @@ -158,12 +156,35 @@ public class HuaweiSmsClient extends AbstractSmsClient { |
| 158 | 156 | } |
| 159 | 157 | } |
| 160 | 158 | @Override |
| 161 | - public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) { | |
| 162 | - List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class); | |
| 163 | - return convertList(statuses, status -> new SmsReceiveRespDTO().setSuccess(Objects.equals(status.getStatus(),"DELIVRD")) | |
| 164 | - .setErrorCode(status.getStatus()).setErrorMsg(status.getStatus()) | |
| 165 | - .setMobile(status.getPhoneNumber()).setReceiveTime(status.getUpdateTime()) | |
| 166 | - .setSerialNo(status.getSmsMsgId())); | |
| 159 | + public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String requestBody) { | |
| 160 | + | |
| 161 | + System.out.println("text in parseSmsReceiveStatus===== " + requestBody); | |
| 162 | + | |
| 163 | + Map<String, String> params = new HashMap<>(); | |
| 164 | + try { | |
| 165 | + String[] pairs = requestBody.split("&"); | |
| 166 | + for (String pair : pairs) { | |
| 167 | + int idx = pair.indexOf("="); | |
| 168 | + String key = URLDecoder.decode(pair.substring(0, idx), "UTF-8"); | |
| 169 | + String value = URLDecoder.decode(pair.substring(idx + 1), "UTF-8"); | |
| 170 | + params.put(key, value); | |
| 171 | + } | |
| 172 | + } catch (Exception e) { | |
| 173 | + e.printStackTrace(); | |
| 174 | + } | |
| 175 | + | |
| 176 | + List<SmsReceiveRespDTO> respDTOS = new ArrayList<>(); | |
| 177 | + respDTOS.add(new SmsReceiveRespDTO() | |
| 178 | + .setSuccess("DELIVRD".equals(params.get("status"))) // 是否接收成功 | |
| 179 | + .setErrorCode(params.get("status")) // 状态报告编码 | |
| 180 | + .setErrorMsg(params.get("statusDesc")) | |
| 181 | + .setMobile(params.get("to")) // 手机号 | |
| 182 | + .setReceiveTime(LocalDateTime.ofInstant(Instant.parse(params.get("updateTime")), ZoneId.of("UTC"))) // 状态报告时间 | |
| 183 | + .setSerialNo(params.get("smsMsgId")) // 发送序列号 | |
| 184 | + .setLogId(Long.valueOf(params.get("extend")))//logId | |
| 185 | + ); | |
| 186 | + | |
| 187 | + return respDTOS; | |
| 167 | 188 | } |
| 168 | 189 | |
| 169 | 190 | @Override |
| ... | ... | @@ -171,56 +192,5 @@ public class HuaweiSmsClient extends AbstractSmsClient { |
| 171 | 192 | //华为短信模板查询和发送短信,是不同的两套key和secret,与阿里、腾讯的区别较大,这里模板查询校验暂不实现。 |
| 172 | 193 | return new SmsTemplateRespDTO().setId(null).setContent(null) |
| 173 | 194 | .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(null); |
| 174 | - | |
| 175 | 195 | } |
| 176 | - | |
| 177 | - @Data | |
| 178 | - public static class SmsResponse { | |
| 179 | - | |
| 180 | - /** | |
| 181 | - * 是否成功 | |
| 182 | - */ | |
| 183 | - private boolean success; | |
| 184 | - | |
| 185 | - /** | |
| 186 | - * 厂商原返回体 | |
| 187 | - */ | |
| 188 | - private Object data; | |
| 189 | - | |
| 190 | - } | |
| 191 | - | |
| 192 | - | |
| 193 | - /** | |
| 194 | - * 短信接收状态 | |
| 195 | - * | |
| 196 | - * 参见 <a href="https://support.huaweicloud.com/api-msgsms/sms_05_0003.html">文档</a> | |
| 197 | - * | |
| 198 | - * @author scholar | |
| 199 | - */ | |
| 200 | - @Data | |
| 201 | - public static class SmsReceiveStatus { | |
| 202 | - | |
| 203 | - /** | |
| 204 | - * 本条状态报告对应的短信的接收方号码,仅当状态报告中携带了extend参数时才会同时携带该参数 | |
| 205 | - */ | |
| 206 | - @JsonProperty("to") | |
| 207 | - private String phoneNumber; | |
| 208 | - | |
| 209 | - /** | |
| 210 | - * 短信资源的更新时间,通常为短信平台接收短信状态报告的时间 | |
| 211 | - */ | |
| 212 | - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) | |
| 213 | - private LocalDateTime updateTime; | |
| 214 | - | |
| 215 | - /** | |
| 216 | - * 短信状态报告枚举值 | |
| 217 | - */ | |
| 218 | - private String status; | |
| 219 | - | |
| 220 | - /** | |
| 221 | - * 发送短信成功时返回的短信唯一标识。 | |
| 222 | - */ | |
| 223 | - private String smsMsgId; | |
| 224 | - } | |
| 225 | - | |
| 226 | -} | |
| 196 | +} | |
| 227 | 197 | \ No newline at end of file | ... | ... |
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClientTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.common.core.KeyValue; | |
| 4 | +import cn.iocoder.yudao.framework.common.util.http.HttpUtils; | |
| 5 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | |
| 6 | +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; | |
| 7 | +import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; | |
| 8 | +import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; | |
| 9 | +import com.google.common.collect.Lists; | |
| 10 | + | |
| 11 | +import org.junit.jupiter.api.Test; | |
| 12 | +import org.mockito.InjectMocks; | |
| 13 | +import org.mockito.MockedStatic; | |
| 14 | + | |
| 15 | +import java.time.LocalDateTime; | |
| 16 | +import java.util.List; | |
| 17 | + | |
| 18 | +import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; | |
| 19 | +import static org.junit.jupiter.api.Assertions.*; | |
| 20 | +import static org.mockito.ArgumentMatchers.*; | |
| 21 | +import static org.mockito.ArgumentMatchers.anyString; | |
| 22 | +import static org.mockito.Mockito.mockStatic; | |
| 23 | + | |
| 24 | +/** | |
| 25 | + * {@link HuaweiSmsClient} 的单元测试 | |
| 26 | + * | |
| 27 | + * @author scholar | |
| 28 | + */ | |
| 29 | +public class HuaweiSmsClientTest extends BaseMockitoUnitTest { | |
| 30 | + | |
| 31 | + private final SmsChannelProperties properties = new SmsChannelProperties() | |
| 32 | + .setApiKey(randomString())// 随机一个 apiKey,避免构建报错 | |
| 33 | + .setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错 | |
| 34 | + .setSignature("芋道源码"); | |
| 35 | + | |
| 36 | + @InjectMocks | |
| 37 | + private HuaweiSmsClient smsClient = new HuaweiSmsClient(properties); | |
| 38 | + | |
| 39 | + @Test | |
| 40 | + public void testDoInit() { | |
| 41 | + // 调用 | |
| 42 | + smsClient.doInit(); | |
| 43 | + } | |
| 44 | + | |
| 45 | + @Test | |
| 46 | + public void testDoSendSms_success() throws Throwable { | |
| 47 | + | |
| 48 | + try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { | |
| 49 | + // 准备参数 | |
| 50 | + Long sendLogId = randomLongId(); | |
| 51 | + String mobile = randomString(); | |
| 52 | + String apiTemplateId = randomString() + " " + randomString(); | |
| 53 | + List<KeyValue<String, Object>> templateParams = Lists.newArrayList( | |
| 54 | + new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); | |
| 55 | + | |
| 56 | + // mock 方法 | |
| 57 | + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) | |
| 58 | + .thenReturn( | |
| 59 | + "{\"result\":[{\"originTo\":\"+86155****5678\",\"createTime\":\"2018-05-25T16:34:34Z\",\"from\":\"1069********0012\",\"smsMsgId\":\"d6e3cdd0-522b-4692-8304-a07553cdf591_8539659\",\"status\":\"000000\",\"countryId\":\"CN\",\"total\":2}],\"code\":\"000000\",\"description\":\"Success\"}\n" | |
| 60 | + ); | |
| 61 | + | |
| 62 | + // 调用 | |
| 63 | + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, | |
| 64 | + apiTemplateId, templateParams); | |
| 65 | + // 断言 | |
| 66 | + assertTrue(result.getSuccess()); | |
| 67 | + assertEquals("d6e3cdd0-522b-4692-8304-a07553cdf591_8539659", result.getSerialNo()); | |
| 68 | + assertEquals("000000", result.getApiCode()); | |
| 69 | + | |
| 70 | + } | |
| 71 | + } | |
| 72 | + | |
| 73 | + @Test | |
| 74 | + public void testDoSendSms_fail() throws Throwable { | |
| 75 | + try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { | |
| 76 | + // 准备参数 | |
| 77 | + Long sendLogId = randomLongId(); | |
| 78 | + String mobile = randomString(); | |
| 79 | + String apiTemplateId = randomString() + " " + randomString(); | |
| 80 | + List<KeyValue<String, Object>> templateParams = Lists.newArrayList( | |
| 81 | + new KeyValue<>("1", 1234), new KeyValue<>("2", "login")); | |
| 82 | + | |
| 83 | + // mock 方法 | |
| 84 | + httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString())) | |
| 85 | + .thenReturn( | |
| 86 | + "{\"result\":[{\"originTo\":\"+86155****5678\",\"createTime\":\"2018-05-25T16:34:34Z\",\"from\":\"1069********0012\",\"smsMsgId\":\"d6e3cdd0-522b-4692-8304-a07553cdf591_8539659\",\"status\":\"E200015\",\"countryId\":\"CN\",\"total\":2}],\"code\":\"E000000\",\"description\":\"Success\"}\n" | |
| 87 | + ); | |
| 88 | + | |
| 89 | + // 调用 | |
| 90 | + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, | |
| 91 | + apiTemplateId, templateParams); | |
| 92 | + // 断言 | |
| 93 | + assertFalse(result.getSuccess()); | |
| 94 | + assertEquals("d6e3cdd0-522b-4692-8304-a07553cdf591_8539659", result.getSerialNo()); | |
| 95 | + assertEquals("E200015", result.getApiCode()); | |
| 96 | + } | |
| 97 | + } | |
| 98 | + | |
| 99 | + @Test | |
| 100 | + public void testParseSmsReceiveStatus() { | |
| 101 | + // 准备参数 | |
| 102 | + String text = "sequence=1&total=1&statusDesc=%E7%94%A8%E6%88%B7%E5%B7%B2%E6%88%90%E5%8A%9F%E6%94%B6%E5%88%B0%E7%9F%AD%E4%BF%A1&updateTime=2024-08-15T03%3A00%3A34Z&source=2&smsMsgId=70207ed7-1d02-41b0-8537-bb25fd1c2364_143684459&status=DELIVRD&extend=176"; | |
| 103 | + | |
| 104 | + // 调用 | |
| 105 | + List<SmsReceiveRespDTO> statuses = smsClient.parseSmsReceiveStatus(text); | |
| 106 | + // 断言 | |
| 107 | + assertEquals(1, statuses.size()); | |
| 108 | + assertTrue(statuses.getFirst().getSuccess()); | |
| 109 | + assertEquals("DELIVRD", statuses.getFirst().getErrorCode()); | |
| 110 | + assertEquals(LocalDateTime.of(2024, 8, 15, 3, 0, 34), statuses.getFirst().getReceiveTime()); | |
| 111 | + assertEquals("70207ed7-1d02-41b0-8537-bb25fd1c2364_143684459", statuses.getFirst().getSerialNo()); | |
| 112 | + } | |
| 113 | + | |
| 114 | +} | ... | ... |