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;
|
protected final SmsCodeMapping codeMapping;
|
||||||
|
|
||||||
public AbstractSmsClient(SmsChannelProperties properties, SmsCodeMapping codeMapping) {
|
public AbstractSmsClient(SmsChannelProperties properties, SmsCodeMapping codeMapping) {
|
||||||
this.properties = properties;
|
this.properties = prepareProperties(properties);
|
||||||
this.codeMapping = codeMapping;
|
this.codeMapping = codeMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,11 +54,21 @@ public abstract class AbstractSmsClient implements SmsClient {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
log.info("[refresh][配置({})发生变化,重新初始化]", properties);
|
log.info("[refresh][配置({})发生变化,重新初始化]", properties);
|
||||||
this.properties = properties;
|
this.properties = prepareProperties(properties);
|
||||||
// 初始化
|
// 初始化
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在赋值给{@link this#properties}前,子类可根据需要预处理短信渠道配置
|
||||||
|
*
|
||||||
|
* @param properties 数据库中存储的短信渠道配置
|
||||||
|
* @return 满足子类实现的短信渠道配置
|
||||||
|
*/
|
||||||
|
protected SmsChannelProperties prepareProperties(SmsChannelProperties properties) {
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getId() {
|
public Long getId() {
|
||||||
return properties.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.client.impl.AbstractSmsClient;
|
||||||
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
|
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.SmsChannelProperties;
|
||||||
|
import cn.iocoder.yudao.framework.sms.core.property.TencentSmsChannelProperties;
|
||||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
@ -42,21 +43,33 @@ public class TencentSmsClient extends AbstractSmsClient {
|
|||||||
|
|
||||||
private SmsClient client;
|
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) {
|
public TencentSmsClient(SmsChannelProperties properties) {
|
||||||
// 腾讯云发放短信的时候,需要额外的参数 sdkAppId。考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
|
super(properties, new TencentSmsCodeMapping());
|
||||||
// 因此,这边需要使用 TencentSmsChannelProperties 做拆分,重新封装到 properties 内。
|
|
||||||
super(TencentSmsChannelProperties.build(properties), new TencentSmsCodeMapping());
|
|
||||||
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
|
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doInit() {
|
protected void doInit() {
|
||||||
// init 或者 refresh 时,需要重新封装 properties
|
|
||||||
properties = TencentSmsChannelProperties.build(properties);
|
|
||||||
// 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId,secretKey
|
// 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId,secretKey
|
||||||
Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret());
|
Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret());
|
||||||
// TODO @FinallySays:那把 ap-nanjing 枚举下到这个类的静态变量里哈。
|
client = new SmsClient(credential, ENDPOINT);
|
||||||
client = new SmsClient(credential, "ap-nanjing");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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 发送短信
|
* 调用腾讯云 SDK 发送短信
|
||||||
*
|
*
|
||||||
@ -113,7 +140,7 @@ public class TencentSmsClient extends AbstractSmsClient {
|
|||||||
return CollectionUtils.convertList(callback, status -> {
|
return CollectionUtils.convertList(callback, status -> {
|
||||||
SmsReceiveRespDTO data = new SmsReceiveRespDTO();
|
SmsReceiveRespDTO data = new SmsReceiveRespDTO();
|
||||||
data.setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription());
|
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());
|
data.setMobile(status.getMobile()).setSerialNo(status.getSerialNo());
|
||||||
SessionContext context;
|
SessionContext context;
|
||||||
Long logId;
|
Long logId;
|
||||||
@ -130,7 +157,7 @@ public class TencentSmsClient extends AbstractSmsClient {
|
|||||||
this::doGetSmsTemplate0,
|
this::doGetSmsTemplate0,
|
||||||
response -> {
|
response -> {
|
||||||
SmsTemplateRespDTO data = convertTemplateStatusDTO(response.getDescribeTemplateStatusSet()[0]);
|
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();
|
DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest();
|
||||||
request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)});
|
request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)});
|
||||||
// 地区 0:表示国内短信。1:表示国际/港澳台短信。
|
// 地区 0:表示国内短信。1:表示国际/港澳台短信。
|
||||||
// TODO @FinallySays:那把 0L 枚举下到这个类的静态变量里哈。
|
request.setInternational(INTERNATIONAL);
|
||||||
request.setInternational(0L);
|
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,6 +232,11 @@ public class TencentSmsClient extends AbstractSmsClient {
|
|||||||
@Data
|
@Data
|
||||||
private static class SmsReceiveStatus {
|
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;
|
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
|
@Override
|
||||||
public ErrorCode apply(String apiCode) {
|
public ErrorCode apply(String apiCode) {
|
||||||
switch (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.ContainSensitiveWord": return SMS_SEND_CONTENT_INVALID;
|
||||||
case "FailedOperation.JsonParseFail":
|
case "FailedOperation.JsonParseFail":
|
||||||
case "MissingParameter.EmptyPhoneNumberSet":
|
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"));
|
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
|
@Test
|
||||||
public void testDoSendSms() throws Throwable {
|
public void testDoSendSms() throws Throwable {
|
||||||
// 准备参数
|
// 准备参数
|
||||||
@ -81,7 +94,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
|
|||||||
o.setSendStatusSet(sendStatuses);
|
o.setSendStatusSet(sendStatuses);
|
||||||
SendStatus sendStatus = new SendStatus();
|
SendStatus sendStatus = new SendStatus();
|
||||||
sendStatuses[0] = sendStatus;
|
sendStatuses[0] = sendStatus;
|
||||||
sendStatus.setCode("Ok");
|
sendStatus.setCode(TencentSmsClient.API_SUCCESS_CODE);
|
||||||
sendStatus.setMessage("send success");
|
sendStatus.setMessage("send success");
|
||||||
sendStatus.setSerialNo(serialNo);
|
sendStatus.setSerialNo(serialNo);
|
||||||
});
|
});
|
||||||
@ -162,7 +175,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
|
|||||||
// 调用
|
// 调用
|
||||||
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId.toString());
|
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId.toString());
|
||||||
// 断言
|
// 断言
|
||||||
assertEquals("Ok", result.getApiCode());
|
assertEquals(TencentSmsClient.API_SUCCESS_CODE, result.getApiCode());
|
||||||
assertNull(result.getApiMsg());
|
assertNull(result.getApiMsg());
|
||||||
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
|
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
|
||||||
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
|
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
|
||||||
@ -174,12 +187,23 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
|
|||||||
assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getData().getAuditReason());
|
assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getData().getAuditReason());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO @FinallySays:这个单测,按道理说应该是写成 4 个方法,每个对应一种情况。
|
|
||||||
@Test
|
@Test
|
||||||
public void testConvertTemplateStatusDTO() {
|
public void testConvertSuccessTemplateStatus() {
|
||||||
testTemplateStatus(SmsTemplateAuditStatusEnum.SUCCESS, 0L);
|
testTemplateStatus(SmsTemplateAuditStatusEnum.SUCCESS, 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvertCheckingTemplateStatus() {
|
||||||
testTemplateStatus(SmsTemplateAuditStatusEnum.CHECKING, 1L);
|
testTemplateStatus(SmsTemplateAuditStatusEnum.CHECKING, 1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvertFailTemplateStatus() {
|
||||||
testTemplateStatus(SmsTemplateAuditStatusEnum.FAIL, -1L);
|
testTemplateStatus(SmsTemplateAuditStatusEnum.FAIL, -1L);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConvertUnknownTemplateStatus() {
|
||||||
DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
|
DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
|
||||||
templateStatus.setStatusCode(3L);
|
templateStatus.setStatusCode(3L);
|
||||||
Long templateId = randomLongId();
|
Long templateId = randomLongId();
|
||||||
|
@ -20,7 +20,7 @@ public class TencentSmsCodeMappingTest extends BaseMockitoUnitTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testApply() {
|
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(SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID, codeMapping.apply("FailedOperation.ContainSensitiveWord"));
|
||||||
assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("FailedOperation.JsonParseFail"));
|
assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("FailedOperation.JsonParseFail"));
|
||||||
assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("MissingParameter.EmptyPhoneNumberSet"));
|
assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("MissingParameter.EmptyPhoneNumberSet"));
|
||||||
|
Loading…
Reference in New Issue
Block a user