Commit 22ff197f02fde92c7cceead31a1f06809282f2d9

Authored by scholar
1 parent f81d56eb

完成todo部分的修改

yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/HuaweiSmsClient.java
@@ -2,35 +2,29 @@ package cn.iocoder.yudao.module.system.framework.sms.core.client.impl; @@ -2,35 +2,29 @@ package cn.iocoder.yudao.module.system.framework.sms.core.client.impl;
2 2
3 3
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 -import cn.hutool.core.util.HexUtil;  
6 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
7 6
8 import cn.hutool.crypto.SecureUtil; 7 import cn.hutool.crypto.SecureUtil;
9 -import cn.hutool.crypto.digest.DigestUtil;  
10 -import cn.hutool.json.JSONArray; 8 +import cn.hutool.http.HttpRequest;
  9 +import cn.hutool.http.HttpResponse;
  10 +import cn.hutool.json.JSONObject;
  11 +import cn.hutool.json.JSONUtil;
11 import cn.iocoder.yudao.framework.common.core.KeyValue; 12 import cn.iocoder.yudao.framework.common.core.KeyValue;
  13 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
12 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 14 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
13 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; 15 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
14 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO; 16 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
15 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; 17 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO;
16 import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; 18 import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
17 import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties; 19 import cn.iocoder.yudao.module.system.framework.sms.core.property.SmsChannelProperties;
18 -import org.apache.http.client.methods.*;  
19 -import org.apache.http.entity.StringEntity;  
20 -import org.apache.http.impl.client.CloseableHttpClient;  
21 -import org.apache.http.impl.client.HttpClientBuilder;  
22 -import org.slf4j.Logger;  
23 -import org.slf4j.LoggerFactory;  
24 20
25 import com.fasterxml.jackson.annotation.JsonFormat; 21 import com.fasterxml.jackson.annotation.JsonFormat;
26 import com.fasterxml.jackson.annotation.JsonProperty; 22 import com.fasterxml.jackson.annotation.JsonProperty;
27 import lombok.Data; 23 import lombok.Data;
28 import lombok.extern.slf4j.Slf4j; 24 import lombok.extern.slf4j.Slf4j;
29 -import org.apache.http.HttpResponse;  
30 25
31 import java.io.UnsupportedEncodingException; 26 import java.io.UnsupportedEncodingException;
32 import java.net.URLEncoder; 27 import java.net.URLEncoder;
33 -import java.nio.charset.StandardCharsets;  
34 import java.text.SimpleDateFormat; 28 import java.text.SimpleDateFormat;
35 import java.util.*; 29 import java.util.*;
36 30
@@ -38,6 +32,7 @@ import java.util.*; @@ -38,6 +32,7 @@ import java.util.*;
38 import java.time.LocalDateTime; 32 import java.time.LocalDateTime;
39 33
40 34
  35 +import static cn.hutool.crypto.digest.DigestUtil.sha256Hex;
