sms:移除 SmsCodeMapping + SmsCommonResult,简化短信的封装

pull/67/head
YunaiV 2023-11-21 23:32:26 +08:00
parent 4118f25d75
commit 562f82580e
30 changed files with 346 additions and 874 deletions

View File

@ -31,8 +31,8 @@ public interface SmsClient {
* @param templateParams List * @param templateParams List
* @return * @return
*/ */
SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, String apiTemplateId, SmsSendRespDTO sendSms(Long logId, String mobile, String apiTemplateId,
List<KeyValue<String, Object>> templateParams); List<KeyValue<String, Object>> templateParams) throws Throwable;
/** /**
* *
@ -49,6 +49,6 @@ public interface SmsClient {
* @param apiTemplateId API * @param apiTemplateId API
* @return * @return
*/ */
SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId); SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable;
} }

View File

@ -1,17 +0,0 @@
package cn.iocoder.yudao.framework.sms.core.client;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import java.util.function.Function;
/**
* API
*
* @see SmsCommonResult
* @see SmsFrameworkErrorCodeConstants
*
* @author
*/
public interface SmsCodeMapping extends Function<String, ErrorCode> {
}

View File

@ -1,68 +0,0 @@
package cn.iocoder.yudao.framework.sms.core.client;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
/**
* CommonResult
*
* code msg {@link #apiCode} {@link #apiMsg}
*
* {@link #apiRequestId}
*
* @author
*/
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class SmsCommonResult<T> extends CommonResult<T> {
/**
* API
*
* 使 String
*/
private String apiCode;
/**
* API
*/
private String apiMsg;
/**
* API
*/
private String apiRequestId;
private SmsCommonResult() {
}
public static <T> SmsCommonResult<T> build(String apiCode, String apiMsg, String apiRequestId,
T data, SmsCodeMapping codeMapping) {
Assert.notNull(codeMapping, "参数 codeMapping 不能为空");
SmsCommonResult<T> result = new SmsCommonResult<T>().setApiCode(apiCode).setApiMsg(apiMsg).setApiRequestId(apiRequestId);
result.setData(data);
// 翻译错误码
if (codeMapping != null) {
ErrorCode errorCode = codeMapping.apply(apiCode);
if (errorCode == null) {
errorCode = SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
}
result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg());
}
return result;
}
public static <T> SmsCommonResult<T> error(Throwable ex) {
SmsCommonResult<T> result = new SmsCommonResult<>();
result.setCode(SmsFrameworkErrorCodeConstants.EXCEPTION.getCode());
result.setMsg(ExceptionUtil.getRootCauseMessage(ex));
return result;
}
}

View File

@ -10,9 +10,34 @@ import lombok.Data;
@Data @Data
public class SmsSendRespDTO { public class SmsSendRespDTO {
/**
*
*/
private Boolean success;
/**
* API
*/
private String apiRequestId;
// ==================== 成功时字段 ====================
/** /**
* API * API
*/ */
private String serialNo; private String serialNo;
// ==================== 失败时字段 ====================
/**
* API
*
* 使 String
*/
private String apiCode;
/**
* API
*/
private String apiMsg;
} }

View File

@ -1,17 +1,9 @@
package cn.iocoder.yudao.framework.sms.core.client.impl; package cn.iocoder.yudao.framework.sms.core.client.impl;
import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.sms.core.client.SmsClient; import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
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.property.SmsChannelProperties; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import java.util.List;
/** /**
* *
* *
@ -25,14 +17,9 @@ public abstract class AbstractSmsClient implements SmsClient {
* *
*/ */
protected volatile SmsChannelProperties properties; protected volatile SmsChannelProperties properties;
/**
*
*/
protected final SmsCodeMapping codeMapping;
public AbstractSmsClient(SmsChannelProperties properties, SmsCodeMapping codeMapping) { public AbstractSmsClient(SmsChannelProperties properties) {
this.properties = prepareProperties(properties); this.properties = properties;
this.codeMapping = codeMapping;
} }
/** /**
@ -54,74 +41,13 @@ public abstract class AbstractSmsClient implements SmsClient {
return; return;
} }
log.info("[refresh][配置({})发生变化,重新初始化]", properties); log.info("[refresh][配置({})发生变化,重新初始化]", 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();
} }
@Override
public final SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile,
String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
// 执行短信发送
SmsCommonResult<SmsSendRespDTO> result;
try {
result = doSendSms(logId, mobile, apiTemplateId, templateParams);
} catch (Throwable ex) {
// 打印异常日志
log.error("[sendSms][发送短信异常sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]",
logId, mobile, apiTemplateId, templateParams, ex);
// 封装返回
return SmsCommonResult.error(ex);
}
return result;
}
protected abstract SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
String apiTemplateId, List<KeyValue<String, Object>> templateParams)
throws Throwable;
@Override
public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable {
try {
return doParseSmsReceiveStatus(text);
} catch (Throwable ex) {
log.error("[parseSmsReceiveStatus][text({}) 解析发生异常]", text, ex);
throw ex;
}
}
protected abstract List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable;
@Override
public SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId) {
// 执行短信发送
SmsCommonResult<SmsTemplateRespDTO> result;
try {
result = doGetSmsTemplate(apiTemplateId);
} catch (Throwable ex) {
// 打印异常日志
log.error("[getSmsTemplate][获得短信模板({}) 发生异常]", apiTemplateId, ex);
// 封装返回
return SmsCommonResult.error(ex);
}
return result;
}
protected abstract SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable;
} }

View File

@ -1,25 +1,21 @@
package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun; package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 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.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; 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.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 com.aliyuncs.AcsRequest;
import com.aliyuncs.AcsResponse;
import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient; import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest; import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile; import com.aliyuncs.profile.IClientProfile;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
@ -31,9 +27,8 @@ import lombok.extern.slf4j.Slf4j;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
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.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
@ -46,6 +41,11 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DE
@Slf4j @Slf4j
public class AliyunSmsClient extends AbstractSmsClient { public class AliyunSmsClient extends AbstractSmsClient {
/**
* code
*/
public static final String API_CODE_SUCCESS = "OK";
/** /**
* REGION, 使 * REGION, 使
*/ */
@ -57,7 +57,7 @@ public class AliyunSmsClient extends AbstractSmsClient {
private volatile IAcsClient client; private volatile IAcsClient client;
public AliyunSmsClient(SmsChannelProperties properties) { public AliyunSmsClient(SmsChannelProperties properties) {
super(properties, new AliyunSmsCodeMapping()); super(properties);
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
} }
@ -69,9 +69,9 @@ public class AliyunSmsClient extends AbstractSmsClient {
} }
@Override @Override
protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
String apiTemplateId, List<KeyValue<String, Object>> templateParams) { List<KeyValue<String, Object>> templateParams) throws Throwable {
// 构建参数 // 构建请求
SendSmsRequest request = new SendSmsRequest(); SendSmsRequest request = new SendSmsRequest();
request.setPhoneNumbers(mobile); request.setPhoneNumbers(mobile);
request.setSignName(properties.getSignature()); request.setSignName(properties.getSignature());
@ -79,34 +79,32 @@ public class AliyunSmsClient extends AbstractSmsClient {
request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams))); request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams)));
request.setOutId(String.valueOf(sendLogId)); request.setOutId(String.valueOf(sendLogId));
// 执行请求 // 执行请求
return invoke(request, response -> new SmsSendRespDTO().setSerialNo(response.getBizId())); SendSmsResponse response = client.getAcsResponse(request);
return new SmsSendRespDTO().setSuccess(Objects.equals(response.getCode(), API_CODE_SUCCESS)).setSerialNo(response.getBizId())
.setApiRequestId(response.getRequestId()).setApiCode(response.getCode()).setApiMsg(response.getMessage());
} }
@Override @Override
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable { public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class); List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class);
return statuses.stream().map(status -> { return convertList(statuses, status -> new SmsReceiveRespDTO().setSuccess(status.getSuccess())
SmsReceiveRespDTO resp = new SmsReceiveRespDTO(); .setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg())
resp.setSuccess(status.getSuccess()); .setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime())
resp.setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg()); .setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId())));
resp.setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime());
resp.setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId()));
return resp;
}).collect(Collectors.toList());
} }
@Override @Override
protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) { public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
// 构建参数 // 构建请求
QuerySmsTemplateRequest request = new QuerySmsTemplateRequest(); QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
request.setTemplateCode(apiTemplateId); request.setTemplateCode(apiTemplateId);
// 执行请求 // 执行请求
return invoke(request, response -> { QuerySmsTemplateResponse response = client.getAcsResponse(request);
SmsTemplateRespDTO data = new SmsTemplateRespDTO(); if (response.getTemplateStatus() == null) {
data.setId(response.getTemplateCode()).setContent(response.getTemplateContent()); return null;
data.setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason()); }
return data; return new SmsTemplateRespDTO().setId(response.getTemplateCode()).setContent(response.getTemplateContent())
}); .setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason());
} }
@VisibleForTesting @VisibleForTesting
@ -119,37 +117,10 @@ public class AliyunSmsClient extends AbstractSmsClient {
} }
} }
@VisibleForTesting
<T extends AcsResponse, R> SmsCommonResult<R> invoke(AcsRequest<T> request, Function<T, R> responseConsumer) {
try {
// 执行发送. 由于阿里云 sms 短信没有统一的 Response但是有统一的 code、message、requestId 属性,所以只好反射
T sendResult = client.getAcsResponse(request);
String code = (String) ReflectUtil.getFieldValue(sendResult, "code");
String message = (String) ReflectUtil.getFieldValue(sendResult, "message");
String requestId = (String) ReflectUtil.getFieldValue(sendResult, "requestId");
// 解析结果
R data = null;
if (Objects.equals(code, "OK")) { // 请求成功的情况下
data = responseConsumer.apply(sendResult);
}
// 拼接结果
return SmsCommonResult.build(code, message, requestId, data, codeMapping);
} catch (ClientException ex) {
return SmsCommonResult.build(ex.getErrCode(), formatResultMsg(ex), ex.getRequestId(), null, codeMapping);
}
}
private static String formatResultMsg(ClientException ex) {
if (StrUtil.isEmpty(ex.getErrorDescription())) {
return ex.getErrMsg();
}
return ex.getErrMsg() + " => " + ex.getErrorDescription();
}
/** /**
* *
* *
* https://help.aliyun.com/document_detail/101867.html 文档 * <a href="https://help.aliyun.com/document_detail/101867.html"></a>
* *
* @author * @author
*/ */

