mirror of
https://gitee.com/hhyykk/ipms-sjy.git
synced 2025-07-23 23:45:08 +08:00
Merge remote-tracking branch 'origin/master' into feature/vue3
# Conflicts: # yudao-dependencies/pom.xml
This commit is contained in:
@ -12,7 +12,7 @@
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>短信拓展,支持阿里云、云片、腾讯云</description>
|
||||
<description>短信拓展,支持阿里云、腾讯云</description>
|
||||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<dependencies>
|
||||
@ -64,11 +64,6 @@
|
||||
<!-- 三方云服务相关 -->
|
||||
|
||||
<!-- SMS SDK begin -->
|
||||
<dependency>
|
||||
<groupId>com.yunpian.sdk</groupId>
|
||||
<artifactId>yunpian-java-sdk</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-core</artifactId>
|
||||
|
@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.sms.core.client.SmsClientFactory;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.impl.aliyun.AliyunSmsClient;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.impl.debug.DebugDingTalkSmsClient;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.impl.tencent.TencentSmsClient;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.impl.yunpian.YunpianSmsClient;
|
||||
import cn.iocoder.yudao.framework.sms.core.enums.SmsChannelEnum;
|
||||
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -80,7 +79,6 @@ public class SmsClientFactoryImpl implements SmsClientFactory {
|
||||
// 创建客户端
|
||||
switch (channelEnum) {
|
||||
case ALIYUN: return new AliyunSmsClient(properties);
|
||||
case YUN_PIAN: return new YunpianSmsClient(properties);
|
||||
case DEBUG_DING_TALK: return new DebugDingTalkSmsClient(properties);
|
||||
case TENCENT: return new TencentSmsClient(properties);
|
||||
}
|
||||
|
@ -1,210 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.sms.core.client.impl.yunpian;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||
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 com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.yunpian.sdk.YunpianClient;
|
||||
import com.yunpian.sdk.constant.YunpianConstant;
|
||||
import com.yunpian.sdk.model.Result;
|
||||
import com.yunpian.sdk.model.Template;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
|
||||
|
||||
/**
|
||||
* 云片短信客户端的实现类
|
||||
*
|
||||
* @author zzf
|
||||
* @since 9:48 2021/3/5
|
||||
*/
|
||||
@Slf4j
|
||||
public class YunpianSmsClient extends AbstractSmsClient {
|
||||
|
||||
/**
|
||||
* 云信短信客户端
|
||||
*/
|
||||
private volatile YunpianClient client;
|
||||
|
||||
public YunpianSmsClient(SmsChannelProperties properties) {
|
||||
super(properties, new YunpianSmsCodeMapping());
|
||||
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doInit() {
|
||||
YunpianClient oldClient = client;
|
||||
// 初始化新的客户端
|
||||
YunpianClient newClient = new YunpianClient(properties.getApiKey());
|
||||
newClient.init();
|
||||
this.client = newClient;
|
||||
// 销毁老的客户端
|
||||
if (oldClient != null) {
|
||||
oldClient.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
|
||||
String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
|
||||
return invoke(() -> {
|
||||
Map<String, String> request = new HashMap<>();
|
||||
request.put(YunpianConstant.MOBILE, mobile);
|
||||
request.put(YunpianConstant.TPL_ID, apiTemplateId);
|
||||
request.put(YunpianConstant.TPL_VALUE, formatTplValue(templateParams));
|
||||
request.put(YunpianConstant.UID, String.valueOf(sendLogId));
|
||||
request.put(YunpianConstant.CALLBACK_URL, properties.getCallbackUrl());
|
||||
return client.sms().tpl_single_send(request);
|
||||
}, response -> new SmsSendRespDTO().setSerialNo(String.valueOf(response.getSid())));
|
||||
}
|
||||
|
||||
private static String formatTplValue(List<KeyValue<String, Object>> templateParams) {
|
||||
if (CollUtil.isEmpty(templateParams)) {
|
||||
return "";
|
||||
}
|
||||
// 参考 https://www.yunpian.com/official/document/sms/zh_cn/introduction_demos_encode_sample 格式化
|
||||
StringJoiner joiner = new StringJoiner("&");
|
||||
templateParams.forEach(param -> joiner.add(String.format("#%s#=%s", param.getKey(),
|
||||
URLUtil.encode(String.valueOf(param.getValue())))));
|
||||
return joiner.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
|
||||
List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class);
|
||||
return statuses.stream().map(status -> {
|
||||
SmsReceiveRespDTO resp = new SmsReceiveRespDTO();
|
||||
resp.setSuccess(Objects.equals(status.getReportStatus(), "SUCCESS"));
|
||||
resp.setErrorCode(status.getErrorMsg()).setErrorMsg(status.getErrorDetail());
|
||||
resp.setMobile(status.getMobile()).setReceiveTime(status.getUserReceiveTime());
|
||||
resp.setSerialNo(String.valueOf(status.getSid())).setLogId(status.getUid());
|
||||
return resp;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable {
|
||||
return invoke(() -> {
|
||||
if (!NumberUtil.isNumber(apiTemplateId)) {
|
||||
throw new IllegalArgumentException("云片的 API 模板编号必须为整数");
|
||||
}
|
||||
Map<String, String> request = new HashMap<>();
|
||||
request.put(YunpianConstant.APIKEY, properties.getApiKey());
|
||||
request.put(YunpianConstant.TPL_ID, apiTemplateId);
|
||||
return client.tpl().get(request);
|
||||
}, response -> {
|
||||
Template template = response.get(0);
|
||||
return new SmsTemplateRespDTO().setId(String.valueOf(template.getTpl_id())).setContent(template.getTpl_content())
|
||||
.setAuditStatus(convertSmsTemplateAuditStatus(template.getCheck_status())).setAuditReason(template.getReason());
|
||||
});
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
Integer convertSmsTemplateAuditStatus(String checkStatus) {
|
||||
switch (checkStatus) {
|
||||
case "CHECKING": return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
|
||||
case "SUCCESS": return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
|
||||
case "FAIL": return SmsTemplateAuditStatusEnum.FAIL.getStatus();
|
||||
default: throw new IllegalArgumentException(String.format("未知审核状态(%s)", checkStatus));
|
||||
}
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
<T, R> SmsCommonResult<R> invoke(Supplier<Result<T>> requestConsumer, Function<T, R> responseConsumer) throws Throwable {
|
||||
// 执行请求
|
||||
Result<T> result = requestConsumer.get();
|
||||
if (result.getThrowable() != null) {
|
||||
throw result.getThrowable();
|
||||
}
|
||||
// 解析结果
|
||||
R data = null;
|
||||
if (result.getData() != null) {
|
||||
data = responseConsumer.apply(result.getData());
|
||||
}
|
||||
// 拼接结果
|
||||
return SmsCommonResult.build(String.valueOf(result.getCode()), formatResultMsg(result), null, data, codeMapping);
|
||||
}
|
||||
|
||||
private static String formatResultMsg(Result<?> sendResult) {
|
||||
if (StrUtil.isEmpty(sendResult.getDetail())) {
|
||||
return sendResult.getMsg();
|
||||
}
|
||||
return sendResult.getMsg() + " => " + sendResult.getDetail();
|
||||
}
|
||||
|
||||
/**
|
||||
* 短信接收状态
|
||||
*
|
||||
* 参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Data
|
||||
public static class SmsReceiveStatus {
|
||||
|
||||
/**
|
||||
* 接收状态
|
||||
*
|
||||
* 目前仅有 SUCCESS / FAIL,所以使用 Boolean 接收
|
||||
*/
|
||||
@JsonProperty("report_status")
|
||||
private String reportStatus;
|
||||
/**
|
||||
* 接收手机号
|
||||
*/
|
||||
private String mobile;
|
||||
/**
|
||||
* 运营商返回的代码,如:"DB:0103"
|
||||
*
|
||||
* 由于不同运营商信息不同,此字段仅供参考;
|
||||
*/
|
||||
@JsonProperty("error_msg")
|
||||
private String errorMsg;
|
||||
/**
|
||||
* 运营商反馈代码的中文解释
|
||||
*
|
||||
* 默认不推送此字段,如需推送,请联系客服
|
||||
*/
|
||||
@JsonProperty("error_detail")
|
||||
private String errorDetail;
|
||||
/**
|
||||
* 短信编号
|
||||
*/
|
||||
private Long sid;
|
||||
/**
|
||||
* 用户自定义 id
|
||||
*
|
||||
* 这里我们传递的是 SysSmsLogDO 的日志编号
|
||||
*/
|
||||
private Long uid;
|
||||
/**
|
||||
* 用户接收时间
|
||||
*/
|
||||
@JsonProperty("user_receive_time")
|
||||
@JsonFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND, timezone = TIME_ZONE_DEFAULT)
|
||||
private LocalDateTime userReceiveTime;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,59 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.sms.core.client.impl.yunpian;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
|
||||
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants.SUCCESS;
|
||||
import static com.yunpian.sdk.constant.Code.*;
|
||||
|
||||
/**
|
||||
* 云片的 SmsCodeMapping 实现类
|
||||
* <p>
|
||||
* 参见 https://www.yunpian.com/official/document/sms/zh_CN/returnvalue_common 文档
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class YunpianSmsCodeMapping implements SmsCodeMapping {
|
||||
|
||||
@Override
|
||||
public ErrorCode apply(String apiCode) {
|
||||
int code = Integer.parseInt(apiCode);
|
||||
switch (code) {
|
||||
case OK:
|
||||
return SUCCESS;
|
||||
case ARGUMENT_MISSING:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR;
|
||||
case BAD_ARGUMENT_FORMAT:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR;
|
||||
case TPL_NOT_FOUND:
|
||||
case TPL_NOT_VALID:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID;
|
||||
case MONEY_NOT_ENOUGH:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH;
|
||||
case BLACK_WORD:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID;
|
||||
case DUP_IN_SHORT_TIME:
|
||||
case TOO_MANY_TIME_IN_5:
|
||||
case DAY_LIMIT_PER_MOBILE:
|
||||
case HOUR_LIMIT_PER_MOBILE:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL;
|
||||
case BLACK_PHONE_FILTER:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_MOBILE_BLACK;
|
||||
case SIGN_NOT_MATCH:
|
||||
case BAD_SIGN_FORMAT:
|
||||
case SIGN_NOT_VALID:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID;
|
||||
case BAD_API_KEY:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID;
|
||||
case API_NOT_ALLOWED:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY;
|
||||
case IP_NOT_ALLOWED:
|
||||
return SmsFrameworkErrorCodeConstants.SMS_IP_DENY;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
|
||||
}
|
||||
|
||||
}
|
@ -15,7 +15,6 @@ import lombok.Getter;
|
||||
public enum SmsChannelEnum {
|
||||
|
||||
DEBUG_DING_TALK("DEBUG_DING_TALK", "调试(钉钉)"),
|
||||
YUN_PIAN("YUN_PIAN", "云片"),
|
||||
ALIYUN("ALIYUN", "阿里云"),
|
||||
TENCENT("TENCENT", "腾讯云"),
|
||||
// HUA_WEI("HUA_WEI", "华为云"),
|
||||
|
@ -16,7 +16,6 @@ public interface SmsFrameworkErrorCodeConstants {
|
||||
// ========== 权限 / 限流等相关 2001000100 ==========
|
||||
|
||||
ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2001000100, "没有发送短信的权限");
|
||||
// 云片:可以配置 IP 白名单,只有在白名单中才可以发送短信
|
||||
ErrorCode SMS_IP_DENY = new ErrorCode(2001000100, "IP 不允许发送短信");
|
||||
|
||||
// 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。
|
||||
|
@ -1,53 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.sms.core.client.impl.yunpian;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.impl.yunpian.YunpianSmsClient;
|
||||
import cn.iocoder.yudao.framework.sms.core.enums.SmsChannelEnum;
|
||||
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@link YunpianSmsClient} 的集成测试
|
||||
*/
|
||||
public class YunpianSmsClientIntegrationTest {
|
||||
|
||||
private static YunpianSmsClient smsClient;
|
||||
|
||||
@BeforeAll
|
||||
public static void init() {
|
||||
// 创建配置类
|
||||
SmsChannelProperties properties = new SmsChannelProperties();
|
||||
properties.setId(1L);
|
||||
properties.setSignature("芋道");
|
||||
properties.setCode(SmsChannelEnum.YUN_PIAN.getCode());
|
||||
properties.setApiKey("1555a14277cb8a608cf45a9e6a80d510");
|
||||
// 创建客户端
|
||||
smsClient = new YunpianSmsClient(properties);
|
||||
smsClient.init();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendSms() {
|
||||
List<KeyValue<String, Object>> templateParams = new ArrayList<>();
|
||||
templateParams.add(new KeyValue<>("code", "1024"));
|
||||
templateParams.add(new KeyValue<>("operation", "嘿嘿"));
|
||||
// SmsResult result = smsClient.send(1L, "15601691399", "4372216", templateParams);
|
||||
SmsCommonResult<SmsSendRespDTO> result = smsClient.sendSms(1L, "15601691399", "4383920", templateParams);
|
||||
System.out.println(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmsTemplate() {
|
||||
String apiTemplateId = "4383920";
|
||||
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.getSmsTemplate(apiTemplateId);
|
||||
System.out.println(result);
|
||||
}
|
||||
|
||||
}
|
@ -1,202 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.sms.core.client.impl.yunpian;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
|
||||
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
|
||||
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
|
||||
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.yunpian.sdk.YunpianClient;
|
||||
import com.yunpian.sdk.api.SmsApi;
|
||||
import com.yunpian.sdk.api.TplApi;
|
||||
import com.yunpian.sdk.constant.YunpianConstant;
|
||||
import com.yunpian.sdk.model.Result;
|
||||
import com.yunpian.sdk.model.SmsSingleSend;
|
||||
import com.yunpian.sdk.model.Template;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
|
||||
import static com.yunpian.sdk.constant.Code.OK;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* 对 {@link YunpianSmsClient} 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class YunpianSmsClientTest extends BaseMockitoUnitTest {
|
||||
|
||||
private final SmsChannelProperties properties = new SmsChannelProperties()
|
||||
.setApiKey(randomString()); // 随机一个 apiKey,避免构建报错
|
||||
|
||||
@InjectMocks
|
||||
private final YunpianSmsClient smsClient = new YunpianSmsClient(properties);
|
||||
|
||||
@Mock
|
||||
private YunpianClient client;
|
||||
|
||||
@Test
|
||||
public void testDoInit() {
|
||||
// 准备参数
|
||||
// mock 方法
|
||||
|
||||
// 调用
|
||||
smsClient.doInit();
|
||||
// 断言
|
||||
assertNotEquals(client, ReflectUtil.getFieldValue(smsClient, "client"));
|
||||
verify(client, times(1)).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testDoSendSms() throws Throwable {
|
||||
// 准备参数
|
||||
Long sendLogId = randomLongId();
|
||||
String mobile = randomString();
|
||||
String apiTemplateId = randomString();
|
||||
List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
|
||||
new KeyValue<>("code", 1234), new KeyValue<>("op", "login"));
|
||||
// mock sms 方法
|
||||
SmsApi smsApi = mock(SmsApi.class);
|
||||
when(client.sms()).thenReturn(smsApi);
|
||||
// mock tpl_single_send 方法
|
||||
Map<String, String> request = new HashMap<>();
|
||||
request.put(YunpianConstant.MOBILE, mobile);
|
||||
request.put(YunpianConstant.TPL_ID, apiTemplateId);
|
||||
request.put(YunpianConstant.TPL_VALUE, "#code#=1234&#op#=login");
|
||||
request.put(YunpianConstant.UID, String.valueOf(sendLogId));
|
||||
request.put(YunpianConstant.CALLBACK_URL, properties.getCallbackUrl());
|
||||
Result<SmsSingleSend> responseResult = randomPojo(Result.class, SmsSingleSend.class,
|
||||
o -> o.setCode(OK)); // API 发送成功的 code
|
||||
when(smsApi.tpl_single_send(eq(request))).thenReturn(responseResult);
|
||||
|
||||
// 调用
|
||||
SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile,
|
||||
apiTemplateId, templateParams);
|
||||
// 断言
|
||||
assertEquals(String.valueOf(responseResult.getCode()), result.getApiCode());
|
||||
assertEquals(responseResult.getMsg() + " => " + responseResult.getDetail(), result.getApiMsg());
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
|
||||
assertNull(result.getApiRequestId());
|
||||
// 断言结果
|
||||
assertEquals(String.valueOf(responseResult.getData().getSid()), result.getData().getSerialNo());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDoParseSmsReceiveStatus() throws Throwable {
|
||||
// 准备参数
|
||||
String text = "[{\"sid\":9527,\"uid\":1024,\"user_receive_time\":\"2014-03-17 22:55:21\",\"error_msg\":\"\",\"mobile\":\"15205201314\",\"report_status\":\"SUCCESS\"}]";
|
||||
// mock 方法
|
||||
|
||||
// 调用
|
||||
|
||||
// 断言
|
||||
// 调用
|
||||
List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text);
|
||||
// 断言
|
||||
assertEquals(1, statuses.size());
|
||||
assertTrue(statuses.get(0).getSuccess());
|
||||
assertEquals("", statuses.get(0).getErrorCode());
|
||||
assertNull(statuses.get(0).getErrorMsg());
|
||||
assertEquals("15205201314", statuses.get(0).getMobile());
|
||||
assertEquals(LocalDateTime.of(2014, 3, 17, 22, 55, 21), statuses.get(0).getReceiveTime());
|
||||
assertEquals("9527", statuses.get(0).getSerialNo());
|
||||
assertEquals(1024L, statuses.get(0).getLogId());
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testDoGetSmsTemplate() throws Throwable {
|
||||
// 准备参数
|
||||
String apiTemplateId = String.valueOf(randomLongId());
|
||||
// mock tpl 方法
|
||||
TplApi tplApi = mock(TplApi.class);
|
||||
when(client.tpl()).thenReturn(tplApi);
|
||||
// mock get 方法
|
||||
Map<String, String> request = new HashMap<>();
|
||||
request.put(YunpianConstant.APIKEY, properties.getApiKey());
|
||||
request.put(YunpianConstant.TPL_ID, apiTemplateId);
|
||||
Result<List<Template>> responseResult = randomPojo(Result.class, List.class, o -> {
|
||||
o.setCode(OK); // API 发送成功的 code
|
||||
o.setData(randomPojoList(Template.class, t -> t.setCheck_status("SUCCESS")));
|
||||
});
|
||||
when(tplApi.get(eq(request))).thenReturn(responseResult);
|
||||
|
||||
// 调用
|
||||
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId);
|
||||
// 断言
|
||||
assertEquals(String.valueOf(responseResult.getCode()), result.getApiCode());
|
||||
assertEquals(responseResult.getMsg() + " => " + responseResult.getDetail(), result.getApiMsg());
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
|
||||
assertNull(result.getApiRequestId());
|
||||
// 断言结果
|
||||
Template template = responseResult.getData().get(0);
|
||||
assertEquals(template.getTpl_id().toString(), result.getData().getId());
|
||||
assertEquals(template.getTpl_content(), result.getData().getContent());
|
||||
assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
|
||||
assertEquals(template.getReason(), result.getData().getAuditReason());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertSmsTemplateAuditStatus() {
|
||||
assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(),
|
||||
smsClient.convertSmsTemplateAuditStatus("CHECKING"));
|
||||
assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(),
|
||||
smsClient.convertSmsTemplateAuditStatus("SUCCESS"));
|
||||
assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(),
|
||||
smsClient.convertSmsTemplateAuditStatus("FAIL"));
|
||||
assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus("test"),
|
||||
"未知审核状态(test)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvoke_throwable() {
|
||||
// 准备参数
|
||||
Supplier<Result<Object>> requestConsumer =
|
||||
() -> new Result<>().setThrowable(new NullPointerException());
|
||||
// mock 方法
|
||||
|
||||
// 调用,并断言异常
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> smsClient.invoke(requestConsumer, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testInvoke_success() throws Throwable {
|
||||
// 准备参数
|
||||
Result<SmsSingleSend> responseResult = randomPojo(Result.class, SmsSingleSend.class, o -> o.setCode(OK));
|
||||
Supplier<Result<SmsSingleSend>> requestConsumer = () -> responseResult;
|
||||
Function<SmsSingleSend, SmsSendRespDTO> responseConsumer =
|
||||
smsSingleSend -> new SmsSendRespDTO().setSerialNo(String.valueOf(responseResult.getData().getSid()));
|
||||
// mock 方法
|
||||
|
||||
// 调用
|
||||
SmsCommonResult<SmsSendRespDTO> result = smsClient.invoke(requestConsumer, responseConsumer);
|
||||
// 断言
|
||||
assertEquals(String.valueOf(responseResult.getCode()), result.getApiCode());
|
||||
assertEquals(responseResult.getMsg() + " => " + responseResult.getDetail(), result.getApiMsg());
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
|
||||
assertNull(result.getApiRequestId());
|
||||
assertEquals(String.valueOf(responseResult.getData().getSid()), result.getData().getSerialNo());
|
||||
}
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package cn.iocoder.yudao.framework.sms.core.client.impl.yunpian;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
|
||||
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
|
||||
import static com.yunpian.sdk.constant.Code.*;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
/**
|
||||
* {@link YunpianSmsCodeMapping} 的单元测试
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
class YunpianSmsCodeMappingTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private YunpianSmsCodeMapping codeMapping;
|
||||
|
||||
@Test
|
||||
public void testApply() {
|
||||
assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply(String.valueOf(OK)));
|
||||
Assertions.assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply(String.valueOf(ARGUMENT_MISSING)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply(String.valueOf(BAD_ARGUMENT_FORMAT)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply(String.valueOf(MONEY_NOT_ENOUGH)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply(String.valueOf(TPL_NOT_FOUND)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply(String.valueOf(TPL_NOT_VALID)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(DUP_IN_SHORT_TIME)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(TOO_MANY_TIME_IN_5)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(DAY_LIMIT_PER_MOBILE)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply(String.valueOf(HOUR_LIMIT_PER_MOBILE)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_BLACK, codeMapping.apply(String.valueOf(BLACK_PHONE_FILTER)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply(String.valueOf(SIGN_NOT_MATCH)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply(String.valueOf(SIGN_NOT_VALID)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply(String.valueOf(BAD_SIGN_FORMAT)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply(String.valueOf(BAD_API_KEY)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply(String.valueOf(API_NOT_ALLOWED)));
|
||||
assertEquals(SmsFrameworkErrorCodeConstants.SMS_IP_DENY, codeMapping.apply(String.valueOf(IP_NOT_ALLOWED)));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user