diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java index 4fbb8649d..a041970be 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java +++ b/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> 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 body, String path) { String signDate = DateUtil.date().setTimeZone(TimeZone.getTimeZone("UTC")).toString("yyyyMMdd'T'HHmmss'Z'"); - //请求头 + // 1. 请求头 Map 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 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() { + + @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)); + } } } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java index dde1475d4..da783189b 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java +++ b/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); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClient.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClient.java index ae3138362..19cde8c26 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/TencentSmsClient.java +++ b/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 body) throws Exception { + private JSONObject request(String action, TreeMap body) { // 1.1 请求 Header Map headers = new HashMap<>(); headers.put("Content-Type", "application/json; charset=utf-8"); diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java index c3e896695..93b99bcc5 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClientTest.java +++ b/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 httpUtilsMockedStatic = mockStatic(HttpUtils.class)) { @@ -113,12 +108,13 @@ public class QiniuSmsClientTest extends BaseMockitoUnitTest { List 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 diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java index 09608fea0..faba754a2 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/SmsClientTests.java +++ b/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> templateParams = List.of(new KeyValue<>("code", "1024")); + List> 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> templateParams = List.of(new KeyValue<>("code", "1122")); + List> templateParams = ListUtil.of(new KeyValue<>("code", "1122")); // 调用 SmsSendRespDTO smsSendRespDTO = client.sendSms(sendLogId, mobile, apiTemplateId, templateParams); // 打印结果