View File

@ -1,42 +0,0 @@
package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
/**
* SmsCodeMapping
*
* https://help.aliyun.com/document_detail/101346.htm 文档
*
* @author
*/
public class AliyunSmsCodeMapping implements SmsCodeMapping {
@Override
public ErrorCode apply(String apiCode) {
switch (apiCode) {
case "OK": return GlobalErrorCodeConstants.SUCCESS;
case "isv.ACCOUNT_NOT_EXISTS":
case "isv.ACCOUNT_ABNORMAL":
case "MissingAccessKeyId": return SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID;
case "isp.RAM_PERMISSION_DENY": return SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY;
case "isv.INVALID_JSON_PARAM":
case "isv.INVALID_PARAMETERS": return SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR;
case "isv.BUSINESS_LIMIT_CONTROL": return SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL;
case "isv.DAY_LIMIT_CONTROL": return SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL;
case "isv.SMS_CONTENT_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID;
case "isv.SMS_TEMPLATE_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID;
case "isv.SMS_SIGNATURE_ILLEGAL":
case "isv.SIGN_NAME_ILLEGAL":
case "isv.SMS_SIGN_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID;
case "isv.AMOUNT_NOT_ENOUGH":
case "isv.OUT_OF_SERVICE": return SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH;
case "isv.MOBILE_NUMBER_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID;
case "isv.TEMPLATE_MISSING_PARAMETERS": return SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR;
default: return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
}
}
}

View File

@ -1,22 +0,0 @@
package cn.iocoder.yudao.framework.sms.core.client.impl.debug;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import java.util.Objects;
/**
* SmsCodeMapping
*
* @author
*/
public class DebugDingTalkCodeMapping implements SmsCodeMapping {
@Override
public ErrorCode apply(String apiCode) {
return Objects.equals(apiCode, "0") ? GlobalErrorCodeConstants.SUCCESS : SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
}
}

View File

