From 799affcecb2d0d06e4c28e39a53cd40eb19eb42b Mon Sep 17 00:00:00 2001 From: gaoqr <13665037151@163.com> Date: Thu, 17 Jul 2025 18:23:49 +0800 Subject: [PATCH] =?UTF-8?q?cf-framework=EF=BC=9Asms=E7=9F=AD=E4=BF=A1clien?= =?UTF-8?q?t=E7=A7=BB=E9=99=A4=E4=BA=91=E5=8E=82=E5=95=86sdk=EF=BC=8C?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E5=AF=B9=E6=8E=A5http=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../imes/framework/common/core/KeyValue.java | 22 ++ .../common/util/collection/MapUtils.java | 4 +- .../framework/common/util/http/HttpUtils.java | 123 ++++++--- cf-module-system/cf-module-system-biz/pom.xml | 17 -- .../admin/sms/SmsCallbackController.java | 10 - .../framework/sms/core/client/SmsClient.java | 4 +- .../core/client/impl/AbstractSmsClient.java | 14 - .../client/impl/SmsClientFactoryImpl.java | 4 +- .../client/impl/aliyun/AliyunSmsClient.java | 261 +++++++++--------- .../impl/debug/DebugDingTalkSmsClient.java | 17 +- .../client/impl/tencent/TencentSmsClient.java | 229 --------------- .../sms/core/enums/SmsChannelEnum.java | 5 +- .../system/mq/message/sms/SmsSendMessage.java | 4 +- .../system/mq/producer/sms/SmsProducer.java | 4 +- .../service/sms/SmsSendServiceImpl.java | 10 +- .../sms/handler/SmsSendAfterSendHandler.java | 4 +- .../impl/SmsSendAfterSendHandlerImpl.java | 6 +- .../service/sms/SmsSendServiceImplTest.java | 8 +- 18 files changed, 259 insertions(+), 487 deletions(-) create mode 100644 cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/core/KeyValue.java delete mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/tencent/TencentSmsClient.java diff --git a/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/core/KeyValue.java b/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/core/KeyValue.java new file mode 100644 index 000000000..6bd6e5b29 --- /dev/null +++ b/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/core/KeyValue.java @@ -0,0 +1,22 @@ +package com.cf.imes.framework.common.core; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * Key Value 的键值对 + * + * @author + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class KeyValue implements Serializable { + + private K key; + private V value; + +} diff --git a/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/util/collection/MapUtils.java b/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/util/collection/MapUtils.java index 8c74382ba..6a5a923d3 100644 --- a/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/util/collection/MapUtils.java +++ b/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/util/collection/MapUtils.java @@ -1,7 +1,7 @@ package com.cf.imes.framework.common.util.collection; import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Pair; +import com.cf.imes.framework.common.core.KeyValue; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; @@ -56,7 +56,7 @@ public class MapUtils { consumer.accept(value); } - public static Map convertMap(List> keyValues) { + public static Map convertMap(List> keyValues) { Map map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size()); keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue())); return map; diff --git a/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/util/http/HttpUtils.java b/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/util/http/HttpUtils.java index 9ac1146f6..7d65af264 100644 --- a/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/util/http/HttpUtils.java +++ b/cf-framework/cf-common/src/main/java/com/cf/imes/framework/common/util/http/HttpUtils.java @@ -5,13 +5,18 @@ import cn.hutool.core.map.TableMap; import cn.hutool.core.net.url.UrlBuilder; import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; import org.springframework.util.StringUtils; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import javax.servlet.http.HttpServletRequest; import java.net.URI; +import java.net.URLEncoder; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Map; /** @@ -21,6 +26,16 @@ import java.util.Map; */ public class HttpUtils { + /** + * 编码 URL 参数 + * + * @param value 参数 + * @return 编码后的参数 + */ + public static String encodeUtf8(String value) { + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } + @SuppressWarnings("unchecked") public static String replaceUrlQuery(String url, String key, String value) { UrlBuilder builder = UrlBuilder.of(url, Charset.defaultCharset()); @@ -64,49 +79,41 @@ public class HttpUtils { .userInfo(redirectUri.getUserInfo()).path(redirectUri.getPath()); if (fragment) { - buildFragmentUri(template, builder, redirectUri, query, keys); + StringBuilder values = new StringBuilder(); + if (redirectUri.getFragment() != null) { + String append = redirectUri.getFragment(); + values.append(append); + } + for (String key : query.keySet()) { + if (values.length() > 0) { + values.append("&"); + } + String name = key; + if (keys != null && keys.containsKey(key)) { + name = keys.get(key); + } + values.append(name).append("={").append(key).append("}"); + } + if (values.length() > 0) { + template.fragment(values.toString()); + } + UriComponents encoded = template.build().expand(query).encode(); + builder.fragment(encoded.getFragment()); } else { - buildUri(template, builder, redirectUri, query, keys); + for (String key : query.keySet()) { + String name = key; + if (keys != null && keys.containsKey(key)) { + name = keys.get(key); + } + template.queryParam(name, "{" + key + "}"); + } + template.fragment(redirectUri.getFragment()); + UriComponents encoded = template.build().expand(query).encode(); + builder.query(encoded.getQuery()); } return builder.build().toUriString(); } - private static void buildUri(UriComponentsBuilder template, UriComponentsBuilder builder, URI redirectUri, Map query, Map keys) { - for (String key : query.keySet()) { - String name = key; - if (keys != null && keys.containsKey(key)) { - name = keys.get(key); - } - template.queryParam(name, "{" + key + "}"); - } - template.fragment(redirectUri.getFragment()); - UriComponents encoded = template.build().expand(query).encode(); - builder.query(encoded.getQuery()); - } - - private static void buildFragmentUri(UriComponentsBuilder template, UriComponentsBuilder builder, URI redirectUri, Map query, Map keys) { - StringBuilder values = new StringBuilder(); - if (redirectUri.getFragment() != null) { - String append = redirectUri.getFragment(); - values.append(append); - } - for (String key : query.keySet()) { - if (values.length() > 0) { - values.append("&"); - } - String name = key; - if (keys != null && keys.containsKey(key)) { - name = keys.get(key); - } - values.append(name).append("={").append(key).append("}"); - } - if (values.length() > 0) { - template.fragment(values.toString()); - } - UriComponents encoded = template.build().expand(query).encode(); - builder.fragment(encoded.getFragment()); - } - public static String[] obtainBasicAuthorization(HttpServletRequest request) { String clientId; String clientSecret; @@ -117,18 +124,52 @@ public class HttpUtils { authorization = Base64.decodeStr(authorization); clientId = CharSequenceUtil.subBefore(authorization, ":", false); clientSecret = CharSequenceUtil.subAfter(authorization, ":", false); - // 再从 Param 中获取 + // 再从 Param 中获取 } else { clientId = request.getParameter("client_id"); clientSecret = request.getParameter("client_secret"); } // 如果两者非空,则返回 - if (CharSequenceUtil.isNotEmpty(clientId) && CharSequenceUtil.isNotEmpty(clientSecret)) { + if (StrUtil.isNotEmpty(clientId) && StrUtil.isNotEmpty(clientSecret)) { return new String[]{clientId, clientSecret}; } return null; } + /** + * HTTP post 请求,基于 {@link cn.hutool.http.HttpUtil} 实现 + * + * 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数 + * + * @param url URL + * @param headers 请求头 + * @param requestBody 请求体 + * @return 请求结果 + */ + public static String post(String url, Map headers, String requestBody) { + try (HttpResponse response = HttpRequest.post(url) + .addHeaders(headers) + .body(requestBody) + .execute()) { + return response.body(); + } + } -} + /** + * HTTP get 请求,基于 {@link cn.hutool.http.HttpUtil} 实现 + * + * 为什么要封装该方法,因为 HttpUtil 默认封装的方法,没有允许传递 headers 参数 + * + * @param url URL + * @param headers 请求头 + * @return 请求结果 + */ + public static String get(String url, Map headers) { + try (HttpResponse response = HttpRequest.get(url) + .addHeaders(headers) + .execute()) { + return response.body(); + } + } +} \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/pom.xml b/cf-module-system/cf-module-system-biz/pom.xml index 4c7bc271f..7e6dd8634 100644 --- a/cf-module-system/cf-module-system-biz/pom.xml +++ b/cf-module-system/cf-module-system-biz/pom.xml @@ -209,23 +209,6 @@ cf-spring-boot-starter-biz-pay - - - - - com.aliyun - aliyun-java-sdk-core - - - com.aliyun - aliyun-java-sdk-dysmsapi - - - com.tencentcloudapi - tencentcloud-sdk-java-sms - - - diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsCallbackController.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsCallbackController.java index 9f15190b9..265f4a275 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsCallbackController.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/sms/SmsCallbackController.java @@ -35,14 +35,4 @@ public class SmsCallbackController { return success(true); } - @PostMapping("/tencent") - @PermitAll - @Operation(summary = "腾讯云短信的回调", description = "参见 https://cloud.tencent.com/document/product/382/52077 文档") - @OperateLog(enable = false) - public CommonResult receiveTencentSmsStatus(HttpServletRequest request) throws Throwable { - String text = ServletUtils.getBody(request); - smsSendService.receiveSmsStatus(SmsChannelEnum.TENCENT.getCode(), text); - return success(true); - } - } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/SmsClient.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/SmsClient.java index 75f05504a..ea994ea8e 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/SmsClient.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/SmsClient.java @@ -1,6 +1,6 @@ package com.cf.imes.module.system.framework.sms.core.client; -import cn.hutool.core.lang.Pair; +import com.cf.imes.framework.common.core.KeyValue; import com.cf.imes.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; import com.cf.imes.module.system.framework.sms.core.client.dto.SmsSendRespDTO; import com.cf.imes.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; @@ -24,7 +24,7 @@ public interface SmsClient { * @return 短信发送结果 */ SmsSendRespDTO sendSms(Long logId, String mobile, String apiTemplateId, - List> templateParams) throws Throwable; + List> templateParams) throws Throwable; /** * 解析接收短信的接收结果 diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/AbstractSmsClient.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/AbstractSmsClient.java index 339ea45f7..fc7629896 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/AbstractSmsClient.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/AbstractSmsClient.java @@ -26,23 +26,9 @@ public abstract class AbstractSmsClient implements SmsClient { * 初始化 */ public final void init() { - doInit(); log.debug("[init][配置({}) 初始化完成]", properties); } - /** - * 自定义初始化 - */ - protected abstract void doInit(); - - /** - * 配置是否发生改变 - * - * @param properties - * @return - */ - protected abstract boolean propChanged(SmsProperties properties); - public final void refresh(SmsProperties properties) { // 判断是否更新 if (properties.equals(this.properties)) { diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java index d54f2eab6..c6568535f 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/SmsClientFactoryImpl.java @@ -5,7 +5,6 @@ import com.cf.imes.module.system.framework.sms.core.client.SmsClient; import com.cf.imes.module.system.framework.sms.core.client.SmsClientFactory; import com.cf.imes.module.system.framework.sms.core.client.impl.aliyun.AliyunSmsClient; import com.cf.imes.module.system.framework.sms.core.client.impl.debug.DebugDingTalkSmsClient; -import com.cf.imes.module.system.framework.sms.core.client.impl.tencent.TencentSmsClient; import com.cf.imes.module.system.framework.sms.core.enums.SmsChannelEnum; import com.cf.imes.module.system.framework.sms.core.property.SmsChannelProperties; import com.cf.imes.module.system.framework.sms.core.property.SmsProperties; @@ -48,7 +47,7 @@ public class SmsClientFactoryImpl implements SmsClientFactory { String channel = smsProperties.getChannel(); AbstractSmsClient client = channelCodeClients.get(channel); // 1、新的短信类型;2、配置改变 - if (ObjectUtil.isNull(client) || client.propChanged(smsProperties)) { + if (ObjectUtil.isNull(client)) { client = this.createSmsClient(smsProperties); client.init(); channelCodeClients.put(channel, client); @@ -69,7 +68,6 @@ public class SmsClientFactoryImpl implements SmsClientFactory { switch (channelEnum) { case ALIYUN: return new AliyunSmsClient(smsProperties); case DEBUG_DING_TALK: return new DebugDingTalkSmsClient(smsProperties); - case TENCENT: return new TencentSmsClient(smsProperties); } // 创建失败,错误日志 + 抛出异常 log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", smsProperties); diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java index cff829d45..4a0bf9a32 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java @@ -1,18 +1,17 @@ package com.cf.imes.module.system.framework.sms.core.client.impl.aliyun; +import cn.hutool.core.date.format.FastDateFormat; import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Pair; +import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.ObjectUtil; -import com.aliyuncs.DefaultAcsClient; -import com.aliyuncs.IAcsClient; -import com.aliyuncs.auth.Credential; -import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest; -import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse; -import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; -import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; -import com.aliyuncs.profile.DefaultProfile; -import com.aliyuncs.profile.IClientProfile; +import cn.hutool.crypto.SecureUtil; +import cn.hutool.crypto.digest.DigestUtil; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import com.cf.imes.framework.common.core.KeyValue; import com.cf.imes.framework.common.util.collection.MapUtils; +import com.cf.imes.framework.common.util.http.HttpUtils; import com.cf.imes.framework.common.util.json.JsonUtils; import com.cf.imes.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; import com.cf.imes.module.system.framework.sms.core.client.dto.SmsSendRespDTO; @@ -20,19 +19,19 @@ import com.cf.imes.module.system.framework.sms.core.client.dto.SmsTemplateRespDT import com.cf.imes.module.system.framework.sms.core.client.impl.AbstractSmsClient; import com.cf.imes.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import com.cf.imes.module.system.framework.sms.core.property.SmsProperties; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; -import lombok.Data; +import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; -import java.time.LocalDateTime; +import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.TimeZone; +import java.util.TreeMap; +import java.util.stream.Collectors; import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertList; -import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -import static com.cf.imes.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; /** * 阿里短信客户端的实现类 @@ -43,20 +42,11 @@ import static com.cf.imes.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT @Slf4j public class AliyunSmsClient extends AbstractSmsClient { - /** - * 调用成功 code - */ - public static final String API_CODE_SUCCESS = "OK"; + private static final String URL = "https://dysmsapi.aliyuncs.com"; + private static final String HOST = "dysmsapi.aliyuncs.com"; + private static final String VERSION = "2017-05-25"; - /** - * REGION, 使用杭州 - */ - private static final String ENDPOINT = "cn-hangzhou"; - - /** - * 阿里云客户端 - */ - private volatile IAcsClient client; + private static final String RESPONSE_CODE_SUCCESS = "OK"; public AliyunSmsClient(SmsProperties properties) { super(properties); @@ -64,66 +54,66 @@ public class AliyunSmsClient extends AbstractSmsClient { Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); } - @Override - protected void doInit() { - IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, properties.getApiKey(), properties.getApiSecret()); - client = new DefaultAcsClient(profile); - } - - @Override - protected boolean propChanged(SmsProperties properties) { - DefaultAcsClient defaultAcsClient = (DefaultAcsClient) client; - if (ObjectUtil.isNotNull(defaultAcsClient)) { - Credential credential = defaultAcsClient.getProfile().getCredential(); - String accessKeyId = credential.getAccessKeyId(); - if (ObjectUtil.notEqual(properties.getApiKey(), accessKeyId)) { - return true; - } - String accessSecret = credential.getAccessSecret(); - if (ObjectUtil.notEqual(properties.getApiSecret(), accessSecret)) { - return true; - } - } - return false; - } - @Override public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, - List> templateParams) throws Throwable { - // 构建请求 - SendSmsRequest request = new SendSmsRequest(); - request.setPhoneNumbers(mobile); - request.setSignName(properties.getSignature()); - request.setTemplateCode(apiTemplateId); - request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams))); - request.setOutId(String.valueOf(sendLogId)); - // 执行请求 - SendSmsResponse response = client.getAcsResponse(request); - return new SmsSendRespDTO().setSuccess(Objects.equals(response.getCode(), API_CODE_SUCCESS)).setSerialNo(response.getBizId()) - .setApiRequestId(response.getRequestId()).setApiCode(response.getCode()).setApiMsg(response.getMessage()).setMobile(mobile).setChannelCode(properties.getChannel()); + List> templateParams) throws Throwable { + Assert.notBlank(properties.getSignature(), "短信签名不能为空"); + // 1. 执行请求 + // 参考链接 https://api.aliyun.com/document/Dysmsapi/2017-05-25/SendSms + TreeMap queryParam = new TreeMap<>(); + queryParam.put("PhoneNumbers", mobile); + queryParam.put("SignName", properties.getSignature()); + queryParam.put("TemplateCode", apiTemplateId); + queryParam.put("TemplateParam", JsonUtils.toJsonString(MapUtils.convertMap(templateParams))); + queryParam.put("OutId", sendLogId); + JSONObject response = request("SendSms", queryParam); + + // 2. 解析请求 + return new SmsSendRespDTO() + .setSuccess(Objects.equals(response.getStr("Code"), RESPONSE_CODE_SUCCESS)) + .setSerialNo(response.getStr("BizId")) + .setApiRequestId(response.getStr("RequestId")) + .setApiCode(response.getStr("Code")) + .setApiMsg(response.getStr("Message")); } @Override public List parseSmsReceiveStatus(String text) { - List statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class); - return convertList(statuses, status -> new SmsReceiveRespDTO().setSuccess(status.getSuccess()) - .setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg()) - .setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime()) - .setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId()))); + JSONArray statuses = JSONUtil.parseArray(text); + // 字段参考 https://help.aliyun.com/zh/sms/developer-reference/smsreport-2 + return convertList(statuses, status -> { + JSONObject statusObj = (JSONObject) status; + return new SmsReceiveRespDTO() + .setSuccess(statusObj.getBool("success")) // 是否接收成功 + .setErrorCode(statusObj.getStr("err_code")) // 状态报告编码 + .setErrorMsg(statusObj.getStr("err_msg")) // 状态报告说明 + .setMobile(statusObj.getStr("phone_number")) // 手机号 + .setReceiveTime(statusObj.getLocalDateTime("report_time", null)) // 状态报告时间 + .setSerialNo(statusObj.getStr("biz_id")) // 发送序列号 + .setLogId(statusObj.getLong("out_id")); // 用户序列号 + }); } @Override public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { - // 构建请求 - QuerySmsTemplateRequest request = new QuerySmsTemplateRequest(); - request.setTemplateCode(apiTemplateId); - // 执行请求 - QuerySmsTemplateResponse response = client.getAcsResponse(request); - if (response.getTemplateStatus() == null) { + // 1. 执行请求 + // 参考链接 https://api.aliyun.com/document/Dysmsapi/2017-05-25/QuerySmsTemplate + TreeMap queryParam = new TreeMap<>(); + queryParam.put("TemplateCode", apiTemplateId); + JSONObject response = request("QuerySmsTemplate", queryParam); + + // 2.1 请求失败 + String code = response.getStr("Code"); + if (ObjectUtil.notEqual(code, RESPONSE_CODE_SUCCESS)) { + log.error("[getSmsTemplate][模版编号({}) 响应不正确({})]", apiTemplateId, response); return null; } - return new SmsTemplateRespDTO().setId(response.getTemplateCode()).setContent(response.getTemplateContent()) - .setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason()); + // 2.2 请求成功 + return new SmsTemplateRespDTO() + .setId(response.getStr("TemplateCode")) + .setContent(response.getStr("TemplateContent")) + .setAuditStatus(convertSmsTemplateAuditStatus(response.getInt("TemplateStatus"))) + .setAuditReason(response.getStr("Reason")); } @VisibleForTesting @@ -137,66 +127,71 @@ public class AliyunSmsClient extends AbstractSmsClient { } /** - * 短信接收状态 + * 请求阿里云短信 * - * 参见 文档 - * - * @author 晨丰科技 + * @see V3 版本请求体&签名机制 + * @param apiName 请求的 API 名称 + * @param queryParams 请求参数 + * @return 请求结果 */ - @Data - public static class SmsReceiveStatus { + private JSONObject request(String apiName, TreeMap queryParams) { + // 1. 请求参数 + String queryString = queryParams.entrySet().stream() + .map(entry -> percentCode(entry.getKey()) + "=" + percentCode(String.valueOf(entry.getValue()))) + .collect(Collectors.joining("&")); - /** - * 手机号 - */ - @JsonProperty("phone_number") - private String phoneNumber; - /** - * 发送时间 - */ - @JsonProperty("send_time") - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private LocalDateTime sendTime; - /** - * 状态报告时间 - */ - @JsonProperty("report_time") - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private LocalDateTime reportTime; - /** - * 是否接收成功 - */ - private Boolean success; - /** - * 状态报告说明 - */ - @JsonProperty("err_msg") - private String errMsg; - /** - * 状态报告编码 - */ - @JsonProperty("err_code") - private String errCode; - /** - * 发送序列号 - */ - @JsonProperty("biz_id") - private String bizId; - /** - * 用户序列号 - * - * 这里我们传递的是 SysSmsLogDO 的日志编号 - */ - @JsonProperty("out_id") - private String outId; - /** - * 短信长度,例如说 1、2、3 - * - * 140 字节算一条短信,短信长度超过 140 字节时会拆分成多条短信发送 - */ - @JsonProperty("sms_size") - private Integer smsSize; + // 2.1 请求 Header + TreeMap headers = new TreeMap<>(); + headers.put("host", HOST); + headers.put("x-acs-version", VERSION); + headers.put("x-acs-action", apiName); + headers.put("x-acs-date", FastDateFormat.getInstance("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone("GMT")).format(new Date())); + headers.put("x-acs-signature-nonce", IdUtil.randomUUID()); + // 2.2 构建签名 Header + StringBuilder canonicalHeaders = new StringBuilder(); // 构造请求头,多个规范化消息头,按照消息头名称(小写)的字符代码顺序以升序排列后拼接在一起 + StringBuilder signedHeadersBuilder = new StringBuilder(); // 已签名消息头列表,多个请求头名称(小写)按首字母升序排列并以英文分号(;)分隔 + headers.entrySet().stream().filter(entry -> entry.getKey().toLowerCase().startsWith("x-acs-") + || entry.getKey().equalsIgnoreCase("host") + || entry.getKey().equalsIgnoreCase("content-type")) + .sorted(Map.Entry.comparingByKey()).forEach(entry -> { + String lowerKey = entry.getKey().toLowerCase(); + canonicalHeaders.append(lowerKey).append(":").append(String.valueOf(entry.getValue()).trim()).append("\n"); + signedHeadersBuilder.append(lowerKey).append(";"); + }); + String signedHeaders = signedHeadersBuilder.substring(0, signedHeadersBuilder.length() - 1); + + // 3. 请求 Body + String requestBody = ""; // 短信 API 为 RPC 接口,query parameters 在 uri 中拼接,因此 request body 如果没有特殊要求,设置为空。 + String hashedRequestBody = DigestUtil.sha256Hex(requestBody); + + // 4. 构建 Authorization 签名 + String canonicalRequest = "POST" + "\n" + "/" + "\n" + queryString + "\n" + + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestBody; + String hashedCanonicalRequest = DigestUtil.sha256Hex(canonicalRequest); + String stringToSign = "ACS3-HMAC-SHA256" + "\n" + hashedCanonicalRequest; + String signature = SecureUtil.hmacSha256(properties.getApiSecret()).digestHex(stringToSign); // 计算签名 + headers.put("Authorization", "ACS3-HMAC-SHA256" + " " + "Credential=" + properties.getApiKey() + + ", " + "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature); + + // 5. 发起请求 + String responseBody = HttpUtils.post(URL + "?" + queryString, headers, requestBody); + return JSONUtil.parseObj(responseBody); + } + + /** + * 对指定的字符串进行 URL 编码,并对特定的字符进行替换,以符合URL编码规范 + * + * @param str 需要进行 URL 编码的字符串 + * @return 编码后的字符串 + */ + @SneakyThrows + private static String percentCode(String str) { + Assert.notNull(str, "str 不能为空"); + return HttpUtils.encodeUtf8(str) + .replace("+", "%20") // 加号 "+" 被替换为 "%20" + .replace("*", "%2A") // 星号 "*" 被替换为 "%2A" + .replace("%7E", "~"); // 波浪号 "%7E" 被替换为 "~" } } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java index c30940a4b..c5a977613 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java @@ -2,13 +2,13 @@ package com.cf.imes.module.system.framework.sms.core.client.impl.debug; import cn.hutool.core.codec.Base64; import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Pair; import cn.hutool.core.map.MapUtil; import cn.hutool.core.text.CharSequenceUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.crypto.digest.HmacAlgorithm; import cn.hutool.http.HttpUtil; +import com.cf.imes.framework.common.core.KeyValue; import com.cf.imes.framework.common.util.collection.MapUtils; import com.cf.imes.framework.common.util.json.JsonUtils; import com.cf.imes.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; @@ -38,19 +38,9 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient { Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); } - @Override - protected void doInit() { - // nothing - } - - @Override - protected boolean propChanged(SmsProperties properties) { - return false; - } - @Override public SmsSendRespDTO sendSms(Long sendLogId, String mobile, - String apiTemplateId, List> templateParams) throws Throwable { + String apiTemplateId, List> templateParams) throws Throwable { // 构建请求 String url = buildUrl("robot/send"); Map params = new HashMap<>(); @@ -64,7 +54,7 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient { Map responseObj = JsonUtils.parseObject(responseText, Map.class); String errorCode = MapUtil.getStr(responseObj, "errcode"); return new SmsSendRespDTO().setSuccess(Objects.equals(errorCode, "0")).setSerialNo(StrUtil.uuid()) - .setApiCode(errorCode).setApiMsg(MapUtil.getStr(responseObj, "errmsg")).setMobile(mobile).setChannelCode(properties.getChannel()); + .setApiCode(errorCode).setApiMsg(MapUtil.getStr(responseObj, "errorMsg")); } /** @@ -99,5 +89,4 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient { return new SmsTemplateRespDTO().setId(apiTemplateId).setContent("") .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(""); } - } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/tencent/TencentSmsClient.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/tencent/TencentSmsClient.java deleted file mode 100644 index 3a1960cfd..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/client/impl/tencent/TencentSmsClient.java +++ /dev/null @@ -1,229 +0,0 @@ -package com.cf.imes.module.system.framework.sms.core.client.impl.tencent; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Pair; -import cn.hutool.core.text.CharSequenceUtil; -import com.cf.imes.framework.common.util.collection.ArrayUtils; -import com.cf.imes.framework.common.util.json.JsonUtils; -import com.cf.imes.module.system.framework.sms.core.client.dto.SmsReceiveRespDTO; -import com.cf.imes.module.system.framework.sms.core.client.dto.SmsSendRespDTO; -import com.cf.imes.module.system.framework.sms.core.client.dto.SmsTemplateRespDTO; -import com.cf.imes.module.system.framework.sms.core.client.impl.AbstractSmsClient; -import com.cf.imes.module.system.framework.sms.core.enums.SmsTemplateAuditStatusEnum; -import com.cf.imes.module.system.framework.sms.core.property.SmsProperties; -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.annotations.VisibleForTesting; -import com.tencentcloudapi.common.Credential; -import com.tencentcloudapi.sms.v20210111.SmsClient; -import com.tencentcloudapi.sms.v20210111.models.DescribeSmsTemplateListRequest; -import com.tencentcloudapi.sms.v20210111.models.DescribeSmsTemplateListResponse; -import com.tencentcloudapi.sms.v20210111.models.DescribeTemplateListStatus; -import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest; -import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse; -import com.tencentcloudapi.sms.v20210111.models.SendStatus; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Objects; - -import static com.cf.imes.framework.common.util.collection.CollectionUtils.convertList; -import static com.cf.imes.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; -import static com.cf.imes.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; - -/** - * 腾讯云短信功能实现 - * - * 参见 文档 - * - * @author shiwp - */ -public class TencentSmsClient extends AbstractSmsClient { - - /** - * 调用成功 code - */ - public static final String API_CODE_SUCCESS = "Ok"; - - /** - * REGION,使用南京 - */ - private static final String ENDPOINT = "ap-nanjing"; - - /** - * 是否国际/港澳台短信: - * - * 0:表示国内短信。 - * 1:表示国际/港澳台短信。 - */ - private static final long INTERNATIONAL_CHINA = 0L; - - private SmsClient client; - - public TencentSmsClient(SmsProperties properties) { - super(properties); - Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); - validateSdkAppId(properties); - } - - @Override - protected void doInit() { - // 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId,secretKey - Credential credential = new Credential(getApiKey(), properties.getApiSecret()); - client = new SmsClient(credential, ENDPOINT); - } - - @Override - protected boolean propChanged(SmsProperties properties) { - return false; - } - - /** - * 参数校验腾讯云的 SDK AppId - * - * 原因是:腾讯云发放短信的时候,需要额外的参数 sdkAppId - * - * 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。 - * - * @param properties 配置 - */ - private static void validateSdkAppId(SmsProperties properties) { - String combineKey = properties.getApiKey(); - Assert.notEmpty(combineKey, "apiKey 不能为空"); - String[] keys = combineKey.trim().split(" "); - Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]"); - } - - private String getSdkAppId() { - return CharSequenceUtil.subAfter(properties.getApiKey(), " ", true); - } - - private String getApiKey() { - return CharSequenceUtil.subBefore(properties.getApiKey(), " ", true); - } - - @Override - public SmsSendRespDTO sendSms(Long sendLogId, String mobile, - String apiTemplateId, List> templateParams) throws Throwable { - // 构建请求 - SendSmsRequest request = new SendSmsRequest(); - request.setSmsSdkAppId(getSdkAppId()); - request.setPhoneNumberSet(new String[]{mobile}); - request.setSignName(properties.getSignature()); - request.setTemplateId(apiTemplateId); - request.setTemplateParamSet(ArrayUtils.toArray(templateParams, e -> String.valueOf(e.getValue()))); - request.setSessionContext(JsonUtils.toJsonString(new SessionContext().setLogId(sendLogId))); - // 执行请求 - SendSmsResponse response = client.SendSms(request); - SendStatus status = response.getSendStatusSet()[0]; - return new SmsSendRespDTO().setSuccess(Objects.equals(status.getCode(), API_CODE_SUCCESS)).setSerialNo(status.getSerialNo()) - .setApiRequestId(response.getRequestId()).setApiCode(status.getCode()).setApiMsg(status.getMessage()).setChannelCode(properties.getChannel()); - } - - @Override - public List parseSmsReceiveStatus(String text) { - List callback = JsonUtils.parseArray(text, SmsReceiveStatus.class); - return convertList(callback, status -> new SmsReceiveRespDTO() - .setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus())) - .setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription()) - .setMobile(status.getMobile()).setReceiveTime(status.getReceiveTime()) - .setSerialNo(status.getSerialNo()).setLogId(status.getSessionContext().getLogId())); - } - - @Override - public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { - // 构建请求 - DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest(); - request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)}); - request.setInternational(INTERNATIONAL_CHINA); - // 执行请求 - DescribeSmsTemplateListResponse response = client.DescribeSmsTemplateList(request); - DescribeTemplateListStatus status = response.getDescribeTemplateStatusSet()[0]; - if (status == null || status.getStatusCode() == null) { - return null; - } - return new SmsTemplateRespDTO().setId(status.getTemplateId().toString()).setContent(status.getTemplateContent()) - .setAuditStatus(convertSmsTemplateAuditStatus(status.getStatusCode().intValue())).setAuditReason(status.getReviewReply()); - } - - @VisibleForTesting - Integer convertSmsTemplateAuditStatus(int templateStatus) { - switch (templateStatus) { - case 1: return SmsTemplateAuditStatusEnum.CHECKING.getStatus(); - case 0: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus(); - case -1: return SmsTemplateAuditStatusEnum.FAIL.getStatus(); - default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus)); - } - } - - @Data - private static class SmsReceiveStatus { - - /** - * 短信接受成功 code - */ - public static final String SUCCESS_CODE = "SUCCESS"; - - /** - * 用户实际接收到短信的时间 - */ - @JsonProperty("user_receive_time") - @JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT) - private LocalDateTime receiveTime; - - /** - * 国家(或地区)码 - */ - @JsonProperty("nationcode") - private String nationCode; - - /** - * 手机号码 - */ - private String mobile; - - /** - * 实际是否收到短信接收状态,SUCCESS(成功)、FAIL(失败) - */ - @JsonProperty("report_status") - private String status; - - /** - * 用户接收短信状态码错误信息 - */ - @JsonProperty("errmsg") - private String errCode; - - /** - * 用户接收短信状态描述 - */ - @JsonProperty("description") - private String description; - - /** - * 本次发送标识 ID(与发送接口返回的SerialNo对应) - */ - @JsonProperty("sid") - private String serialNo; - - /** - * 用户的 session 内容(与发送接口的请求参数 SessionContext 一致) - */ - @JsonProperty("ext") - private SessionContext sessionContext; - - } - - @VisibleForTesting - @Data - static class SessionContext { - - /** - * 发送短信记录id - */ - private Long logId; - - } - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/enums/SmsChannelEnum.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/enums/SmsChannelEnum.java index 17ea57c4c..58fbecd8f 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/enums/SmsChannelEnum.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/framework/sms/core/enums/SmsChannelEnum.java @@ -15,10 +15,7 @@ import lombok.Getter; public enum SmsChannelEnum { DEBUG_DING_TALK("DEBUG_DING_TALK", "调试(钉钉)"), - ALIYUN("ALIYUN", "阿里云"), - TENCENT("TENCENT", "腾讯云"), -// HUA_WEI("HUA_WEI", "华为云"), - ; + ALIYUN("ALIYUN", "阿里云"); /** * 编码 diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/message/sms/SmsSendMessage.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/message/sms/SmsSendMessage.java index 4252eda8b..502c9bc91 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/message/sms/SmsSendMessage.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/message/sms/SmsSendMessage.java @@ -1,6 +1,6 @@ package com.cf.imes.module.system.mq.message.sms; -import cn.hutool.core.lang.Pair; +import com.cf.imes.framework.common.core.KeyValue; import lombok.Data; import javax.validation.constraints.NotNull; @@ -37,7 +37,7 @@ public class SmsSendMessage { /** * 短信模板参数 */ - private List> templateParams; + private List> templateParams; /** * 短信消息模板类型:system_sms_template.type diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/producer/sms/SmsProducer.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/producer/sms/SmsProducer.java index 850d7aa05..88981e9a9 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/producer/sms/SmsProducer.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/mq/producer/sms/SmsProducer.java @@ -1,6 +1,6 @@ package com.cf.imes.module.system.mq.producer.sms; -import cn.hutool.core.lang.Pair; +import com.cf.imes.framework.common.core.KeyValue; import com.cf.imes.module.system.dal.dataobject.sms.SmsTemplateDO; import com.cf.imes.module.system.mq.message.sms.SmsSendMessage; import lombok.extern.slf4j.Slf4j; @@ -32,7 +32,7 @@ public class SmsProducer { * @param template 模板信息 * @param templateParams 短信模板参数 */ - public void sendSmsSendMessage(Long logId, String mobile, SmsTemplateDO template, List> templateParams) { + public void sendSmsSendMessage(Long logId, String mobile, SmsTemplateDO template, List> templateParams) { SmsSendMessage message = new SmsSendMessage().setLogId(logId).setMobile(mobile); message.setChannelId(template.getChannelId()).setApiTemplateId(template.getApiTemplateId()).setTemplateType(template.getType()).setTemplateParams(templateParams); // event异步发送短信,保证子线程内部request不为空 diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendServiceImpl.java index 0d22d8e89..8589eed59 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/SmsSendServiceImpl.java @@ -3,7 +3,7 @@ package com.cf.imes.module.system.service.sms; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.lang.Assert; -import cn.hutool.core.lang.Pair; +import com.cf.imes.framework.common.core.KeyValue; import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.enums.UserTypeEnum; import com.cf.imes.framework.common.exception.util.ServiceExceptionUtil; @@ -86,7 +86,7 @@ public class SmsSendServiceImpl implements SmsSendService { // 校验手机号码是否存在 mobile = validateMobile(mobile); // 构建有序的模板参数。为什么放在这个位置,是提前保证模板参数的正确性,而不是到了插入发送日志 - List> newTemplateParams = buildTemplateParams(template, templateParams); + List> newTemplateParams = buildTemplateParams(template, templateParams); // 创建发送日志。如果模板被禁用,则不发送短信,只记录日志 Boolean isSend = CommonStatusEnum.ENABLE.getStatus().equals(template.getStatus()); @@ -121,13 +121,13 @@ public class SmsSendServiceImpl implements SmsSendService { * @return 处理后的参数 */ @VisibleForTesting - List> buildTemplateParams(SmsTemplateDO template, Map templateParams) { + List> buildTemplateParams(SmsTemplateDO template, Map templateParams) { return template.getParams().stream().map(key -> { Object value = templateParams.get(key); if (value == null) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.SMS_SEND_MOBILE_TEMPLATE_PARAM_MISS, key); } - return new Pair<>(key, value); + return new KeyValue<>(key, value); }).collect(Collectors.toList()); } @@ -145,7 +145,7 @@ public class SmsSendServiceImpl implements SmsSendService { SmsClient smsClient = smsChannelService.getSmsClient(); Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId()); - List> templateParams = message.getTemplateParams(); + List> templateParams = message.getTemplateParams(); String channelCode = null; // 发送短信 try { diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/SmsSendAfterSendHandler.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/SmsSendAfterSendHandler.java index e6b836c88..04b9cccbf 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/SmsSendAfterSendHandler.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/SmsSendAfterSendHandler.java @@ -1,6 +1,6 @@ package com.cf.imes.module.system.service.sms.handler; -import cn.hutool.core.lang.Pair; +import com.cf.imes.framework.common.core.KeyValue; import com.cf.imes.module.system.framework.sms.core.client.dto.SmsSendRespDTO; import java.util.List; @@ -21,5 +21,5 @@ public interface SmsSendAfterSendHandler { boolean checkTemplateType(Integer templateType); - void afterSend(SmsSendRespDTO sendResponse, List> params); + void afterSend(SmsSendRespDTO sendResponse, List> params); } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/impl/SmsSendAfterSendHandlerImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/impl/SmsSendAfterSendHandlerImpl.java index 7f1da1cfd..f6df36aee 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/impl/SmsSendAfterSendHandlerImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/sms/handler/impl/SmsSendAfterSendHandlerImpl.java @@ -1,7 +1,7 @@ package com.cf.imes.module.system.service.sms.handler.impl; -import cn.hutool.core.lang.Pair; import cn.hutool.core.util.ObjectUtil; +import com.cf.imes.framework.common.core.KeyValue; import com.cf.imes.module.system.framework.sms.core.client.dto.SmsSendRespDTO; import com.cf.imes.module.system.dal.redis.RedisKeyConstants; import com.cf.imes.module.system.enums.sms.SmsTemplateTypeEnum; @@ -33,10 +33,10 @@ public class SmsSendAfterSendHandlerImpl implements SmsSendAfterSendHandler { } @Override - public void afterSend(SmsSendRespDTO sendResponse, List> params) { + public void afterSend(SmsSendRespDTO sendResponse, List> params) { // 发送成功把验证码存入redis if (sendResponse.getSuccess()) { - for (Pair keyValue : params) { + for (KeyValue keyValue : params) { if ("code".equals(keyValue.getKey())) { redisTemplate.opsForValue().set(String.format(RedisKeyConstants.SMS_CAPTCHA_VERIFICATION, sendResponse.getMobile()), keyValue.getValue(), smsCodeProperties.getExpireTimes()); } diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsSendServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsSendServiceImplTest.java index 607f4c5c3..ed2094052 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsSendServiceImplTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/sms/SmsSendServiceImplTest.java @@ -1,7 +1,7 @@ package com.cf.imes.module.system.service.sms; -import cn.hutool.core.lang.Pair; import cn.hutool.core.map.MapUtil; +import com.cf.imes.framework.common.core.KeyValue; import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.enums.UserTypeEnum; import com.cf.imes.module.system.framework.sms.core.client.SmsClient; @@ -87,7 +87,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest { assertEquals(smsLogId, resultSmsLogId); // 断言调用 verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(user.getMobile()), eq(template), - eq(Lists.newArrayList(new Pair<>("code", "1234"), new Pair<>("op", "login")))); + eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); } @Test @@ -125,7 +125,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest { assertEquals(smsLogId, resultSmsLogId); // 断言调用 verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(mobile), eq(template), - eq(Lists.newArrayList(new Pair<>("code", "1234"), new Pair<>("op", "login")))); + eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); } /** @@ -164,7 +164,7 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest { assertEquals(smsLogId, resultSmsLogId); // 断言调用 verify(smsProducer).sendSmsSendMessage(eq(smsLogId), eq(mobile), eq(template), - eq(Lists.newArrayList(new Pair<>("code", "1234"), new Pair<>("op", "login")))); + eq(Lists.newArrayList(new KeyValue<>("code", "1234"), new KeyValue<>("op", "login")))); } /**