mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-23 07:41:53 +08:00
magic 参数处理&AbstractSmsClient模版优化
This commit is contained in:
parent
2e66845584
commit
eb147a92ff
@ -31,7 +31,7 @@ public abstract class AbstractSmsClient implements SmsClient {
|
||||
protected final SmsCodeMapping codeMapping;
|
||||
|
||||
public AbstractSmsClient(SmsChannelProperties properties, SmsCodeMapping codeMapping) {
|
||||
this.properties = properties;
|
||||
this.properties = prepareProperties(properties);
|
||||
this.codeMapping = codeMapping;
|
||||
}
|
||||
|
||||
@ -54,11 +54,21 @@ public abstract class AbstractSmsClient implements SmsClient {
|
||||
return;
|
||||
}
|
||||
log.info("[refresh][配置({})发生变化,重新初始化]", properties);
|
||||
this.properties = properties;
|
||||
this.properties = prepareProperties(properties);
|
||||
// 初始化
|
||||
this.init();
|
||||
}
|
||||
|
||||
/**
|
||||
* 在赋值给{@link this#properties}前,子类可根据需要预处理短信渠道配置
|
||||
*
|
||||
* @param properties 数据库中存储的短信渠道配置
|
||||
* @return 满足子类实现的短信渠道配置
|
||||
*/
|
||||
protected SmsChannelProperties prepareProperties(SmsChannelProperties properties) {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getId() {
|
||||
return properties.getId();
|
||||
|
@ -14,6 +14,7 @@ import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
|
||||
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
|
||||
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
|
||||
import cn.iocoder.yudao.framework.sms.core.property.TencentSmsChannelProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
@ -42,21 +43,33 @@ public class TencentSmsClient extends AbstractSmsClient {
|
||||
|
||||
private SmsClient client;
|
||||
|
||||
/**
|
||||
* 调用成功 code
|
||||
*/
|
||||
public static final String API_SUCCESS_CODE = "Ok";
|
||||
|
||||
/**
|
||||
* REGION, 使用南京
|
||||
*/
|
||||
private static final String ENDPOINT = "ap-nanjing";
|
||||
|
||||
/**
|
||||
* 是否国际/港澳台短信:
|
||||
* 0:表示国内短信。
|
||||
* 1:表示国际/港澳台短信。
|
||||
*/
|
||||
private static final long INTERNATIONAL = 0L;
|
||||
|
||||
public TencentSmsClient(SmsChannelProperties properties) {
|
||||
// 腾讯云发放短信的时候,需要额外的参数 sdkAppId。考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
|
||||
// 因此,这边需要使用 TencentSmsChannelProperties 做拆分,重新封装到 properties 内。
|
||||
super(TencentSmsChannelProperties.build(properties), new TencentSmsCodeMapping());
|
||||
super(properties, new TencentSmsCodeMapping());
|
||||
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doInit() {
|
||||
// init 或者 refresh 时,需要重新封装 properties
|
||||
properties = TencentSmsChannelProperties.build(properties);
|
||||
// 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId,secretKey
|
||||
Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret());
|
||||
// TODO @FinallySays:那把 ap-nanjing 枚举下到这个类的静态变量里哈。
|
||||
client = new SmsClient(credential, "ap-nanjing");
|
||||
client = new SmsClient(credential, ENDPOINT);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -73,6 +86,20 @@ public class TencentSmsClient extends AbstractSmsClient {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 腾讯云发放短信的时候,需要额外的参数 sdkAppId。
|
||||
* 考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
|
||||
* 因此,这边需要使用 TencentSmsChannelProperties 做拆分,重新封装到 properties 内。
|
||||
*
|
||||
* @param properties 数据库中存储的短信渠道配置
|
||||
* @return TencentSmsChannelProperties
|
||||
*/
|
||||
@Override
|
||||
protected SmsChannelProperties prepareProperties(SmsChannelProperties properties) {
|
||||
return TencentSmsChannelProperties.build(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用腾讯云 SDK 发送短信
|
||||
*
|
||||
@ -113,7 +140,7 @@ public class TencentSmsClient extends AbstractSmsClient {
|
||||
return CollectionUtils.convertList(callback, status -> {
|
||||
SmsReceiveRespDTO data = new SmsReceiveRespDTO();
|
||||
data.setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription());
|
||||
data.setReceiveTime(status.getReceiveTime()).setSuccess("SUCCESS".equalsIgnoreCase(status.getStatus()));
|
||||
data.setReceiveTime(status.getReceiveTime()).setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus()));
|
||||
data.setMobile(status.getMobile()).setSerialNo(status.getSerialNo());
|
||||
SessionContext context;
|
||||
Long logId;
|
||||
@ -130,7 +157,7 @@ public class TencentSmsClient extends AbstractSmsClient {
|
||||
this::doGetSmsTemplate0,
|
||||
response -> {
|
||||
SmsTemplateRespDTO data = convertTemplateStatusDTO(response.getDescribeTemplateStatusSet()[0]);
|
||||
return SmsCommonResult.build("Ok", null, response.getRequestId(), data, codeMapping);
|
||||
return SmsCommonResult.build(API_SUCCESS_CODE, null, response.getRequestId(), data, codeMapping);
|
||||
});
|
||||
}
|
||||
|
||||
@ -171,8 +198,7 @@ public class TencentSmsClient extends AbstractSmsClient {
|
||||
DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest();
|
||||
request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)});
|
||||
// 地区 0:表示国内短信。1:表示国际/港澳台短信。
|
||||
// TODO @FinallySays:那把 0L 枚举下到这个类的静态变量里哈。
|
||||
request.setInternational(0L);
|
||||
request.setInternational(INTERNATIONAL);
|
||||
return request;
|
||||
}
|
||||
|
||||
@ -206,6 +232,11 @@ public class TencentSmsClient extends AbstractSmsClient {
|
||||
@Data
|
||||
private static class SmsReceiveStatus {
|
||||
|
||||
/**
|
||||
* 短信接受成功 code
|
||||
*/
|
||||
public static final String SUCCESS_CODE = "SUCCESS";
|
||||
|
||||
/**
|
||||
* 用户实际接收到短信的时间
|
||||
*/
|
||||
@ -270,27 +301,4 @@ public class TencentSmsClient extends AbstractSmsClient {
|
||||
R apply(T t) throws TencentCloudSDKException;
|
||||
}
|
||||
|
||||
// TODO @FinallySays:要不单独一个类,不用作为内部类哈。这样,可能一看就知道,哟,腾讯短信是特殊的
|
||||
@Data
|
||||
private static class TencentSmsChannelProperties extends SmsChannelProperties {
|
||||
|
||||
private String sdkAppId;
|
||||
|
||||
public static TencentSmsChannelProperties build(SmsChannelProperties properties) {
|
||||
if (properties instanceof TencentSmsChannelProperties) {
|
||||
return (TencentSmsChannelProperties) properties;
|
||||
}
|
||||
TencentSmsChannelProperties result = BeanUtil.toBean(properties, TencentSmsChannelProperties.class);
|
||||
String combineKey = properties.getApiKey();
|
||||
Assert.notEmpty(combineKey, "apiKey 不能为空");
|
||||
String[] keys = combineKey.trim().split(" ");
|
||||
Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]");
|
||||
Assert.notBlank(keys[0], "腾讯云短信 secretId 不能为空");
|
||||
Assert.notBlank(keys[1], "腾讯云短信 sdkAppId 不能为空");
|
||||
result.setSdkAppId(keys[1]).setApiKey(keys[0]);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ public class TencentSmsCodeMapping implements SmsCodeMapping {
|
||||
@Override
|
||||
public ErrorCode apply(String apiCode) {
|
||||
switch (apiCode) {
|
||||
case "Ok": return GlobalErrorCodeConstants.SUCCESS;
|
||||
case TencentSmsClient.API_SUCCESS_CODE: return GlobalErrorCodeConstants.SUCCESS;
|
||||
case "FailedOperation.ContainSensitiveWord": return SMS_SEND_CONTENT_INVALID;
|
||||
case "FailedOperation.JsonParseFail":
|
||||
case "MissingParameter.EmptyPhoneNumberSet":
|
||||
|
@ -0,0 +1,41 @@
|
||||
package cn.iocoder.yudao.framework.sms.core.property;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 腾讯云短信配置实现类
|
||||
* 腾讯云发送短信时,需要额外的参数 sdkAppId,
|
||||
*
|
||||
* @author shiwp
|
||||
*/
|
||||
@Data
|
||||
|
||||
public class TencentSmsChannelProperties extends SmsChannelProperties {
|
||||
|
||||
/**
|
||||
* 应用 id
|
||||
*/
|
||||
private String sdkAppId;
|
||||
|
||||
/**
|
||||
* 考虑到不破坏原有的 apiKey + apiSecret 的结构,
|
||||
* 所以腾讯云短信存储时,将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
|
||||
* 因此在使用时,需要将 secretId 和 sdkAppId 解析出来,分别存储到对应字段中。
|
||||
*/
|
||||
public static TencentSmsChannelProperties build(SmsChannelProperties properties) {
|
||||
if (properties instanceof TencentSmsChannelProperties) {
|
||||
return (TencentSmsChannelProperties) properties;
|
||||
}
|
||||
TencentSmsChannelProperties result = BeanUtil.toBean(properties, TencentSmsChannelProperties.class);
|
||||
String combineKey = properties.getApiKey();
|
||||
Assert.notEmpty(combineKey, "apiKey 不能为空");
|
||||
String[] keys = combineKey.trim().split(" ");
|
||||
Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]");
|
||||
Assert.notBlank(keys[0], "腾讯云短信 secretId 不能为空");
|
||||
Assert.notBlank(keys[1], "腾讯云短信 sdkAppId 不能为空");
|
||||
result.setSdkAppId(keys[1]).setApiKey(keys[0]);
|
||||
return result;
|
||||
}
|
||||
}
|
@ -64,6 +64,19 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
|
||||
assertNotSame(client, ReflectUtil.getFieldValue(smsClient, "client"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRefresh() {
|
||||
// 准备参数
|
||||
SmsChannelProperties p = new SmsChannelProperties()
|
||||
.setApiKey(randomString() + " " + randomString()) // 随机一个 apiKey,避免构建报错
|
||||
.setApiSecret(randomString()) // 随机一个 apiSecret,避免构建报错
|
||||
.setSignature("芋道源码");
|
||||
// 调用
|
||||
smsClient.refresh(p);
|
||||
// 断言
|
||||
assertNotSame(client, ReflectUtil.getFieldValue(smsClient, "client"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoSendSms() throws Throwable {
|
||||
// 准备参数
|
||||
@ -81,7 +94,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
|
||||
o.setSendStatusSet(sendStatuses);
|
||||
SendStatus sendStatus = new SendStatus();
|
||||
sendStatuses[0] = sendStatus;
|
||||
sendStatus.setCode("Ok");
|
||||
sendStatus.setCode(TencentSmsClient.API_SUCCESS_CODE);
|
||||
sendStatus.setMessage("send success");
|
||||
sendStatus.setSerialNo(serialNo);
|
||||
});
|
||||
@ -162,7 +175,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
|
||||
// 调用
|
||||
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId.toString());
|
||||
// 断言
|
||||
assertEquals("Ok", result.getApiCode());
|
||||
assertEquals(TencentSmsClient.API_SUCCESS_CODE, result.getApiCode());
|
||||
assertNull(result.getApiMsg());
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
|
||||
@ -174,12 +187,23 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
|
||||
assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getData().getAuditReason());
|
||||
}
|
||||
|
||||
// TODO @FinallySays:这个单测,按道理说应该是写成 4 个方法,每个对应一种情况。
|
||||
@Test
|
||||
public void testConvertTemplateStatusDTO() {
|
||||
public void testConvertSuccessTemplateStatus() {
|
||||
testTemplateStatus(SmsTemplateAuditStatusEnum.SUCCESS, 0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertCheckingTemplateStatus() {
|
||||
testTemplateStatus(SmsTemplateAuditStatusEnum.CHECKING, 1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertFailTemplateStatus() {
|
||||
testTemplateStatus(SmsTemplateAuditStatusEnum.FAIL, -1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertUnknownTemplateStatus() {
|
||||
DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
|
||||
templateStatus.setStatusCode(3L);
|
||||
Long templateId = randomLongId();
|
||||
|
@ -20,7 +20,7 @@ public class TencentSmsCodeMappingTest extends BaseMockitoUnitTest {
|
||||
|
||||
@Test
|
||||
public void testApply() {
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply("Ok"));
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply(TencentSmsClient.API_SUCCESS_CODE));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID, codeMapping.apply("FailedOperation.ContainSensitiveWord"));
|
||||
assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("FailedOperation.JsonParseFail"));
|
||||
assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("MissingParameter.EmptyPhoneNumberSet"));
|
||||
|
Loading…
Reference in New Issue
Block a user