Commit 91f758c2952997b7dddd4449d5350c0851b68fab
1 parent
97265603
根据 code review 对腾讯云短信实现优化
Showing
4 changed files
with
56 additions
and
59 deletions
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); | ... | ... |