@ -8,19 +8,19 @@ import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.HmacAlgorithm; import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; 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.SmsSendRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; 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.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
/** /**
* WebHook * WebHook
@ -32,7 +32,7 @@ import java.util.Map;
public class DebugDingTalkSmsClient extends AbstractSmsClient { public class DebugDingTalkSmsClient extends AbstractSmsClient {
public DebugDingTalkSmsClient(SmsChannelProperties properties) { public DebugDingTalkSmsClient(SmsChannelProperties properties) {
super(properties, new DebugDingTalkCodeMapping()); super(properties);
Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
} }
@ -42,8 +42,8 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
} }
@Override @Override
protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile, public SmsSendRespDTO sendSms(Long sendLogId, String mobile,
String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable { String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
// 构建请求 // 构建请求
String url = buildUrl("robot/send"); String url = buildUrl("robot/send");
Map<String, Object> params = new HashMap<>(); Map<String, Object> params = new HashMap<>();
@ -55,14 +55,15 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
String responseText = HttpUtil.post(url, JsonUtils.toJsonString(params)); String responseText = HttpUtil.post(url, JsonUtils.toJsonString(params));
// 解析结果 // 解析结果
Map<?, ?> responseObj = JsonUtils.parseObject(responseText, Map.class); Map<?, ?> responseObj = JsonUtils.parseObject(responseText, Map.class);
return SmsCommonResult.build(MapUtil.getStr(responseObj, "errcode"), MapUtil.getStr(responseObj, "errorMsg"), String errorCode = MapUtil.getStr(responseObj, "errcode");
null, new SmsSendRespDTO().setSerialNo(StrUtil.uuid()), codeMapping); return new SmsSendRespDTO().setSuccess(Objects.equals(errorCode, "0")).setSerialNo(StrUtil.uuid())
.setApiCode(errorCode).setApiMsg(MapUtil.getStr(responseObj, "errorMsg"));
} }
/** /**
* *
* *
* https://developers.dingtalk.com/document/app/custom-robot-access/title-nfv-794-g71 文档 * <a href="https://developers.dingtalk.com/document/app/custom-robot-access/title-nfv-794-g71"></a>
* *
* @param path * @param path
* @return * @return
@ -82,15 +83,14 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
} }
@Override @Override
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable { public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
throw new UnsupportedOperationException("模拟短信客户端,暂时无需解析回调"); throw new UnsupportedOperationException("模拟短信客户端,暂时无需解析回调");
} }
@Override @Override
protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) { public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) {
SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("") return new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(""); .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason("");
return SmsCommonResult.build("0", "success", null, data, codeMapping);
} }
} }

View File

@ -1,41 +0,0 @@
package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
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;
}
}

View File

@ -4,9 +4,7 @@ import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 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.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; 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.dto.SmsTemplateRespDTO;
@ -17,23 +15,22 @@ 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;
import com.tencentcloudapi.common.Credential; import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.sms.v20210111.SmsClient; import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.*; import com.tencentcloudapi.sms.v20210111.models.*;
import lombok.Data; import lombok.Data;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.Objects;
import java.util.function.Supplier;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
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.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
/** /**
* *
* <p> *
* https://cloud.tencent.com/document/product/382/52077 * <a href="https://cloud.tencent.com/document/product/382/52077"></a>
* *
* @author shiwp * @author shiwp
*/ */
@ -42,7 +39,7 @@ public class TencentSmsClient extends AbstractSmsClient {
/** /**
* code * code
*/ */
public static final String API_SUCCESS_CODE = "Ok"; public static final String API_CODE_SUCCESS = "Ok";
/** /**
* REGION使 * REGION使
@ -51,180 +48,103 @@ public class TencentSmsClient extends AbstractSmsClient {
/** /**
* / * /
*
* 0 * 0
* 1/ * 1/
*/ */
private static final long INTERNATIONAL = 0L; private static final long INTERNATIONAL_CHINA = 0L;
private SmsClient client; private SmsClient client;
public TencentSmsClient(SmsChannelProperties properties) { public TencentSmsClient(SmsChannelProperties properties) {
super(properties, new TencentSmsCodeMapping()); super(properties);
Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
validateSdkAppId(properties);
} }
@Override @Override
protected void doInit() { protected void doInit() {
// 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretIdsecretKey // 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretIdsecretKey
Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret()); Credential credential = new Credential(getApiKey(), properties.getApiSecret());
client = new SmsClient(credential, ENDPOINT); client = new SmsClient(credential, ENDPOINT);
} }
/**
* SDK AppId
*
* sdkAppId
*
* apiKey + apiSecret secretId apiKey "secretId sdkAppId"
*
* @param properties
*/
private static void validateSdkAppId(SmsChannelProperties properties) {
String combineKey = properties.getApiKey();
Assert.notEmpty(combineKey, "apiKey 不能为空");
String[] keys = combineKey.trim().split(" ");
Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]");
}
private String getSdkAppId() {
return StrUtil.subAfter(properties.getApiKey(), " ", true);
}
private String getApiKey() {
return StrUtil.subBefore(properties.getApiKey(), " ", true);
}
@Override @Override
protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, public SmsSendRespDTO sendSms(Long sendLogId, String mobile,
String mobile, String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
String apiTemplateId, // 构建请求
List<KeyValue<String, Object>> templateParams) throws Throwable {
return invoke(() -> buildSendSmsRequest(sendLogId, mobile, apiTemplateId, templateParams),
this::doSendSms0,
response -> {
SendStatus sendStatus = response.getSendStatusSet()[0];
return SmsCommonResult.build(sendStatus.getCode(), sendStatus.getMessage(), response.getRequestId(),
new SmsSendRespDTO().setSerialNo(sendStatus.getSerialNo()), codeMapping);
});
}
/**
* sdkAppId
* apiKey + apiSecret secretId apiKey "secretId sdkAppId"
* 使 TencentSmsChannelProperties properties
*
* @param properties
* @return TencentSmsChannelProperties
*/
@Override
protected SmsChannelProperties prepareProperties(SmsChannelProperties properties) {
return TencentSmsChannelProperties.build(properties);
}
/**
* SDK
*
* @param request
* @return
* @throws TencentCloudSDKException SDK
*/
private SendSmsResponse doSendSms0(SendSmsRequest request) throws TencentCloudSDKException {
return client.SendSms(request);
}
/**
*
*
* @param sendLogId
* @param mobile
* @param apiTemplateId API
* @param templateParams List
* @return
*/
private SendSmsRequest buildSendSmsRequest(Long sendLogId,
String mobile,
String apiTemplateId,
List<KeyValue<String, Object>> templateParams) {
SendSmsRequest request = new SendSmsRequest(); SendSmsRequest request = new SendSmsRequest();
request.setSmsSdkAppId(((TencentSmsChannelProperties) properties).getSdkAppId()); request.setSmsSdkAppId(getSdkAppId());
request.setPhoneNumberSet(new String[]{mobile}); request.setPhoneNumberSet(new String[]{mobile});
request.setSignName(properties.getSignature()); request.setSignName(properties.getSignature());
request.setTemplateId(apiTemplateId); request.setTemplateId(apiTemplateId);
request.setTemplateParamSet(ArrayUtils.toArray(templateParams, e -> String.valueOf(e.getValue()))); request.setTemplateParamSet(ArrayUtils.toArray(templateParams, e -> String.valueOf(e.getValue())));
request.setSessionContext(JsonUtils.toJsonString(new SessionContext().setLogId(sendLogId))); request.setSessionContext(JsonUtils.toJsonString(new SessionContext().setLogId(sendLogId)));
return request; // 执行请求
SendSmsResponse response = client.SendSms(request);
SendStatus status = response.getSendStatusSet()[0];
return new SmsSendRespDTO().setSuccess(Objects.equals(status.getCode(), API_CODE_SUCCESS)).setSerialNo(status.getSerialNo())
.setApiRequestId(response.getRequestId()).setApiCode(status.getCode()).setApiMsg(status.getMessage());
} }
@Override @Override
protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable { public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
List<SmsReceiveStatus> callback = JsonUtils.parseArray(text, SmsReceiveStatus.class); List<SmsReceiveStatus> callback = JsonUtils.parseArray(text, SmsReceiveStatus.class);
return CollectionUtils.convertList(callback, status -> { return convertList(callback, status -> new SmsReceiveRespDTO()
SmsReceiveRespDTO data = new SmsReceiveRespDTO(); .setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus()))
data.setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription()); .setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription())
data.setReceiveTime(status.getReceiveTime()).setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus())); .setMobile(status.getMobile()).setReceiveTime(status.getReceiveTime())
data.setMobile(status.getMobile()).setSerialNo(status.getSerialNo()); .setSerialNo(status.getSerialNo()).setLogId(status.getSessionContext().getLogId()));
SessionContext context;
Long logId;
Assert.notNull(context = status.getSessionContext(), "回执信息中未解析出 context请联系腾讯云小助手");
Assert.notNull(logId = context.getLogId(), "回执信息中未解析出 logId请联系腾讯云小助手");
data.setLogId(logId);
return data;
});
} }
@Override @Override
protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable { public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
return invoke(() -> this.buildSmsTemplateStatusRequest(apiTemplateId), // 构建请求
this::doGetSmsTemplate0, DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest();
response -> { request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)});
SmsTemplateRespDTO data = convertTemplateStatusDTO(response.getDescribeTemplateStatusSet()[0]); request.setInternational(INTERNATIONAL_CHINA);
return SmsCommonResult.build(API_SUCCESS_CODE, null, response.getRequestId(), data, codeMapping); // 执行请求
}); DescribeSmsTemplateListResponse response = client.DescribeSmsTemplateList(request);
DescribeTemplateListStatus status = response.getDescribeTemplateStatusSet()[0];
if (status == null || status.getStatusCode() == null) {
return null;
}
return new SmsTemplateRespDTO().setId(status.getTemplateId().toString()).setContent(status.getTemplateContent())
.setAuditStatus(convertSmsTemplateAuditStatus(status.getStatusCode().intValue())).setAuditReason(status.getReviewReply());
} }
@VisibleForTesting @VisibleForTesting
SmsTemplateRespDTO convertTemplateStatusDTO(DescribeTemplateListStatus templateStatus) { Integer convertSmsTemplateAuditStatus(int templateStatus) {
if (templateStatus == null) { switch (templateStatus) {
return null; case 1: return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
case 0: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
case -1: return SmsTemplateAuditStatusEnum.FAIL.getStatus();
default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
} }
SmsTemplateAuditStatusEnum auditStatus;
Assert.notNull(templateStatus.getStatusCode(),
StrUtil.format("短信模版审核状态为 null模版 id{}", templateStatus.getTemplateId()));
switch (templateStatus.getStatusCode().intValue()) {
case -1:
auditStatus = SmsTemplateAuditStatusEnum.FAIL;
break;
case 0:
auditStatus = SmsTemplateAuditStatusEnum.SUCCESS;
break;
case 1:
auditStatus = SmsTemplateAuditStatusEnum.CHECKING;
break;
default:
throw new IllegalStateException(StrUtil.format("不能解析短信模版审核状态{},模版 id{}",
templateStatus.getStatusCode(), templateStatus.getTemplateId()));
}
SmsTemplateRespDTO data = new SmsTemplateRespDTO();
data.setId(String.valueOf(templateStatus.getTemplateId())).setContent(templateStatus.getTemplateContent());
data.setAuditStatus(auditStatus.getStatus()).setAuditReason(templateStatus.getReviewReply());
return data;
}
/**
*
* @param apiTemplateId api id
* @return
*/
private DescribeSmsTemplateListRequest buildSmsTemplateStatusRequest(String apiTemplateId) {
DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest();
request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)});
// 地区 0表示国内短信。1表示国际/港澳台短信。
request.setInternational(INTERNATIONAL);
return request;
}
/**
* SDK
*
* @param request
* @return
* @throws TencentCloudSDKException SDK
*/
private DescribeSmsTemplateListResponse doGetSmsTemplate0(DescribeSmsTemplateListRequest request) throws TencentCloudSDKException {
return client.DescribeSmsTemplateList(request);
}
<Q, P, R> SmsCommonResult<R> invoke(Supplier<Q> requestSupplier,
SdkFunction<Q, P> responseSupplier,
Function<P, SmsCommonResult<R>> resultGen) {
// 构建请求body
Q request = requestSupplier.get();
P response;
// 调用腾讯云发送短信
try {
response = responseSupplier.apply(request);
} catch (TencentCloudSDKException e) {
// 调用异常,封装结果
return SmsCommonResult.build(e.getErrorCode(), e.getMessage(), e.getRequestId(), null, codeMapping);
}
return resultGen.apply(response);
} }
@Data @Data
@ -278,7 +198,7 @@ public class TencentSmsClient extends AbstractSmsClient {
private String serialNo; private String serialNo;
/** /**
* session SessionContext * session SessionContext
*/ */
@JsonProperty("ext") @JsonProperty("ext")
private SessionContext sessionContext; private SessionContext sessionContext;
@ -293,10 +213,7 @@ public class TencentSmsClient extends AbstractSmsClient {
* id * id
*/ */
private Long logId; private Long logId;
}
private interface SdkFunction<T, R> {
R apply(T t) throws TencentCloudSDKException;
} }
} }

