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 2  
3 3  
4 4 import cn.hutool.core.lang.Assert;
5   -import cn.hutool.core.util.HexUtil;
6 5 import cn.hutool.core.util.StrUtil;
7 6  
8 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 12 import cn.iocoder.yudao.framework.common.core.KeyValue;
  13 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
12 14 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
13 15 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO;
14 16 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsSendRespDTO;
15 17 import cn.iocoder.yudao.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO;
16 18 import cn.iocoder.yudao.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
17 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 21 import com.fasterxml.jackson.annotation.JsonFormat;
26 22 import com.fasterxml.jackson.annotation.JsonProperty;
27 23 import lombok.Data;
28 24 import lombok.extern.slf4j.Slf4j;
29   -import org.apache.http.HttpResponse;
30 25  
31 26 import java.io.UnsupportedEncodingException;
32 27 import java.net.URLEncoder;
33   -import java.nio.charset.StandardCharsets;
34 28 import java.text.SimpleDateFormat;
35 29 import java.util.*;
36 30  
... ... @@ -38,6 +32,7 @@ import java.util.*;
38 32 import java.time.LocalDateTime;
39 33  
40 34  
  35 +import static cn.hutool.crypto.digest.DigestUtil.sha256Hex;
41 36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
42 37 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
43 38 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
... ... @@ -55,88 +50,90 @@ public class HuaweiSmsClient extends AbstractSmsClient {
55 50 /**
56 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 62 public HuaweiSmsClient(SmsChannelProperties properties) {
62 63 super(properties);
63 64 Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
64 65 Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
65 66 }
66   - @Override
67   - protected void doInit() {
68 67  
69   - }
70 68 @Override
71 69 public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
72 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 72 // 所以将 通道号 拼接到 apiTemplateId 字段中,格式为 "apiTemplateId 通道号"。空格为分隔符。
76 73 String sender = StrUtil.subAfter(apiTemplateId, " ", true); //中国大陆短信签名通道号或全球短信通道号
77 74 String templateId = StrUtil.subBefore(apiTemplateId, " ", true); //模板ID
78 75  
79   - //必填,全局号码格式(包含国家码),示例:+86151****6789,多个号码之间用英文逗号分隔
80   - String receiver = mobile; //短信接收人号码
81   -
82 76 //选填,短信状态报告接收地址,推荐使用域名,为空或者不填表示不接收状态报告
83 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 89 SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.ENGLISH);
86 90 sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
87   - String singerDate = sdf.format(new Date());
  91 + String sdkDate = sdf.format(new Date());
88 92  
89 93 // ************* 步骤 1:拼接规范请求串 *************
90 94 String httpRequestMethod = "POST";
91 95 String canonicalUri = "/sms/batchSendSms/v1/";
92 96 String canonicalQueryString = "";//查询参数为空
93 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 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 102 if (null == body || body.isEmpty()) {
110 103 return null;
111 104 }
112   - String hashedRequestBody = HexUtil.encodeHexStr(DigestUtil.sha256(body));
  105 + String hashedRequestBody = sha256Hex(body);
113 106 String canonicalRequest = httpRequestMethod + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
114   - + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestBody;
  107 + + canonicalHeaders + "\n" + SIGNEDHEADERS + "\n" + hashedRequestBody;
115 108  
116 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 113 // ************* 步骤 3:计算签名 *************
121 114 String signature = SecureUtil.hmacSha256(properties.getApiSecret()).digestHex(stringToSign);
122 115  
123 116 // ************* 步骤 4:拼接 Authorization *************
124 117 String authorization = "SDK-HMAC-SHA256" + " " + "Access=" + properties.getApiKey() + ", "
125   - + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
  118 + + "SignedHeaders=" + SIGNEDHEADERS + ", " + "Signature=" + signature;
126 119  
127 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 139 static String buildRequestBody(String sender, String receiver, String templateId, List<String> templateParas,
... ... @@ -151,7 +148,7 @@ public class HuaweiSmsClient extends AbstractSmsClient {
151 148 appendToBody(body, "from=", sender);
152 149 appendToBody(body, "&to=", receiver);
153 150 appendToBody(body, "&templateId=", templateId);
154   - appendToBody(body, "&templateParas=", new JSONArray(templateParas).toString());
  151 + appendToBody(body, "&templateParas=", JsonUtils.toJsonString(templateParas));
155 152 appendToBody(body, "&statusCallback=", statusCallBack);
156 153 appendToBody(body, "&signature=", signature);
157 154 return body.toString();
... ... @@ -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 *
... ...