mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-22 15:21:53 +08:00
七牛云短信实现,评审意见修改
This commit is contained in:
parent
4f7ac969fe
commit
1c1abae5bb
@ -135,7 +135,6 @@ public class HttpUtils {
|
|||||||
* @return 请求结果
|
* @return 请求结果
|
||||||
*/
|
*/
|
||||||
public static String post(String url, Map<String, String> headers, String requestBody) {
|
public static String post(String url, Map<String, String> headers, String requestBody) {
|
||||||
|
|
||||||
try (HttpResponse response = HttpRequest.post(url)
|
try (HttpResponse response = HttpRequest.post(url)
|
||||||
.addHeaders(headers)
|
.addHeaders(headers)
|
||||||
.body(requestBody)
|
.body(requestBody)
|
||||||
@ -154,7 +153,6 @@ public class HttpUtils {
|
|||||||
* @return 请求结果
|
* @return 请求结果
|
||||||
*/
|
*/
|
||||||
public static String get(String url, Map<String, String> headers) {
|
public static String get(String url, Map<String, String> headers) {
|
||||||
|
|
||||||
try (HttpResponse response = HttpRequest.get(url)
|
try (HttpResponse response = HttpRequest.get(url)
|
||||||
.addHeaders(headers)
|
.addHeaders(headers)
|
||||||
.execute()) {
|
.execute()) {
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
package cn.iocoder.yudao.module.system.framework.sms.core.client.impl;
|
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.collection.ListUtil;
|
||||||
|
import cn.hutool.core.date.DateUtil;
|
||||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||||
import cn.hutool.core.lang.Assert;
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import cn.hutool.core.util.ObjUtil;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
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.crypto.digest.HmacAlgorithm;
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
import cn.hutool.json.JSONUtil;
|
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 com.google.common.annotations.VisibleForTesting;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 七牛云短信客户端的实现类
|
* 七牛云短信客户端的实现类
|
||||||
@ -45,69 +44,60 @@ public class QiniuSmsClient extends AbstractSmsClient {
|
|||||||
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
|
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doInit() {
|
protected void doInit() {
|
||||||
}
|
}
|
||||||
@Override
|
|
||||||
public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
|
public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
|
||||||
List<KeyValue<String, Object>> templateParams) throws Throwable {
|
List<KeyValue<String, Object>> templateParams) throws Throwable {
|
||||||
|
|
||||||
// 1. 执行请求
|
// 1. 执行请求
|
||||||
// 参考链接 https://developer.qiniu.com/sms/5824/through-the-api-send-text-messages
|
// 参考链接 https://developer.qiniu.com/sms/5824/through-the-api-send-text-messages
|
||||||
LinkedHashMap<String, Object> body = new LinkedHashMap<>();
|
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("template_id", apiTemplateId);
|
||||||
body.put("mobile", mobile);
|
body.put("mobile", mobile);
|
||||||
body.put("parameters", paramsMap);
|
body.put("parameters", CollStreamUtil.toMap(templateParams, KeyValue::getKey, KeyValue::getValue));
|
||||||
body.put("seq", Long.toString(sendLogId));
|
body.put("seq", Long.toString(sendLogId));
|
||||||
|
|
||||||
JSONObject response = request("POST", body, null);
|
JSONObject response = request("POST", body, PATH);
|
||||||
// 2. 解析请求
|
// 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"))
|
return new SmsSendRespDTO().setSuccess(response.containsKey("message_id"))
|
||||||
.setSerialNo(response.getStr("message_id"));
|
.setSerialNo(response.getStr("message_id"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 请求七牛云短信
|
* 请求七牛云短信
|
||||||
*
|
*
|
||||||
* @see <a href="https://developer.qiniu.com/sms/5842/sms-api-authentication"</>
|
* @see <a href="https://developer.qiniu.com/sms/5842/sms-api-authentication"</>
|
||||||
* @param httpMethod http请求方法
|
* @param httpMethod http请求方法
|
||||||
* @param queryParams 请求参数
|
* @param body http请求消息体
|
||||||
|
* @param path URL path
|
||||||
* @return 请求结果
|
* @return 请求结果
|
||||||
*/
|
*/
|
||||||
private JSONObject request(String httpMethod, LinkedHashMap<String, Object> body, Map<String, Object> queryParams) {
|
private JSONObject request(String httpMethod, LinkedHashMap<String, Object> body, String path) {
|
||||||
|
String signDate = DateUtil.date().setTimeZone(TimeZone.getTimeZone("UTC")).toString("yyyyMMdd'T'HHmmss'Z'");
|
||||||
String signature = "";
|
|
||||||
String templateIdPath = "";
|
|
||||||
|
|
||||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
|
|
||||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
|
||||||
String signDate = dateFormat.format(new Date());
|
|
||||||
|
|
||||||
//请求头
|
//请求头
|
||||||
Map<String, String> header = new HashMap<>(4);
|
Map<String, String> header = new HashMap<>(4);
|
||||||
header.put("HOST", HOST);
|
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("Content-Type", "application/json");
|
||||||
header.put("X-Qiniu-Date", signDate);
|
header.put("X-Qiniu-Date", signDate);
|
||||||
|
|
||||||
String responseBody ="";
|
String responseBody ="";
|
||||||
if(Objects.equals(httpMethod, "POST")){
|
if (Objects.equals(httpMethod, "POST")){// POST 发送短消息用POST请求
|
||||||
header.put("Authorization", getSignature(httpMethod, HOST, PATH, JSONUtil.toJsonStr(body), signDate));
|
responseBody = HttpUtils.post("https://" + HOST + path, header, JSONUtil.toJsonStr(body));
|
||||||
responseBody = HttpUtils.post("https://" + HOST + PATH, header, JSONUtil.toJsonStr(body));
|
}else { // GET 查询template状态用GET请求
|
||||||
}else { // GET
|
responseBody = HttpUtils.get("https://" + HOST + path, header);
|
||||||
templateIdPath = TEMPLATE_PATH + "/" + queryParams.get("template_id");
|
|
||||||
header.put("Authorization", getSignature(httpMethod, HOST, templateIdPath, null, signDate));
|
|
||||||
responseBody = HttpUtils.get("https://" + HOST + templateIdPath, header);
|
|
||||||
}
|
}
|
||||||
return JSONUtil.parseObj(responseBody);
|
return JSONUtil.parseObj(responseBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSignature(String method, String host, String path, String body, String signDate) {
|
public String getSignature(String method, String host, String path, String body, String signDate) {
|
||||||
|
|
||||||
StringBuilder dataToSign = new StringBuilder();
|
StringBuilder dataToSign = new StringBuilder();
|
||||||
dataToSign.append(method.toUpperCase()).append(" ").append(path);
|
dataToSign.append(method.toUpperCase()).append(" ").append(path);
|
||||||
dataToSign.append("\nHost: ").append(host);
|
dataToSign.append("\nHost: ").append(host);
|
||||||
@ -117,18 +107,15 @@ public class QiniuSmsClient extends AbstractSmsClient {
|
|||||||
if (ObjectUtil.isNotEmpty(body)) {
|
if (ObjectUtil.isNotEmpty(body)) {
|
||||||
dataToSign.append(body);
|
dataToSign.append(body);
|
||||||
}
|
}
|
||||||
HMac hMac = new HMac(HmacAlgorithm.HmacSHA1, properties.getApiSecret().getBytes(StandardCharsets.UTF_8));
|
String encodedSignature = SecureUtil.hmac(HmacAlgorithm.HmacSHA1, properties.getApiSecret()).digestBase64(dataToSign.toString(), true);
|
||||||
byte[] signData = hMac.digest(dataToSign.toString().getBytes(StandardCharsets.UTF_8));
|
|
||||||
String encodedSignature = Base64.getEncoder().encodeToString(signData);
|
|
||||||
|
|
||||||
return "Qiniu " + properties.getApiKey() + ":" + encodedSignature;
|
return "Qiniu " + properties.getApiKey() + ":" + encodedSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
|
public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
|
||||||
|
|
||||||
JSONObject status = JSONUtil.parseObj(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()
|
return ListUtil.of(new SmsReceiveRespDTO()
|
||||||
.setSuccess("DELIVRD".equals(status.getJSONArray("items").getJSONObject(0).getStr("status"))) // 是否接收成功
|
.setSuccess("DELIVRD".equals(status.getJSONArray("items").getJSONObject(0).getStr("status"))) // 是否接收成功
|
||||||
.setErrorMsg(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 {
|
public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
|
||||||
// 1. 执行请求
|
// 1. 执行请求
|
||||||
// 参考链接 https://developer.qiniu.com/sms/5969/query-a-single-template
|
// 参考链接 https://developer.qiniu.com/sms/5969/query-a-single-template
|
||||||
HashMap<String, Object> queryParam = new HashMap<>();
|
JSONObject response = request("GET", null, TEMPLATE_PATH + "/" + apiTemplateId);
|
||||||
queryParam.put("template_id", apiTemplateId);
|
|
||||||
JSONObject response = request("GET", null, queryParam);
|
|
||||||
|
|
||||||
// 2.1 请求失败
|
// 2.1 请求失败
|
||||||
String status = response.getStr("audit_status");
|
if (ObjUtil.notEqual(response.getStr("audit_status"), "passed")) {
|
||||||
if (!Objects.equals(status, "passed")) {
|
|
||||||
log.error("[getSmsTemplate][模版编号({}) 响应不正确({})]", apiTemplateId, response);
|
log.error("[getSmsTemplate][模版编号({}) 响应不正确({})]", apiTemplateId, response);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2.2 请求成功
|
// 2.2 请求成功
|
||||||
return new SmsTemplateRespDTO()
|
return new SmsTemplateRespDTO()
|
||||||
.setId(response.getStr("id"))
|
.setId(response.getStr("id"))
|
||||||
@ -162,11 +146,12 @@ public class QiniuSmsClient extends AbstractSmsClient {
|
|||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
Integer convertSmsTemplateAuditStatus(String templateStatus) {
|
Integer convertSmsTemplateAuditStatus(String templateStatus) {
|
||||||
|
return switch (templateStatus) {
|
||||||
if(Objects.equals(templateStatus, "passed")){
|
case "passed" -> SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
|
||||||
return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
|
case "reviewing" -> SmsTemplateAuditStatusEnum.CHECKING.getStatus();
|
||||||
}else {
|
case "rejected" -> SmsTemplateAuditStatusEnum.FAIL.getStatus();
|
||||||
throw new IllegalArgumentException(String.format("未知审核状态(%str)", templateStatus));
|
case null, default ->
|
||||||
}
|
throw new IllegalArgumentException(String.format("未知审核状态(%str)", templateStatus));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ import static org.mockito.Mockito.mockStatic;
|
|||||||
* @author scholar
|
* @author scholar
|
||||||
*/
|
*/
|
||||||
public class QiniuSmsClientTest extends BaseMockitoUnitTest {
|
public class QiniuSmsClientTest extends BaseMockitoUnitTest {
|
||||||
|
|
||||||
private final SmsChannelProperties properties = new SmsChannelProperties()
|
private final SmsChannelProperties properties = new SmsChannelProperties()
|
||||||
.setApiKey(randomString())// 随机一个 apiKey,避免构建报错
|
.setApiKey(randomString())// 随机一个 apiKey,避免构建报错
|
||||||
.setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错
|
.setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错
|
||||||
@ -46,7 +45,6 @@ public class QiniuSmsClientTest extends BaseMockitoUnitTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDoSendSms_success() throws Throwable {
|
public void testDoSendSms_success() throws Throwable {
|
||||||
|
|
||||||
try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) {
|
try (MockedStatic<HttpUtils> httpUtilsMockedStatic = mockStatic(HttpUtils.class)) {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
Long sendLogId = randomLongId();
|
Long sendLogId = randomLongId();
|
||||||
@ -56,9 +54,7 @@ public class QiniuSmsClientTest extends BaseMockitoUnitTest {
|
|||||||
new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
|
new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
|
||||||
// mock 方法
|
// mock 方法
|
||||||
httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
|
httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
|
||||||
.thenReturn(
|
.thenReturn("{\"message_id\":\"17245678901\"}");
|
||||||
"{\"message_id\":\"17245678901\"}"
|
|
||||||
);
|
|
||||||
// 调用
|
// 调用
|
||||||
SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
|
SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
|
||||||
apiTemplateId, templateParams);
|
apiTemplateId, templateParams);
|
||||||
@ -77,17 +73,17 @@ public class QiniuSmsClientTest extends BaseMockitoUnitTest {
|
|||||||
String apiTemplateId = randomString() + " " + randomString();
|
String apiTemplateId = randomString() + " " + randomString();
|
||||||
List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
|
List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
|
||||||
new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
|
new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
|
||||||
|
|
||||||
// mock 方法
|
// mock 方法
|
||||||
httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
|
httpUtilsMockedStatic.when(() -> HttpUtils.post(anyString(), anyMap(), anyString()))
|
||||||
.thenReturn(
|
.thenReturn("{\"error\":\"BadToken\",\"message\":\"Your authorization token is invalid\",\"request_id\":\"etziWcJFo1C8Ne8X\"}");
|
||||||
"{\"error\":\"BadToken\",\"message\":\"Your authorization token is invalid\",\"request_id\":\"etziWcJFo1C8Ne8X\"}"
|
|
||||||
);
|
|
||||||
// 调用
|
// 调用
|
||||||
SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
|
SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
|
||||||
apiTemplateId, templateParams);
|
apiTemplateId, templateParams);
|
||||||
// 断言
|
// 断言
|
||||||
assertFalse(result.getSuccess());
|
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());
|
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)");
|
||||||
|
}
|
||||||
}
|
}
|
@ -116,7 +116,6 @@ public class SmsClientTests {
|
|||||||
@Test
|
@Test
|
||||||
@Disabled
|
@Disabled
|
||||||
public void testQiniuSmsClient_sendSms() throws Throwable {
|
public void testQiniuSmsClient_sendSms() throws Throwable {
|
||||||
|
|
||||||
SmsChannelProperties properties = new SmsChannelProperties()
|
SmsChannelProperties properties = new SmsChannelProperties()
|
||||||
.setApiKey("SMS_QINIU_ACCESS_KEY")
|
.setApiKey("SMS_QINIU_ACCESS_KEY")
|
||||||
.setApiSecret("SMS_QINIU_SECRET_KEY");
|
.setApiSecret("SMS_QINIU_SECRET_KEY");
|
||||||
@ -135,7 +134,6 @@ public class SmsClientTests {
|
|||||||
@Test
|
@Test
|
||||||
@Disabled
|
@Disabled
|
||||||
public void testQiniuSmsClient_getSmsTemplate() throws Throwable {
|
public void testQiniuSmsClient_getSmsTemplate() throws Throwable {
|
||||||
|
|
||||||
SmsChannelProperties properties = new SmsChannelProperties()
|
SmsChannelProperties properties = new SmsChannelProperties()
|
||||||
.setApiKey("SMS_QINIU_ACCESS_KEY")
|
.setApiKey("SMS_QINIU_ACCESS_KEY")
|
||||||
.setApiSecret("SMS_QINIU_SECRET_KEY");
|
.setApiSecret("SMS_QINIU_SECRET_KEY");
|
||||||
|
Loading…
Reference in New Issue
Block a user