View File

@ -1,50 +0,0 @@
package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
import cn.iocoder.yudao.framework.common.exception.ErrorCode;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import static cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.*;
/**
* SmsCodeMapping
*
* https://cloud.tencent.com/document/api/382/52075#.E5.85.AC.E5.85.B1.E9.94.99.E8.AF.AF.E7.A0.81
*
* @author : shiwp
*/
public class TencentSmsCodeMapping implements SmsCodeMapping {
@Override
public ErrorCode apply(String apiCode) {
switch (apiCode) {
case TencentSmsClient.API_SUCCESS_CODE: return GlobalErrorCodeConstants.SUCCESS;
case "FailedOperation.ContainSensitiveWord": return SMS_SEND_CONTENT_INVALID;
case "FailedOperation.JsonParseFail":
case "MissingParameter.EmptyPhoneNumberSet":
case "LimitExceeded.PhoneNumberCountLimit":
case "FailedOperation.FailResolvePacket": return GlobalErrorCodeConstants.BAD_REQUEST;
case "FailedOperation.InsufficientBalanceInSmsPackage": return SMS_ACCOUNT_MONEY_NOT_ENOUGH;
case "FailedOperation.MarketingSendTimeConstraint": return SMS_SEND_MARKET_LIMIT_CONTROL;
case "FailedOperation.PhoneNumberInBlacklist": return SMS_MOBILE_BLACK;
case "FailedOperation.SignatureIncorrectOrUnapproved": return SMS_SIGN_INVALID;
case "FailedOperation.MissingTemplateToModify":
case "FailedOperation.TemplateIncorrectOrUnapproved": return SMS_TEMPLATE_INVALID;
case "InvalidParameterValue.IncorrectPhoneNumber": return SMS_MOBILE_INVALID;
case "InvalidParameterValue.SdkAppIdNotExist": return SMS_APP_ID_INVALID;
case "InvalidParameterValue.TemplateParameterLengthLimit":
case "InvalidParameterValue.TemplateParameterFormatError": return SMS_TEMPLATE_PARAM_ERROR;
case "LimitExceeded.PhoneNumberDailyLimit": return SMS_SEND_DAY_LIMIT_CONTROL;
case "LimitExceeded.PhoneNumberThirtySecondLimit":
case "LimitExceeded.PhoneNumberOneHourLimit": return SMS_SEND_BUSINESS_LIMIT_CONTROL;
case "UnauthorizedOperation.RequestPermissionDeny":
case "FailedOperation.ForbidAddMarketingTemplates":
case "FailedOperation.NotEnterpriseCertification":
case "UnauthorizedOperation.IndividualUserMarketingSmsPermissionDeny": return SMS_PERMISSION_DENY;
case "UnauthorizedOperation.RequestIpNotInWhitelist": return SMS_IP_DENY;
case "AuthFailure.SecretIdNotFound": return SMS_ACCOUNT_INVALID;
}
return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
}
}

View File