41 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
42 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; 37 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
43 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; 38 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
@@ -55,88 +50,90 @@ public class HuaweiSmsClient extends AbstractSmsClient { @@ -55,88 +50,90 @@ public class HuaweiSmsClient extends AbstractSmsClient {
55 /** 50 /**
56 * 调用成功 code 51 * 调用成功 code
57 */ 52 */
58 - public static final String API_CODE_SUCCESS = "OK";  
59 - private static final Logger LOGGER = LoggerFactory.getLogger(HuaweiSmsClient.class); 53 + public static final String URL = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1";//APP接入地址+接口访问URI
  54 + public static final String HOST = "smsapi.cn-north-4.myhuaweicloud.com:443";
  55 + public static final String SIGNEDHEADERS = "content-type;host;x-sdk-date";
  56 +
  57 + @Override
  58 + protected void doInit() {
  59 +
  60 + }
60 61
61 public HuaweiSmsClient(SmsChannelProperties properties) { 62 public HuaweiSmsClient(SmsChannelProperties properties) {
62 super(properties); 63 super(properties);
63 Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); 64 Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
64 Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); 65 Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
65 } 66 }
66 - @Override  
67 - protected void doInit() {  
68 67
69 - }  
70 @Override 68 @Override
71 public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, 69 public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
72 List<KeyValue<String, Object>> templateParams) throws Throwable { 70 List<KeyValue<String, Object>> templateParams) throws Throwable {
73 - String url = "https://smsapi.cn-north-4.myhuaweicloud.com:443/sms/batchSendSms/v1"; //APP接入地址+接口访问URI  
74 // 相比较阿里短信,华为短信发送的时候需要额外的参数“通道号”,考虑到不破坏原有的的结构 71 // 相比较阿里短信,华为短信发送的时候需要额外的参数“通道号”,考虑到不破坏原有的的结构
75 // 所以将 通道号 拼接到 apiTemplateId 字段中,格式为 "apiTemplateId 通道号"。空格为分隔符。 72 // 所以将 通道号 拼接到 apiTemplateId 字段中,格式为 "apiTemplateId 通道号"。空格为分隔符。
76 String sender = StrUtil.subAfter(apiTemplateId, " ", true); //中国大陆短信签名通道号或全球短信通道号 73 String sender = StrUtil.subAfter(apiTemplateId, " ", true); //中国大陆短信签名通道号或全球短信通道号
77 String templateId = StrUtil.subBefore(apiTemplateId, " ", true); //模板ID 74 String templateId = StrUtil.subBefore(apiTemplateId, " ", true); //模板ID
78 75
79 - //必填,全局号码格式(包含国家码),示例:+86151****6789,多个号码之间用英文逗号分隔  
80 - String receiver = mobile; //短信接收人号码  
81 -  
82 //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告 76 //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
83 String statusCallBack = properties.getCallbackUrl(); 77 String statusCallBack = properties.getCallbackUrl();
84 78
  79 + List<String> templateParas = CollectionUtils.convertList(templateParams, kv -> String.valueOf(kv.getValue()));
  80 +
  81 + JSONObject JsonResponse = sendSmsRequest(sender,mobile,templateId,templateParas,statusCallBack);
  82 + SmsResponse smsResponse = getSmsSendResponse(JsonResponse);
  83 +
  84 + return new SmsSendRespDTO().setSuccess(smsResponse.success).setApiMsg(smsResponse.data.toString());
  85 + }
  86 +
  87 + JSONObject sendSmsRequest(String sender,String mobile,String templateId,List<String> templateParas,String statusCallBack) throws UnsupportedEncodingException {
  88 +
85 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.ENGLISH); 89 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.ENGLISH);
86 sdf.setTimeZone(TimeZone.getTimeZone("UTC")); 90 sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
87 - String singerDate = sdf.format(new Date()); 91 + String sdkDate = sdf.format(new Date());
88 92
89 // ************* 步骤 1:拼接规范请求串 ************* 93 // ************* 步骤 1:拼接规范请求串 *************
90 String httpRequestMethod = "POST"; 94 String httpRequestMethod = "POST";
91 String canonicalUri = "/sms/batchSendSms/v1/"; 95 String canonicalUri = "/sms/batchSendSms/v1/";
92 String canonicalQueryString = "";//查询参数为空 96 String canonicalQueryString = "";//查询参数为空
93 String canonicalHeaders = "content-type:application/x-www-form-urlencoded\n" 97 String canonicalHeaders = "content-type:application/x-www-form-urlencoded\n"
94 - + "host:smsapi.cn-north-4.myhuaweicloud.com:443\n"  
95 - + "x-sdk-date:" + singerDate + "\n";  
96 - String signedHeaders = "content-type;host;x-sdk-date";  
97 - /**  
98 - * 选填,使用无变量模板时请赋空值 String templateParas = "";  
99 - * 单变量模板示例:模板内容为"您的验证码是${NUM_6}"时,templateParas可填写为"[\"111111\"]"  
100 - * 双变量模板示例:模板内容为"您有${NUM_2}件快递请到${TXT_20}领取"时,templateParas可填写为"[\"3\",\"人民公园正门\"]"  
101 - */  
102 - List<String> templateParas = new ArrayList<>();  
103 - for (KeyValue<String, Object> kv : templateParams) {  
104 - templateParas.add(String.valueOf(kv.getValue()));  
105 - }  
106 - 98 + + "host:"+ HOST +"\n"
  99 + + "x-sdk-date:" + sdkDate + "\n";
107 //请求Body,不携带签名名称时,signature请填null 100 //请求Body,不携带签名名称时,signature请填null
108 - String body = buildRequestBody(sender, receiver, templateId, templateParas, statusCallBack, null); 101 + String body = buildRequestBody(sender, mobile, templateId, templateParas, statusCallBack, null);
109 if (null == body || body.isEmpty()) { 102 if (null == body || body.isEmpty()) {
110 return null; 103 return null;
111 } 104 }
112 - String hashedRequestBody = HexUtil.encodeHexStr(DigestUtil.sha256(body)); 105 + String hashedRequestBody = sha256Hex(body);
113 String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" 106 String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
114 - + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestBody; 107 + + canonicalHeaders + "\n" + SIGNEDHEADERS + "\n" + hashedRequestBody;
115 108
116 // ************* 步骤 2:拼接待签名字符串 ************* 109 // ************* 步骤 2:拼接待签名字符串 *************
117 - String hashedCanonicalRequest = HexUtil.encodeHexStr(DigestUtil.sha256(canonicalRequest));  
118 - String stringToSign = "SDK-HMAC-SHA256" + "\n" + singerDate + "\n" + hashedCanonicalRequest; 110 + String hashedCanonicalRequest = sha256Hex(canonicalRequest);
  111 + String stringToSign = "SDK-HMAC-SHA256" + "\n" + sdkDate + "\n" + hashedCanonicalRequest;
