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 1 package cn.iocoder.yudao.framework.common.util.collection;
2 2  
  3 +import cn.hutool.core.collection.CollectionUtil;
3 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 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 19 * Array 工具类
... ... @@ -30,4 +41,16 @@ public class ArrayUtils {
30 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 4 import cn.hutool.core.collection.CollectionUtil;
5 5 import com.google.common.collect.ImmutableMap;
6 6  
7   -import java.lang.reflect.Array;
8 7 import java.util.*;
9 8 import java.util.function.BinaryOperator;
10 9 import java.util.function.Function;
... ... @@ -171,21 +170,4 @@ public class CollectionUtils {
171 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 4 import cn.hutool.core.lang.Assert;
5 5 import cn.hutool.core.util.StrUtil;
6 6 import cn.iocoder.yudao.framework.common.core.KeyValue;
  7 +import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
7 8 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 9 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
9 10 import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
... ... @@ -22,10 +23,8 @@ import com.tencentcloudapi.sms.v20210111.SmsClient;
22 23 import com.tencentcloudapi.sms.v20210111.models.*;
23 24 import lombok.Data;
24 25  
25   -import java.util.Collections;
26 26 import java.util.Date;
27 27 import java.util.List;
28   -import java.util.Optional;
29 28 import java.util.function.Function;
30 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 36 * <p>
38 37 * 参见 https://cloud.tencent.com/document/product/382/52077
39 38 *
40   - * @author : shiwp
  39 + * @author shiwp
41 40 */
42   -// TODO @author 后面,空格即可
43 41 public class TencentSmsClient extends AbstractSmsClient {
44 42  
45 43 private SmsClient client;
46 44  
47 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 48 super(TencentSmsChannelProperties.build(properties), new TencentSmsCodeMapping());
52 49 Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
53 50 }
54 51  
55 52 @Override
56 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 57 Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret());
63 58 client = new SmsClient(credential, "ap-nanjing");
64 59 }
... ... @@ -68,8 +63,7 @@ public class TencentSmsClient extends AbstractSmsClient {
68 63 String mobile,
69 64 String apiTemplateId,
70 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 67 this::doSendSms0,
74 68 response -> {
75 69 SendStatus sendStatus = response.getSendStatusSet()[0];
... ... @@ -79,11 +73,11 @@ public class TencentSmsClient extends AbstractSmsClient {
79 73 }
80 74  
81 75 /**
82   - * 调用腾讯云SDK发送短信
  76 + * 调用腾讯云 SDK 发送短信
83 77 *
84 78 * @param request 发送短信请求
85 79 * @return 发送短信响应
86   - * @throws TencentCloudSDKException SDK用来封装发送短信失败
  80 + * @throws TencentCloudSDKException SDK 用来封装发送短信失败
87 81 */
88 82 private SendSmsResponse doSendSms0(SendSmsRequest request) throws TencentCloudSDKException {
89 83 return client.SendSms(request);
... ... @@ -104,10 +98,10 @@ public class TencentSmsClient extends AbstractSmsClient {
104 98 List<KeyValue<String, Object>> templateParams) {
105 99 SendSmsRequest request = new SendSmsRequest();
106 100 request.setSmsSdkAppId(((TencentSmsChannelProperties) properties).getSdkAppId());
107   - request.setPhoneNumberSet(CollectionUtils.toArray(Collections.singletonList(mobile)));
  101 + request.setPhoneNumberSet(new String[]{mobile});
108 102 request.setSignName(properties.getSignature());
109 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 105 request.setSessionContext(JsonUtils.toJsonString(new SessionContext().setLogId(sendLogId)));
112 106 return request;
113 107 }
... ... @@ -120,11 +114,11 @@ public class TencentSmsClient extends AbstractSmsClient {
120 114 data.setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription());
121 115 data.setReceiveTime(status.getReceiveTime()).setSuccess("SUCCESS".equalsIgnoreCase(status.getStatus()));
122 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 122 return data;
129 123 });
130 124 }
... ... @@ -146,7 +140,7 @@ public class TencentSmsClient extends AbstractSmsClient {
146 140 }
147 141 SmsTemplateAuditStatusEnum auditStatus;
148 142 Assert.notNull(templateStatus.getStatusCode(),
149   - StrUtil.format("短信模版审核状态为null,模版id{}", templateStatus.getTemplateId()));
  143 + StrUtil.format("短信模版审核状态为 null,模版 id{}", templateStatus.getTemplateId()));
150 144 switch (templateStatus.getStatusCode().intValue()) {
151 145 case -1:
152 146 auditStatus = SmsTemplateAuditStatusEnum.FAIL;
... ... @@ -158,7 +152,7 @@ public class TencentSmsClient extends AbstractSmsClient {
158 152 auditStatus = SmsTemplateAuditStatusEnum.CHECKING;
159 153 break;
160 154 default:
161   - throw new IllegalStateException(StrUtil.format("不能解析短信模版审核状态{},模版id{}",
  155 + throw new IllegalStateException(StrUtil.format("不能解析短信模版审核状态{},模版 id{}",
162 156 templateStatus.getStatusCode(), templateStatus.getTemplateId()));
163 157 }
164 158 SmsTemplateRespDTO data = new SmsTemplateRespDTO();
... ... @@ -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 167 * @return 查询模版审核状态请求
174 168 */
175 169 private DescribeSmsTemplateListRequest buildSmsTemplateStatusRequest(String apiTemplateId) {
176 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 174 return request;
182 175 }
183 176  
184 177 /**
185   - * 调用腾讯云SDK查询短信模版状态
  178 + * 调用腾讯云 SDK 查询短信模版状态
186 179 *
187 180 * @param request 查询短信模版状态请求
188 181 * @return 查询短信模版状态响应
189   - * @throws TencentCloudSDKException SDK用来封装查询短信模版状态失败
  182 + * @throws TencentCloudSDKException SDK 用来封装查询短信模版状态失败
190 183 */
191 184 private DescribeSmsTemplateListResponse doGetSmsTemplate0(DescribeSmsTemplateListRequest request) throws TencentCloudSDKException {
192 185 return client.DescribeSmsTemplateList(request);
... ... @@ -285,13 +278,12 @@ public class TencentSmsClient extends AbstractSmsClient {
285 278 return (TencentSmsChannelProperties) properties;
286 279 }
287 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 287 result.setSdkAppId(keys[1]).setApiKey(keys[0]);
296 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 4 import cn.hutool.core.util.StrUtil;
5 5 import cn.iocoder.yudao.framework.common.core.KeyValue;
6 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 8 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
9 9 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
10 10 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
... ... @@ -89,7 +89,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
89 89 assertEquals(mobile, request.getPhoneNumberSet()[0]);
90 90 assertEquals(properties.getSignature(), request.getSignName());
91 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 93 assertEquals(sendLogId, ReflectUtil.getFieldValue(JsonUtils.parseObject(request.getSessionContext(), TencentSmsClient.SessionContext.class), "logId"));
94 94 return true;
95 95 }))).thenReturn(response);
... ...