@ -1,26 +1,20 @@
package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun; package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
import cn.hutool.core.util.ReflectUtil; 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.core.KeyValue;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
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.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; 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.dto.SmsTemplateRespDTO;
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.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import com.aliyuncs.AcsRequest;
import com.aliyuncs.IAcsClient; import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest; import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse; import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher; import org.mockito.ArgumentMatcher;
import org.mockito.InjectMocks; import org.mockito.InjectMocks;
@ -28,12 +22,10 @@ import org.mockito.Mock;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.List; import java.util.List;
import java.util.function.Function;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -67,8 +59,7 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
} }
@Test @Test
@SuppressWarnings("unchecked") public void tesSendSms_success() throws Throwable {
public void testDoSendSms() throws ClientException {
// 准备参数 // 准备参数
Long sendLogId = randomLongId(); Long sendLogId = randomLongId();
String mobile = randomString(); String mobile = randomString();
@ -87,20 +78,47 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
}))).thenReturn(response); }))).thenReturn(response);
// 调用 // 调用
SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile, SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
apiTemplateId, templateParams); apiTemplateId, templateParams);
// 断言 // 断言
assertTrue(result.getSuccess());
assertEquals(response.getRequestId(), result.getApiRequestId());
assertEquals(response.getCode(), result.getApiCode()); assertEquals(response.getCode(), result.getApiCode());
assertEquals(response.getMessage(), result.getApiMsg()); assertEquals(response.getMessage(), result.getApiMsg());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); assertEquals(response.getBizId(), result.getSerialNo());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
assertEquals(response.getRequestId(), result.getApiRequestId());
// 断言结果
assertEquals(response.getBizId(), result.getData().getSerialNo());
} }
@Test @Test
public void testDoTParseSmsReceiveStatus() throws Throwable { public void tesSendSms_fail() 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 方法
SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> o.setCode("ERROR"));
when(client.getAcsResponse(argThat((ArgumentMatcher<SendSmsRequest>) acsRequest -> {
assertEquals(mobile, acsRequest.getPhoneNumbers());
assertEquals(properties.getSignature(), acsRequest.getSignName());
assertEquals(apiTemplateId, acsRequest.getTemplateCode());
assertEquals(toJsonString(MapUtils.convertMap(templateParams)), acsRequest.getTemplateParam());
assertEquals(sendLogId.toString(), acsRequest.getOutId());
return true;
}))).thenReturn(response);
// 调用
SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
// 断言
assertFalse(result.getSuccess());
assertEquals(response.getRequestId(), result.getApiRequestId());
assertEquals(response.getCode(), result.getApiCode());
assertEquals(response.getMessage(), result.getApiMsg());
assertEquals(response.getBizId(), result.getSerialNo());
}
@Test
public void testParseSmsReceiveStatus() {
// 准备参数 // 准备参数
String text = "[\n" + String text = "[\n" +
" {\n" + " {\n" +
@ -118,20 +136,21 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
// mock 方法 // mock 方法
// 调用 // 调用
List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text); List<SmsReceiveRespDTO> statuses = smsClient.parseSmsReceiveStatus(text);
// 断言 // 断言
assertEquals(1, statuses.size()); assertEquals(1, statuses.size());
assertTrue(statuses.get(0).getSuccess()); assertTrue(statuses.get(0).getSuccess());
assertEquals("DELIVERED", statuses.get(0).getErrorCode()); assertEquals("DELIVERED", statuses.get(0).getErrorCode());
assertEquals("用户接收成功", statuses.get(0).getErrorMsg()); assertEquals("用户接收成功", statuses.get(0).getErrorMsg());
assertEquals("13900000001", statuses.get(0).getMobile()); assertEquals("13900000001", statuses.get(0).getMobile());
assertEquals(LocalDateTime.of(2017, 2, 2, 22, 23, 24), statuses.get(0).getReceiveTime()); assertEquals(LocalDateTime.of(2017, 2, 2, 22, 23, 24),
statuses.get(0).getReceiveTime());
assertEquals("12345", statuses.get(0).getSerialNo()); assertEquals("12345", statuses.get(0).getSerialNo());
assertEquals(67890L, statuses.get(0).getLogId()); assertEquals(67890L, statuses.get(0).getLogId());
} }
@Test @Test
public void testDoGetSmsTemplate() throws ClientException { public void testGetSmsTemplate() throws Throwable {
// 准备参数 // 准备参数
String apiTemplateId = randomString(); String apiTemplateId = randomString();
// mock 方法 // mock 方法
@ -145,18 +164,12 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
}))).thenReturn(response); }))).thenReturn(response);
// 调用 // 调用
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId); SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId);
// 断言 // 断言
assertEquals(response.getCode(), result.getApiCode()); assertEquals(response.getTemplateCode(), result.getId());
assertEquals(response.getMessage(), result.getApiMsg()); assertEquals(response.getTemplateContent(), result.getContent());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg()); assertEquals(response.getReason(), result.getAuditReason());
assertEquals(response.getRequestId(), result.getApiRequestId());
// 断言结果
assertEquals(response.getTemplateCode(), result.getData().getId());
assertEquals(response.getTemplateContent(), result.getData().getContent());
assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
assertEquals(response.getReason(), result.getData().getAuditReason());
} }
@Test @Test
@ -171,55 +184,4 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
"未知审核状态(3)"); "未知审核状态(3)");
} }
@Test
@SuppressWarnings("unchecked")
public void testInvoke_throwable() throws ClientException {
// 准备参数
QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
// mock 方法
ClientException ex = new ClientException("isv.INVALID_PARAMETERS", "参数不正确", randomString());
when(client.getAcsResponse(any(AcsRequest.class))).thenThrow(ex);
// 调用,并断言异常
SmsCommonResult<?> result = smsClient.invoke(request, null);
// 断言
assertEquals(ex.getErrCode(), result.getApiCode());
assertEquals(ex.getErrMsg(), result.getApiMsg());
Assertions.assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR.getCode(), result.getCode());
Assertions.assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR.getMsg(), result.getMsg());
assertEquals(ex.getRequestId(), result.getApiRequestId());
}
@Test
public void testInvoke_success() throws ClientException {
// 准备参数
QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
Function<QuerySmsTemplateResponse, SmsTemplateRespDTO> responseConsumer = response -> {
SmsTemplateRespDTO data = new SmsTemplateRespDTO();
data.setId(response.getTemplateCode()).setContent(response.getTemplateContent());
data.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(response.getReason());
return data;
};
// mock 方法
QuerySmsTemplateResponse response = randomPojo(QuerySmsTemplateResponse.class, o -> {
o.setCode("OK");
o.setTemplateStatus(1); // 设置模板通过
});
when(client.getAcsResponse(any(AcsRequest.class))).thenReturn(response);
// 调用
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.invoke(request, responseConsumer);
// 断言
assertEquals(response.getCode(), result.getApiCode());
assertEquals(response.getMessage(), result.getApiMsg());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
assertEquals(response.getRequestId(), result.getApiRequestId());
// 断言结果
assertEquals(response.getTemplateCode(), result.getData().getId());
assertEquals(response.getTemplateContent(), result.getData().getContent());
assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
assertEquals(response.getReason(), result.getData().getAuditReason());
}
} }

View File