119 112
120 // ************* 步骤 3:计算签名 ************* 113 // ************* 步骤 3:计算签名 *************
121 String signature = SecureUtil.hmacSha256(properties.getApiSecret()).digestHex(stringToSign); 114 String signature = SecureUtil.hmacSha256(properties.getApiSecret()).digestHex(stringToSign);
122 115
123 // ************* 步骤 4:拼接 Authorization ************* 116 // ************* 步骤 4:拼接 Authorization *************
124 String authorization = "SDK-HMAC-SHA256" + " " + "Access=" + properties.getApiKey() + ", " 117 String authorization = "SDK-HMAC-SHA256" + " " + "Access=" + properties.getApiKey() + ", "
125 - + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature; 118 + + "SignedHeaders=" + SIGNEDHEADERS + ", " + "Signature=" + signature;
126 119
127 // ************* 步骤 5:构造HttpRequest 并执行request请求,获得response ************* 120 // ************* 步骤 5:构造HttpRequest 并执行request请求,获得response *************
128 - HttpUriRequest postMethod = RequestBuilder.post()  
129 - .setUri(url)  
130 - .setEntity(new StringEntity(body, StandardCharsets.UTF_8))  
131 - .setHeader("Content-Type","application/x-www-form-urlencoded")  
132 - .setHeader("X-Sdk-Date",singerDate)  
133 - .setHeader("Authorization",authorization)  
134 - .build();  
135 - CloseableHttpClient client = HttpClientBuilder.create().build();  
136 - HttpResponse response = client.execute(postMethod);  
137 -  
138 - return new SmsSendRespDTO().setSuccess(Objects.equals(response.getStatusLine().getReasonPhrase(), API_CODE_SUCCESS)).setSerialNo(Integer.toString(response.getStatusLine().getStatusCode()))  
139 - .setApiRequestId(null).setApiCode(null).setApiMsg(null); 121 + HttpResponse response = HttpRequest.post(URL)
  122 + .header("Content-Type", "application/x-www-form-urlencoded")
  123 + .header("X-Sdk-Date", sdkDate)
  124 + .header("host",HOST)
  125 + .header("Authorization", authorization)
  126 + .body(body)
  127 + .execute();
  128 +
  129 + return JSONUtil.parseObj(response.body());
  130 + }
  131 +
  132 + private SmsResponse getSmsSendResponse(JSONObject resJson) {
  133 + SmsResponse smsResponse = new SmsResponse();
  134 + smsResponse.setSuccess("000000".equals(resJson.getStr("code")));
  135 + smsResponse.setData(resJson);
  136 + return smsResponse;
140 } 137 }
141 138
142 static String buildRequestBody(String sender, String receiver, String templateId, List<String> templateParas, 139 static String buildRequestBody(String sender, String receiver, String templateId, List<String> templateParas,
@@ -151,7 +148,7 @@ public class HuaweiSmsClient extends AbstractSmsClient { @@ -151,7 +148,7 @@ public class HuaweiSmsClient extends AbstractSmsClient {
151 appendToBody(body, "from=", sender); 148 appendToBody(body, "from=", sender);
152 appendToBody(body, "&to=", receiver); 149 appendToBody(body, "&to=", receiver);
153 appendToBody(body, "&templateId=", templateId); 150 appendToBody(body, "&templateId=", templateId);
154 - appendToBody(body, "&templateParas=", new JSONArray(templateParas).toString()); 151 + appendToBody(body, "&templateParas=", JsonUtils.toJsonString(templateParas));
155 appendToBody(body, "&statusCallback=", statusCallBack); 152 appendToBody(body, "&statusCallback=", statusCallBack);
156 appendToBody(body, "&signature=", signature); 153 appendToBody(body, "&signature=", signature);
157 return body.toString(); 154 return body.toString();
@@ -179,6 +176,22 @@ public class HuaweiSmsClient extends AbstractSmsClient { @@ -179,6 +176,22 @@ public class HuaweiSmsClient extends AbstractSmsClient {
179 176
180 } 177 }
181 178
  179 + @Data
  180 + public static class SmsResponse {
  181 +
  182 + /**
  183 + * 是否成功
  184 + */
  185 + private boolean success;
  186 +
  187 + /**
  188 + * 厂商原返回体
  189 + */
  190 + private Object data;
  191 +
  192 + }
  193 +
  194 +
182 /** 195 /**
183 * 短信接收状态 196 * 短信接收状态
184 * 197 *