Commit 91f758c2952997b7dddd4449d5350c0851b68fab

Authored by FinallySays
1 parent 97265603

根据 code review 对腾讯云短信实现优化

yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/ArrayUtils.java
1 package cn.iocoder.yudao.framework.common.util.collection; 1 package cn.iocoder.yudao.framework.common.util.collection;
2 2
  3 +import cn.hutool.core.collection.CollectionUtil;
3 import cn.hutool.core.util.ArrayUtil; 4 import cn.hutool.core.util.ArrayUtil;
  5 +import cn.hutool.core.util.TypeUtil;
  6 +import org.springframework.cglib.core.TypeUtils;
4 7
  8 +import java.lang.reflect.Array;
  9 +import java.lang.reflect.ParameterizedType;
  10 +import java.lang.reflect.Type;
  11 +import java.util.Collection;
  12 +import java.util.List;
5 import java.util.function.Consumer; 13 import java.util.function.Consumer;
  14 +import java.util.function.Function;
  15 +
  16 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
6 17
7 /** 18 /**
8 * Array 工具类 19 * Array 工具类
@@ -30,4 +41,16 @@ public class ArrayUtils { @@ -30,4 +41,16 @@ public class ArrayUtils {
30 return result; 41 return result;
31 } 42 }
32 43
  44 + public static <T, V> V[] toArray(Collection<T> from, Function<T, V> mapper) {
  45 + return toArray(convertList(from, mapper));
  46 + }
  47 +
  48 + @SuppressWarnings("unchecked")
  49 + public static <T> T[] toArray(Collection<T> from) {
  50 + if (CollectionUtil.isEmpty(from)) {
  51 + return (T[]) (new Object[0]);
  52 + }
  53 + return ArrayUtil.toArray(from, (Class<T>) CollectionUtil.getElementType(from.iterator()));
  54 + }
  55 +
33 } 56 }
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil; @@ -4,7 +4,6 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.CollectionUtil; 4 import cn.hutool.core.collection.CollectionUtil;
5 import com.google.common.collect.ImmutableMap; 5 import com.google.common.collect.ImmutableMap;
6 6
7 -import java.lang.reflect.Array;  
8 import java.util.*; 7 import java.util.*;
9 import java.util.function.BinaryOperator; 8 import java.util.function.BinaryOperator;
10 import java.util.function.Function; 9 import java.util.function.Function;
@@ -171,21 +170,4 @@ public class CollectionUtils { @@ -171,21 +170,4 @@ public class CollectionUtils {
171 return deptId == null ? Collections.emptyList() : Collections.singleton(deptId); 170 return deptId == null ? Collections.emptyList() : Collections.singleton(deptId);
172 } 171 }
173 172
174 - // TODO @FinallySays:建议放在 ArrayUtils 里,和 hutool 对齐  
175 - public static <T, V> V[] toArray(List<T> from, Function<T, V> mapper) {  
176 - return toArray(convertList(from, mapper));  
177 - }  
178 -  
179 - @SuppressWarnings("unchecked")  
180 - public static <T> T[] toArray(List<T> from) {  
181 - if (CollectionUtil.isEmpty(from)) {  
182 - return (T[]) (new Object[0]);  
183 - }  
184 - Class<T> clazz = (Class<T>) from.get(0).getClass();  
185 - T[] result = (T[]) Array.newInstance(clazz, from.size());  
186 - for (int i = 0; i < from.size(); i++) {  
187 - result[i] = from.get(i);  
188 - }  
189 - return result;  
190 - }  
191 } 173 }
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java
@@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil; @@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.util.StrUtil; 5 import cn.hutool.core.util.StrUtil;
6 import cn.iocoder.yudao.framework.common.core.KeyValue; 6 import cn.iocoder.yudao.framework.common.core.KeyValue;
  7 +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
7 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 9 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
9 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; 10 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
@@ -22,10 +23,8 @@ import com.tencentcloudapi.sms.v20210111.SmsClient; @@ -22,10 +23,8 @@ import com.tencentcloudapi.sms.v20210111.SmsClient;
22 import com.tencentcloudapi.sms.v20210111.models.*; 23 import com.tencentcloudapi.sms.v20210111.models.*;
23 import lombok.Data; 24 import lombok.Data;
24 25
25 -import java.util.Collections;  
26 import java.util.Date; 26 import java.util.Date;
27 import java.util.List; 27 import java.util.List;
28 -import java.util.Optional;  
29 import java.util.function.Function; 28 import java.util.function.Function;
30 import java.util.function.Supplier; 29 import java.util.function.Supplier;
31 30
@@ -37,28 +36,24 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DE @@ -37,28 +36,24 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DE
37 * <p> 36 * <p>
38 * 参见 https://cloud.tencent.com/document/product/382/52077 37 * 参见 https://cloud.tencent.com/document/product/382/52077
39 * 38 *
40 - * @author : shiwp 39 + * @author shiwp
41 */ 40 */
42 -// TODO @author 后面,空格即可  
43 public class TencentSmsClient extends AbstractSmsClient { 41 public class TencentSmsClient extends AbstractSmsClient {
44 42
45 private SmsClient client; 43 private SmsClient client;
46 44
47 public TencentSmsClient(SmsChannelProperties properties) { 45 public TencentSmsClient(SmsChannelProperties properties) {
48 - // TODO @FinallySays:注释的时候,中英文之间要空格哈  
49 - // 腾讯云发放短信的时候需要额外的参数sdkAppId, 所以和secretId组合在一起放到apiKey字段中,格式为[secretId sdkAppId],  
50 - // 这边需要做拆分重新封装到properties内 46 + // 腾讯云发放短信的时候需要额外的参数 sdkAppId, 所以和 secretId 组合在一起放到 apiKey 字段中,格式为[secretId sdkAppId],
  47 + // 这边需要做拆分重新封装到 properties 内
51 super(TencentSmsChannelProperties.build(properties), new TencentSmsCodeMapping()); 48 super(TencentSmsChannelProperties.build(properties), new TencentSmsCodeMapping());
52 Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); 49 Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
53 } 50 }
54 51
55 @Override 52 @Override
56 protected void doInit() { 53 protected void doInit() {
57 - // init或者refresh时需要重新封装properties  
58 - // TODO @FinallySays:是不是不用这个变量呀?p  
59 - final SmsChannelProperties p = properties;  
60 - properties = TencentSmsChannelProperties.build(p);  
61 - // 实例化一个认证对象,入参需要传入腾讯云账户密钥对secretId,secretKey。 54 + // init 或者 refresh 时需要重新封装 properties
  55 + properties = TencentSmsChannelProperties.build(properties);
  56 + // 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId,secretKey。
62 Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret()); 57 Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret());
63 client = new SmsClient(credential, "ap-nanjing"); 58 client = new SmsClient(credential, "ap-nanjing");
64 } 59 }
@@ -68,8 +63,7 @@ public class TencentSmsClient extends AbstractSmsClient { @@ -68,8 +63,7 @@ public class TencentSmsClient extends AbstractSmsClient {
68 String mobile, 63 String mobile,
69 String apiTemplateId, 64 String apiTemplateId,
70 List<KeyValue<String, Object>> templateParams) throws Throwable { 65 List<KeyValue<String, Object>> templateParams) throws Throwable {
71 -  
72 - return invoke(() -> buildSendSmsRequest(sendLogId, mobile, apiTemplateId, templateParams), // TODO @FinallySays:上面不用空行 66 + return invoke(() -> buildSendSmsRequest(sendLogId, mobile, apiTemplateId, templateParams),
73 this::doSendSms0, 67 this::doSendSms0,
74 response -> { 68 response -> {
75 SendStatus sendStatus = response.getSendStatusSet()[0]; 69 SendStatus sendStatus = response.getSendStatusSet()[0];
@@ -79,11 +73,11 @@ public class TencentSmsClient extends AbstractSmsClient { @@ -79,11 +73,11 @@ public class TencentSmsClient extends AbstractSmsClient {
79 } 73 }
80 74
81 /** 75 /**
82 - * 调用腾讯云SDK发送短信 76 + * 调用腾讯云 SDK 发送短信
83 * 77 *
84 * @param request 发送短信请求 78 * @param request 发送短信请求
85 * @return 发送短信响应 79 * @return 发送短信响应
86 - * @throws TencentCloudSDKException SDK用来封装发送短信失败 80 + * @throws TencentCloudSDKException SDK 用来封装发送短信失败
87 */ 81 */
88 private SendSmsResponse doSendSms0(SendSmsRequest request) throws TencentCloudSDKException { 82 private SendSmsResponse doSendSms0(SendSmsRequest request) throws TencentCloudSDKException {
89 return client.SendSms(request); 83 return client.SendSms(request);
@@ -104,10 +98,10 @@ public class TencentSmsClient extends AbstractSmsClient { @@ -104,10 +98,10 @@ public class TencentSmsClient extends AbstractSmsClient {
104 List<KeyValue<String, Object>> templateParams) { 98 List<KeyValue<String, Object>> templateParams) {
105 SendSmsRequest request = new SendSmsRequest(); 99 SendSmsRequest request = new SendSmsRequest();
106 request.setSmsSdkAppId(((TencentSmsChannelProperties) properties).getSdkAppId()); 100 request.setSmsSdkAppId(((TencentSmsChannelProperties) properties).getSdkAppId());
107 - request.setPhoneNumberSet(CollectionUtils.toArray(Collections.singletonList(mobile))); 101 + request.setPhoneNumberSet(new String[]{mobile});
108 request.setSignName(properties.getSignature()); 102 request.setSignName(properties.getSignature());
109 request.setTemplateId(apiTemplateId); 103 request.setTemplateId(apiTemplateId);
110 - request.setTemplateParamSet(CollectionUtils.toArray(templateParams, e -> String.valueOf(e.getValue()))); 104 + request.setTemplateParamSet(ArrayUtils.toArray(templateParams, e -> String.valueOf(e.getValue())));
111 request.setSessionContext(JsonUtils.toJsonString(new SessionContext().setLogId(sendLogId))); 105 request.setSessionContext(JsonUtils.toJsonString(new SessionContext().setLogId(sendLogId)));
112 return request; 106 return request;
113 } 107 }
@@ -120,11 +114,11 @@ public class TencentSmsClient extends AbstractSmsClient { @@ -120,11 +114,11 @@ public class TencentSmsClient extends AbstractSmsClient {
120 data.setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription()); 114 data.setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription());
121 data.setReceiveTime(status.getReceiveTime()).setSuccess("SUCCESS".equalsIgnoreCase(status.getStatus())); 115 data.setReceiveTime(status.getReceiveTime()).setSuccess("SUCCESS".equalsIgnoreCase(status.getStatus()));
122 data.setMobile(status.getMobile()).setSerialNo(status.getSerialNo()); 116 data.setMobile(status.getMobile()).setSerialNo(status.getSerialNo());
123 - // TODO @FinallySays:建议直接判断是否为空,酱紫更易读一些  
124 - Optional.ofNullable(status.getSessionContext()).map(SessionContext::getLogId)  
125 - .ifPresentOrElse(data::setLogId, () -> {  
126 - throw new IllegalStateException(StrUtil.format("未回传logId,需联系腾讯云解决。"));  
127 - }); 117 + SessionContext context;
  118 + Long logId;
  119 + Assert.notNull(context = status.getSessionContext(), "回执信息中未解析出 context,请联系腾讯云小助手");
  120 + Assert.notNull(logId = context.getLogId(), "回执信息中未解析出 logId,请联系腾讯云小助手");
  121 + data.setLogId(logId);
128 return data; 122 return data;
129 }); 123 });
130 } 124 }
@@ -146,7 +140,7 @@ public class TencentSmsClient extends AbstractSmsClient { @@ -146,7 +140,7 @@ public class TencentSmsClient extends AbstractSmsClient {
146 } 140 }
147 SmsTemplateAuditStatusEnum auditStatus; 141 SmsTemplateAuditStatusEnum auditStatus;
148 Assert.notNull(templateStatus.getStatusCode(), 142 Assert.notNull(templateStatus.getStatusCode(),
149 - StrUtil.format("短信模版审核状态为null,模版id{}", templateStatus.getTemplateId())); 143 + StrUtil.format("短信模版审核状态为 null,模版 id{}", templateStatus.getTemplateId()));
150 switch (templateStatus.getStatusCode().intValue()) { 144 switch (templateStatus.getStatusCode().intValue()) {
151 case -1: 145 case -1:
152 auditStatus = SmsTemplateAuditStatusEnum.FAIL; 146 auditStatus = SmsTemplateAuditStatusEnum.FAIL;
@@ -158,7 +152,7 @@ public class TencentSmsClient extends AbstractSmsClient { @@ -158,7 +152,7 @@ public class TencentSmsClient extends AbstractSmsClient {
158 auditStatus = SmsTemplateAuditStatusEnum.CHECKING; 152 auditStatus = SmsTemplateAuditStatusEnum.CHECKING;
159 break; 153 break;
160 default: 154 default:
161 - throw new IllegalStateException(StrUtil.format("不能解析短信模版审核状态{},模版id{}", 155 + throw new IllegalStateException(StrUtil.format("不能解析短信模版审核状态{},模版 id{}",
162 templateStatus.getStatusCode(), templateStatus.getTemplateId())); 156 templateStatus.getStatusCode(), templateStatus.getTemplateId()));
163 } 157 }
164 SmsTemplateRespDTO data = new SmsTemplateRespDTO(); 158 SmsTemplateRespDTO data = new SmsTemplateRespDTO();
@@ -169,24 +163,23 @@ public class TencentSmsClient extends AbstractSmsClient { @@ -169,24 +163,23 @@ public class TencentSmsClient extends AbstractSmsClient {
169 163
170 /** 164 /**
171 * 封装查询模版审核状态请求 165 * 封装查询模版审核状态请求
172 - * @param apiTemplateId api的模版id 166 + * @param apiTemplateId api 的模版 id
173 * @return 查询模版审核状态请求 167 * @return 查询模版审核状态请求
174 */ 168 */
175 private DescribeSmsTemplateListRequest buildSmsTemplateStatusRequest(String apiTemplateId) { 169 private DescribeSmsTemplateListRequest buildSmsTemplateStatusRequest(String apiTemplateId) {
176 DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest(); 170 DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest();
177 - // TODO TODO @FinallySays: new Long[]{Long.parseLong(apiTemplateId)} 就 ok 啦  
178 - request.setTemplateIdSet(CollectionUtils.toArray(Collections.singletonList(apiTemplateId), Long::parseLong));  
179 - // 地区  
180 - request.setInternational(0L); // TODO @FinallySays:0L 最好说明下哈; 171 + request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)});
  172 + // 地区 0:表示国内短信。1:表示国际/港澳台短信。
  173 + request.setInternational(0L);
181 return request; 174 return request;
182 } 175 }
183 176
184 /** 177 /**
185 - * 调用腾讯云SDK查询短信模版状态 178 + * 调用腾讯云 SDK 查询短信模版状态
186 * 179 *
187 * @param request 查询短信模版状态请求 180 * @param request 查询短信模版状态请求
188 * @return 查询短信模版状态响应 181 * @return 查询短信模版状态响应
189 - * @throws TencentCloudSDKException SDK用来封装查询短信模版状态失败 182 + * @throws TencentCloudSDKException SDK 用来封装查询短信模版状态失败
190 */ 183 */
191 private DescribeSmsTemplateListResponse doGetSmsTemplate0(DescribeSmsTemplateListRequest request) throws TencentCloudSDKException { 184 private DescribeSmsTemplateListResponse doGetSmsTemplate0(DescribeSmsTemplateListRequest request) throws TencentCloudSDKException {
192 return client.DescribeSmsTemplateList(request); 185 return client.DescribeSmsTemplateList(request);
@@ -285,13 +278,12 @@ public class TencentSmsClient extends AbstractSmsClient { @@ -285,13 +278,12 @@ public class TencentSmsClient extends AbstractSmsClient {
285 return (TencentSmsChannelProperties) properties; 278 return (TencentSmsChannelProperties) properties;
286 } 279 }
287 TencentSmsChannelProperties result = BeanUtil.toBean(properties, TencentSmsChannelProperties.class); 280 TencentSmsChannelProperties result = BeanUtil.toBean(properties, TencentSmsChannelProperties.class);
288 - // TODO @FinallySays:comb=》combine,不缩写好点哈  
289 - String combKey = properties.getApiKey();  
290 - Assert.notEmpty(combKey, "apiKey 不能为空");  
291 - String[] keys = combKey.trim().split(" ");  
292 - // TODO @FinallySays:建议写多个断言好点,嘿嘿。然后 Assert 支持占位符  
293 - Assert.isTrue(keys.length == 2 && StrUtil.isNotBlank(keys[0]) && StrUtil.isNotBlank(keys[1]),  
294 - "腾讯云短信api配置格式错误,请配置为[secretId sdkAppId]"); 281 + String combineKey = properties.getApiKey();
  282 + Assert.notEmpty(combineKey, "apiKey 不能为空");
  283 + String[] keys = combineKey.trim().split(" ");
  284 + Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置为[secretId sdkAppId]");
  285 + Assert.notBlank(keys[0], "腾讯云短信 secretId 不能为空");
  286 + Assert.notBlank(keys[1], "腾讯云短信 sdkAppId 不能为空");
295 result.setSdkAppId(keys[1]).setApiKey(keys[0]); 287 result.setSdkAppId(keys[1]).setApiKey(keys[0]);
296 return result; 288 return result;
297 } 289 }
yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClientTest.java
@@ -4,7 +4,7 @@ import cn.hutool.core.util.ReflectUtil; @@ -4,7 +4,7 @@ import cn.hutool.core.util.ReflectUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.yudao.framework.common.core.KeyValue; 5 import cn.iocoder.yudao.framework.common.core.KeyValue;
6 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; 6 import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
7 -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 7 +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
8 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 8 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
9 import cn.iocoder.yudao.framework.common.util.date.DateUtils; 9 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
10 import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 10 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
@@ -89,7 +89,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest { @@ -89,7 +89,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
89 assertEquals(mobile, request.getPhoneNumberSet()[0]); 89 assertEquals(mobile, request.getPhoneNumberSet()[0]);
90 assertEquals(properties.getSignature(), request.getSignName()); 90 assertEquals(properties.getSignature(), request.getSignName());
91 assertEquals(apiTemplateId, request.getTemplateId()); 91 assertEquals(apiTemplateId, request.getTemplateId());
92 - assertEquals(toJsonString(CollectionUtils.toArray(new ArrayList<>(MapUtils.convertMap(templateParams).values()), String::valueOf)), toJsonString(request.getTemplateParamSet())); 92 + assertEquals(toJsonString(ArrayUtils.toArray(new ArrayList<>(MapUtils.convertMap(templateParams).values()), String::valueOf)), toJsonString(request.getTemplateParamSet()));
93 assertEquals(sendLogId, ReflectUtil.getFieldValue(JsonUtils.parseObject(request.getSessionContext(), TencentSmsClient.SessionContext.class), "logId")); 93 assertEquals(sendLogId, ReflectUtil.getFieldValue(JsonUtils.parseObject(request.getSessionContext(), TencentSmsClient.SessionContext.class), "logId"));
94 return true; 94 return true;
95 }))).thenReturn(response); 95 }))).thenReturn(response);