@ -1,43 +0,0 @@
package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
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.Test;
import org.mockito.InjectMocks;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link AliyunSmsCodeMapping}
*
* @author
*/
public class AliyunSmsCodeMappingTest extends BaseMockitoUnitTest {
@InjectMocks
private AliyunSmsCodeMapping codeMapping;
@Test
public void testApply() {
assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply("OK"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("MissingAccessKeyId"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("isv.ACCOUNT_NOT_EXISTS"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("isv.ACCOUNT_ABNORMAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL, codeMapping.apply("isv.DAY_LIMIT_CONTROL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID, codeMapping.apply("isv.SMS_CONTENT_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SMS_SIGN_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SIGN_NAME_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("isp.RAM_PERMISSION_DENY"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("isv.OUT_OF_SERVICE"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("isv.AMOUNT_NOT_ENOUGH"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("isv.SMS_TEMPLATE_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SMS_SIGNATURE_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply("isv.INVALID_PARAMETERS"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply("isv.INVALID_JSON_PARAM"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID, codeMapping.apply("isv.MOBILE_NUMBER_ILLEGAL"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("isv.TEMPLATE_MISSING_PARAMETERS"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("isv.BUSINESS_LIMIT_CONTROL"));
}
}

View File

@ -1,13 +1,10 @@
package cn.iocoder.yudao.framework.sms.core.client.impl.tencent; package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils; 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.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; 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.dto.SmsTemplateRespDTO;
@ -31,7 +28,6 @@ import java.util.List;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -78,7 +74,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
} }
@Test @Test
public void testDoSendSms() throws Throwable { public void testDoSendSms_success() throws Throwable {
// 准备参数 // 准备参数
Long sendLogId = randomLongId(); Long sendLogId = randomLongId();
String mobile = randomString(); String mobile = randomString();
@ -94,7 +90,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(TencentSmsClient.API_SUCCESS_CODE); sendStatus.setCode(TencentSmsClient.API_CODE_SUCCESS);
sendStatus.setMessage("send success"); sendStatus.setMessage("send success");
sendStatus.setSerialNo(serialNo); sendStatus.setSerialNo(serialNo);
}); });
@ -109,20 +105,58 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
}))).thenReturn(response); }))).thenReturn(response);
// 调用 // 调用
SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile, SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
apiTemplateId, templateParams);
// 断言 // 断言
assertTrue(result.getSuccess());
assertEquals(response.getRequestId(), result.getApiRequestId());
assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode()); assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode());
assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg()); assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
assertEquals(response.getRequestId(), result.getApiRequestId());
// 断言结果
assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getData().getSerialNo());
} }
@Test @Test
public void testDoTParseSmsReceiveStatus() throws Throwable { public void testDoSendSms_fail() throws Throwable {
// 准备参数
Long sendLogId = randomLongId();
String mobile = randomString();
String apiTemplateId = randomString();
List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
String requestId = randomString();
String serialNo = randomString();
// mock 方法
SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> {
o.setRequestId(requestId);
SendStatus[] sendStatuses = new SendStatus[1];
o.setSendStatusSet(sendStatuses);
SendStatus sendStatus = new SendStatus();
sendStatuses[0] = sendStatus;
sendStatus.setCode("ERROR");
sendStatus.setMessage("send success");
sendStatus.setSerialNo(serialNo);
});
when(client.SendSms(argThat(request -> {
assertEquals(mobile, request.getPhoneNumberSet()[0]);
assertEquals(properties.getSignature(), request.getSignName());
assertEquals(apiTemplateId, request.getTemplateId());
assertEquals(toJsonString(ArrayUtils.toArray(new ArrayList<>(MapUtils.convertMap(templateParams).values()), String::valueOf)),
toJsonString(request.getTemplateParamSet()));
assertEquals(sendLogId, ReflectUtil.getFieldValue(JsonUtils.parseObject(request.getSessionContext(), TencentSmsClient.SessionContext.class), "logId"));
return true;
}))).thenReturn(response);
// 调用
SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
// 断言
assertFalse(result.getSuccess());
assertEquals(response.getRequestId(), result.getApiRequestId());
assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode());
assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg());
assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo());
}
@Test
public void testParseSmsReceiveStatus() {
// 准备参数 // 准备参数
String text = "[\n" + String text = "[\n" +
" {\n" + " {\n" +
@ -139,7 +173,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
// mock 方法 // mock 方法
// 调用 // 调用
List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text); List<SmsReceiveRespDTO> statuses = smsClient.parseSmsReceiveStatus(text);
// 断言 // 断言
assertEquals(1, statuses.size()); assertEquals(1, statuses.size());
assertTrue(statuses.get(0).getSuccess()); assertTrue(statuses.get(0).getSuccess());
@ -152,7 +186,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
} }
@Test @Test
public void testDoGetSmsTemplate() throws Throwable { public void testGetSmsTemplate() throws Throwable {
// 准备参数 // 准备参数
Long apiTemplateId = randomLongId(); Long apiTemplateId = randomLongId();
String requestId = randomString(); String requestId = randomString();
@ -173,50 +207,24 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
}))).thenReturn(response); }))).thenReturn(response);
// 调用 // 调用
SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId.toString()); SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId.toString());
// 断言 // 断言
assertEquals(TencentSmsClient.API_SUCCESS_CODE, result.getApiCode()); assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateId().toString(), result.getId());
assertNull(result.getApiMsg()); assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateContent(), result.getContent());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus());
assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg()); assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getAuditReason());
assertEquals(response.getRequestId(), result.getApiRequestId());
// 断言结果
assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateId().toString(), result.getData().getId());
assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateContent(), result.getData().getContent());
assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getData().getAuditReason());
} }
@Test @Test
public void testConvertSuccessTemplateStatus() { public void testConvertSmsTemplateAuditStatus() {
testTemplateStatus(SmsTemplateAuditStatusEnum.SUCCESS, 0L); assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(),
} smsClient.convertSmsTemplateAuditStatus(0));
assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(),
@Test smsClient.convertSmsTemplateAuditStatus(1));
public void testConvertCheckingTemplateStatus() { assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(),
testTemplateStatus(SmsTemplateAuditStatusEnum.CHECKING, 1L); smsClient.convertSmsTemplateAuditStatus(-1));
} assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3),
"未知审核状态(3)");
@Test
public void testConvertFailTemplateStatus() {
testTemplateStatus(SmsTemplateAuditStatusEnum.FAIL, -1L);
}
@Test
public void testConvertUnknownTemplateStatus() {
DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
templateStatus.setStatusCode(3L);
Long templateId = randomLongId();
// 调用,并断言结果
assertThrows(IllegalStateException.class, () -> smsClient.convertTemplateStatusDTO(templateStatus),
StrUtil.format("不能解析短信模版审核状态[3]模版id[{}]", templateId));
}
private void testTemplateStatus(SmsTemplateAuditStatusEnum expected, Long value) {
DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
templateStatus.setStatusCode(value);
SmsTemplateRespDTO result = smsClient.convertTemplateStatusDTO(templateStatus);
assertEquals(expected.getStatus(), result.getAuditStatus());
} }
} }

View File

@ -1,50 +0,0 @@
package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* {@link TencentSmsCodeMapping}
*
* @author : shiwp
*/
public class TencentSmsCodeMappingTest extends BaseMockitoUnitTest {
@InjectMocks
private TencentSmsCodeMapping codeMapping;
@Test
public void testApply() {
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"));
assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("LimitExceeded.PhoneNumberCountLimit"));
assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("FailedOperation.FailResolvePacket"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("FailedOperation.InsufficientBalanceInSmsPackage"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_MARKET_LIMIT_CONTROL, codeMapping.apply("FailedOperation.MarketingSendTimeConstraint"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_BLACK, codeMapping.apply("FailedOperation.PhoneNumberInBlacklist"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("FailedOperation.SignatureIncorrectOrUnapproved"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("FailedOperation.MissingTemplateToModify"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("FailedOperation.TemplateIncorrectOrUnapproved"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID, codeMapping.apply("InvalidParameterValue.IncorrectPhoneNumber"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_APP_ID_INVALID, codeMapping.apply("InvalidParameterValue.SdkAppIdNotExist"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("InvalidParameterValue.TemplateParameterLengthLimit"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("InvalidParameterValue.TemplateParameterFormatError"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL, codeMapping.apply("LimitExceeded.PhoneNumberDailyLimit"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("LimitExceeded.PhoneNumberThirtySecondLimit"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("LimitExceeded.PhoneNumberOneHourLimit"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("UnauthorizedOperation.RequestPermissionDeny"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("FailedOperation.ForbidAddMarketingTemplates"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("FailedOperation.NotEnterpriseCertification"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("UnauthorizedOperation.IndividualUserMarketingSmsPermissionDeny"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_IP_DENY, codeMapping.apply("UnauthorizedOperation.RequestIpNotInWhitelist"));
assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("AuthFailure.SecretIdNotFound"));
}
}

View File

@ -82,6 +82,10 @@ public interface ErrorCodeConstants {
// ========== 短信模板 1-002-012-000 ========== // ========== 短信模板 1-002-012-000 ==========
ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在"); ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在");
ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_012_001, "已经存在编码为【{}】的短信模板"); ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_012_001, "已经存在编码为【{}】的短信模板");
ErrorCode SMS_TEMPLATE_API_ERROR = new ErrorCode(1_002_012_002, "短信 API 模板调用失败,原因是:{}");
ErrorCode SMS_TEMPLATE_API_AUDIT_CHECKING = new ErrorCode(1_002_012_003, "短信 API 模版无法使用,原因:审批中");
ErrorCode SMS_TEMPLATE_API_AUDIT_FAIL = new ErrorCode(1_002_012_004, "短信 API 模版无法使用,原因:审批不通过,{}");
ErrorCode SMS_TEMPLATE_API_NOT_FOUND = new ErrorCode(1_002_012_005, "短信 API 模版无法使用,原因:模版不存在");
// ========== 短信发送 1-002-013-000 ========== // ========== 短信发送 1-002-013-000 ==========
ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在"); ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在");

View File

@ -63,12 +63,6 @@ public class SmsLogExcelVO {
@ExcelProperty("发送时间") @ExcelProperty("发送时间")
private LocalDateTime sendTime; private LocalDateTime sendTime;
@ExcelProperty("发送结果的编码")
private Integer sendCode;
@ExcelProperty("发送结果的提示")
private String sendMsg;
@ExcelProperty("短信 API 发送结果的编码") @ExcelProperty("短信 API 发送结果的编码")
private String apiSendCode; private String apiSendCode;

View File

@ -52,12 +52,6 @@ public class SmsLogRespVO {
@Schema(description = "发送时间") @Schema(description = "发送时间")
private LocalDateTime sendTime; private LocalDateTime sendTime;
@Schema(description = "发送结果的编码", example = "0")
private Integer sendCode;
@Schema(description = "发送结果的提示", example = "成功")
private String sendMsg;
@Schema(description = "短信 API 发送结果的编码", example = "SUCCESS") @Schema(description = "短信 API 发送结果的编码", example = "SUCCESS")
private String apiSendCode; private String apiSendCode;

View File

@ -1,10 +1,9 @@
package cn.iocoder.yudao.module.system.dal.dataobject.sms; package cn.iocoder.yudao.module.system.dal.dataobject.sms;
import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants; import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence; import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.annotation.TableName;
@ -115,19 +114,6 @@ public class SmsLogDO extends BaseDO {
* *
*/ */
private LocalDateTime sendTime; private LocalDateTime sendTime;
/**
*
*
* {@link SmsFrameworkErrorCodeConstants}
*/
private Integer sendCode;
/**
*
*
* 使 {@link SmsFrameworkErrorCodeConstants}
* Exception
*/
private String sendMsg;
/** /**
* API * API
* *

View File

@ -1,9 +1,9 @@
package cn.iocoder.yudao.module.system.service.sms; package cn.iocoder.yudao.module.system.service.sms;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import java.time.LocalDateTime; import java.time.LocalDateTime;
@ -37,15 +37,15 @@ public interface SmsLogService {
* *
* *
* @param id * @param id
* @param sendCode * @param success
* @param sendMsg
* @param apiSendCode API * @param apiSendCode API
* @param apiSendMsg API * @param apiSendMsg API
* @param apiRequestId API ID * @param apiRequestId API ID
* @param apiSerialNo API * @param apiSerialNo API
*/ */
void updateSmsSendResult(Long id, Integer sendCode, String sendMsg, void updateSmsSendResult(Long id, Boolean success,
String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo); String apiSendCode, String apiSendMsg,
String apiRequestId, String apiSerialNo);
/** /**
* *
@ -56,7 +56,8 @@ public interface SmsLogService {
* @param apiReceiveCode API * @param apiReceiveCode API
* @param apiReceiveMsg API * @param apiReceiveMsg API
*/ */
void updateSmsReceiveResult(Long id, Boolean success, LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg); void updateSmsReceiveResult(Long id, Boolean success,
LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg);
/** /**
* *

View File

@ -1,12 +1,11 @@
package cn.iocoder.yudao.module.system.service.sms; package cn.iocoder.yudao.module.system.service.sms;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsLogMapper; import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsLogMapper;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum;
import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum; import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -55,13 +54,12 @@ public class SmsLogServiceImpl implements SmsLogService {
} }
@Override @Override
public void updateSmsSendResult(Long id, Integer sendCode, String sendMsg, public void updateSmsSendResult(Long id, Boolean success,
String apiSendCode, String apiSendMsg, String apiSendCode, String apiSendMsg,
String apiRequestId, String apiSerialNo) { String apiRequestId, String apiSerialNo) {
SmsSendStatusEnum sendStatus = CommonResult.isSuccess(sendCode) ? SmsSendStatusEnum sendStatus = success ? SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE;
SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE; smsLogMapper.updateById(SmsLogDO.builder().id(id)
smsLogMapper.updateById(SmsLogDO.builder().id(id).sendStatus(sendStatus.getStatus()) .sendStatus(sendStatus.getStatus()).sendTime(LocalDateTime.now())
.sendTime(LocalDateTime.now()).sendCode(sendCode).sendMsg(sendMsg)
.apiSendCode(apiSendCode).apiSendMsg(apiSendMsg) .apiSendCode(apiSendCode).apiSendMsg(apiSendMsg)
.apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build()); .apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build());
} }

View File

@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.system.service.sms; package cn.iocoder.yudao.module.system.service.sms;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.core.KeyValue; import cn.iocoder.yudao.framework.common.core.KeyValue;
@ -8,7 +9,6 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.sms.core.client.SmsClient; import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
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.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
import cn.iocoder.yudao.module.system.service.member.MemberService; import cn.iocoder.yudao.module.system.service.member.MemberService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService; import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -35,6 +36,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
* @author * @author
*/ */
@Service @Service
@Slf4j
public class SmsSendServiceImpl implements SmsSendService { public class SmsSendServiceImpl implements SmsSendService {
@Resource @Resource
@ -158,11 +160,17 @@ public class SmsSendServiceImpl implements SmsSendService {
SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId()); SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId());
Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId()); Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId());
// 发送短信 // 发送短信
SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(), try {
message.getApiTemplateId(), message.getTemplateParams()); SmsSendRespDTO sendResponse = smsClient.sendSms(message.getLogId(), message.getMobile(),
smsLogService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(), message.getApiTemplateId(), message.getTemplateParams());
sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(), smsLogService.updateSmsSendResult(message.getLogId(), sendResponse.getSuccess(),
sendResult.getData() != null ? sendResult.getData().getSerialNo() : null); sendResponse.getApiCode(), sendResponse.getApiMsg(),
sendResponse.getApiRequestId(), sendResponse.getSerialNo());
} catch (Throwable ex) {
log.error("[doSendSms][发送短信异常,日志编号({})]", message.getLogId(), ex);
smsLogService.updateSmsSendResult(message.getLogId(), false,
"EXCEPTION", ExceptionUtil.getRootCauseMessage(ex), null, null);
}
} }
@Override @Override

View File

@ -1,11 +1,11 @@
package cn.iocoder.yudao.module.system.service.sms; package cn.iocoder.yudao.module.system.service.sms;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateUpdateReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateUpdateReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO; import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.List; import java.util.List;

View File

@ -1,12 +1,14 @@
package cn.iocoder.yudao.module.system.service.sms; package cn.iocoder.yudao.module.system.service.sms;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ReUtil; import cn.hutool.core.util.ReUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.sms.core.client.SmsClient; import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
@ -21,11 +23,11 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable; import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -171,9 +173,24 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
// 获得短信模板 // 获得短信模板
SmsClient smsClient = smsChannelService.getSmsClient(channelId); SmsClient smsClient = smsChannelService.getSmsClient(channelId);
Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId)); Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId));
SmsCommonResult<SmsTemplateRespDTO> templateResult = smsClient.getSmsTemplate(apiTemplateId); SmsTemplateRespDTO template;
// 校验短信模板是否正确 try {
templateResult.checkError(); template = smsClient.getSmsTemplate(apiTemplateId);
} catch (Throwable ex) {
throw exception(SMS_TEMPLATE_API_ERROR, ExceptionUtil.getRootCauseMessage(ex));
}
// 校验短信模版
if (template == null) {
throw exception(SMS_TEMPLATE_API_NOT_FOUND);
}
if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.CHECKING.getStatus())) {
throw exception(SMS_TEMPLATE_API_AUDIT_CHECKING);
}
if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.FAIL.getStatus())) {
throw exception(SMS_TEMPLATE_API_AUDIT_FAIL, template.getAuditReason());
}
Assert.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.SUCCESS.getStatus(),
String.format("短信模板(%s) 审核状态(%d) 不正确", apiTemplateId, template.getAuditStatus()));
} }
@Override @Override

View File

@ -164,31 +164,31 @@ public class SmsChannelServiceTest extends BaseDbUnitTest {
@Test @Test
public void testGetSmsChannelPage() { public void testGetSmsChannelPage() {
// mock 数据 // mock 数据
SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class, o -> { // 等会查询到 SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class, o -> { // 等会查询到
o.setSignature("芋道源码"); o.setSignature("芋道源码");
o.setStatus(CommonStatusEnum.ENABLE.getStatus()); o.setStatus(CommonStatusEnum.ENABLE.getStatus());
o.setCreateTime(buildTime(2020, 12, 12)); o.setCreateTime(buildTime(2020, 12, 12));
}); });
smsChannelMapper.insert(dbSmsChannel); smsChannelMapper.insert(dbSmsChannel);
// 测试 signature 不匹配 // 测试 signature 不匹配
smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setSignature("源码"))); smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setSignature("源码")));
// 测试 status 不匹配 // 测试 status 不匹配
smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus())));
// 测试 createTime 不匹配 // 测试 createTime 不匹配
smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11)))); smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11))));
// 准备参数 // 准备参数
SmsChannelPageReqVO reqVO = new SmsChannelPageReqVO(); SmsChannelPageReqVO reqVO = new SmsChannelPageReqVO();
reqVO.setSignature("芋道"); reqVO.setSignature("芋道");
reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus());
reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24));
// 调用 // 调用
PageResult<SmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(reqVO); PageResult<SmsChannelDO> pageResult = smsChannelService.getSmsChannelPage(reqVO);
// 断言 // 断言
assertEquals(1, pageResult.getTotal()); assertEquals(1, pageResult.getTotal());
assertEquals(1, pageResult.getList().size()); assertEquals(1, pageResult.getList().size());
assertPojoEquals(dbSmsChannel, pageResult.getList().get(0)); assertPojoEquals(dbSmsChannel, pageResult.getList().get(0));
} }
@Test @Test

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.service.sms;
import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
@ -172,22 +171,20 @@ public class SmsLogServiceImplTest extends BaseDbUnitTest {
smsLogMapper.insert(dbSmsLog); smsLogMapper.insert(dbSmsLog);
// 准备参数 // 准备参数
Long id = dbSmsLog.getId(); Long id = dbSmsLog.getId();
Integer sendCode = randomInteger(); Boolean success = randomBoolean();
String sendMsg = randomString();
String apiSendCode = randomString(); String apiSendCode = randomString();
String apiSendMsg = randomString(); String apiSendMsg = randomString();
String apiRequestId = randomString(); String apiRequestId = randomString();
String apiSerialNo = randomString(); String apiSerialNo = randomString();
// 调用 // 调用
smsLogService.updateSmsSendResult(id, sendCode, sendMsg, smsLogService.updateSmsSendResult(id, success,
apiSendCode, apiSendMsg, apiRequestId, apiSerialNo); apiSendCode, apiSendMsg, apiRequestId, apiSerialNo);
// 断言 // 断言
dbSmsLog = smsLogMapper.selectById(id); dbSmsLog = smsLogMapper.selectById(id);
assertEquals(CommonResult.isSuccess(sendCode) ? SmsSendStatusEnum.SUCCESS.getStatus() assertEquals(success ? SmsSendStatusEnum.SUCCESS.getStatus() : SmsSendStatusEnum.FAILURE.getStatus(),
: SmsSendStatusEnum.FAILURE.getStatus(), dbSmsLog.getSendStatus()); dbSmsLog.getSendStatus());
assertNotNull(dbSmsLog.getSendTime()); assertNotNull(dbSmsLog.getSendTime());
assertEquals(sendMsg, dbSmsLog.getSendMsg());
assertEquals(apiSendCode, dbSmsLog.getApiSendCode()); assertEquals(apiSendCode, dbSmsLog.getApiSendCode());
assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg()); assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg());
assertEquals(apiRequestId, dbSmsLog.getApiRequestId()); assertEquals(apiRequestId, dbSmsLog.getApiRequestId());

View File

@ -5,7 +5,6 @@ import cn.iocoder.yudao.framework.common.core.KeyValue;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.sms.core.client.SmsClient; import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
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.SmsReceiveRespDTO;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
@ -244,15 +243,14 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testDoSendSms() { public void testDoSendSms() throws Throwable {
// 准备参数 // 准备参数
SmsSendMessage message = randomPojo(SmsSendMessage.class); SmsSendMessage message = randomPojo(SmsSendMessage.class);
// mock SmsClientFactory 的方法 // mock SmsClientFactory 的方法
SmsClient smsClient = spy(SmsClient.class); SmsClient smsClient = spy(SmsClient.class);
when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient); when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
// mock SmsClient 的方法 // mock SmsClient 的方法
SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class); SmsSendRespDTO sendResult = randomPojo(SmsSendRespDTO.class);
sendResult.setData(randomPojo(SmsSendRespDTO.class));
when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()), when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()),
eq(message.getTemplateParams()))).thenReturn(sendResult); eq(message.getTemplateParams()))).thenReturn(sendResult);
@ -260,8 +258,8 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
smsService.doSendSms(message); smsService.doSendSms(message);
// 断言 // 断言
verify(smsLogService).updateSmsSendResult(eq(message.getLogId()), verify(smsLogService).updateSmsSendResult(eq(message.getLogId()),
eq(sendResult.getCode()), eq(sendResult.getMsg()), eq(sendResult.getApiCode()), eq(sendResult.getSuccess()), eq(sendResult.getApiCode()),
eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getData().getSerialNo())); eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getSerialNo()));
} }
@Test @Test

View File

@ -1,13 +1,12 @@
package cn.iocoder.yudao.module.system.service.sms; package cn.iocoder.yudao.module.system.service.sms;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.sms.core.client.SmsClient; import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO;
import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
@ -65,7 +64,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testCreateSmsTemplate_success() { public void testCreateSmsTemplate_success() throws Throwable {
// 准备参数 // 准备参数
SmsTemplateCreateReqVO reqVO = randomPojo(SmsTemplateCreateReqVO.class, o -> { SmsTemplateCreateReqVO reqVO = randomPojo(SmsTemplateCreateReqVO.class, o -> {
o.setContent("正在进行登录操作{operation},您的验证码是{code}"); o.setContent("正在进行登录操作{operation},您的验证码是{code}");
@ -80,8 +79,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO);
// mock 获得 API 短信模板成功 // mock 获得 API 短信模板成功
when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient);
when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class, when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(
o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode()))); randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus())));
// 调用 // 调用
Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO); Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO);
@ -96,7 +95,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void testUpdateSmsTemplate_success() { public void testUpdateSmsTemplate_success() throws Throwable {
// mock 数据 // mock 数据
SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
@ -115,8 +114,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO);
// mock 获得 API 短信模板成功 // mock 获得 API 短信模板成功
when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient);
when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class, when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(
o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode()))); randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus())));
// 调用 // 调用
smsTemplateService.updateSmsTemplate(reqVO); smsTemplateService.updateSmsTemplate(reqVO);