From 562f82580e3b3dca1b81c14e999d0c214faaf4a8 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 21 Nov 2023 23:32:26 +0800 Subject: [PATCH] =?UTF-8?q?sms=EF=BC=9A=E7=A7=BB=E9=99=A4=20SmsCodeMapping?= =?UTF-8?q?=20+=20SmsCommonResult=EF=BC=8C=E7=AE=80=E5=8C=96=E7=9F=AD?= =?UTF-8?q?=E4=BF=A1=E7=9A=84=E5=B0=81=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/sms/core/client/SmsClient.java | 6 +- .../sms/core/client/SmsCodeMapping.java | 17 -- .../sms/core/client/SmsCommonResult.java | 68 ------ .../sms/core/client/dto/SmsSendRespDTO.java | 25 ++ .../core/client/impl/AbstractSmsClient.java | 78 +------ .../client/impl/aliyun/AliyunSmsClient.java | 87 +++---- .../impl/aliyun/AliyunSmsCodeMapping.java | 42 ---- .../impl/debug/DebugDingTalkCodeMapping.java | 22 -- .../impl/debug/DebugDingTalkSmsClient.java | 26 +-- .../tencent/TencentSmsChannelProperties.java | 41 ---- .../client/impl/tencent/TencentSmsClient.java | 221 ++++++------------ .../impl/tencent/TencentSmsCodeMapping.java | 50 ---- .../impl/aliyun/AliyunSmsClientTest.java | 130 ++++------- .../impl/aliyun/AliyunSmsCodeMappingTest.java | 43 ---- .../impl/tencent/TencentSmsClientTest.java | 120 +++++----- .../tencent/TencentSmsCodeMappingTest.java | 50 ---- .../system/enums/ErrorCodeConstants.java | 4 + .../admin/sms/vo/log/SmsLogExcelVO.java | 6 - .../admin/sms/vo/log/SmsLogExportReqVO.java | 2 +- .../admin/sms/vo/log/SmsLogRespVO.java | 6 - .../system/dal/dataobject/sms/SmsLogDO.java | 18 +- .../system/service/sms/SmsLogService.java | 13 +- .../system/service/sms/SmsLogServiceImpl.java | 12 +- .../service/sms/SmsSendServiceImpl.java | 20 +- .../service/sms/SmsTemplateService.java | 2 +- .../service/sms/SmsTemplateServiceImpl.java | 27 ++- .../service/sms/SmsChannelServiceTest.java | 48 ++-- .../service/sms/SmsLogServiceImplTest.java | 11 +- .../service/sms/SmsSendServiceImplTest.java | 10 +- .../sms/SmsTemplateServiceImplTest.java | 15 +- 30 files changed, 346 insertions(+), 874 deletions(-) delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCodeMapping.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCommonResult.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkCodeMapping.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsChannelProperties.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMapping.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMappingTest.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMappingTest.java diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsClient.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsClient.java index 7411c9cd0..ef945a329 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsClient.java @@ -31,8 +31,8 @@ public interface SmsClient { * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序 * @return 短信发送结果 */ - SmsCommonResult sendSms(Long logId, String mobile, String apiTemplateId, - List> templateParams); + SmsSendRespDTO sendSms(Long logId, String mobile, String apiTemplateId, + List> templateParams) throws Throwable; /** * 解析接收短信的接收结果 @@ -49,6 +49,6 @@ public interface SmsClient { * @param apiTemplateId 短信 API 的模板编号 * @return 短信模板 */ - SmsCommonResult getSmsTemplate(String apiTemplateId); + SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable; } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCodeMapping.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCodeMapping.java deleted file mode 100644 index 826306fde..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCodeMapping.java +++ /dev/null @@ -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 { -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCommonResult.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCommonResult.java deleted file mode 100644 index d314de2cd..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCommonResult.java +++ /dev/null @@ -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 extends CommonResult { - - /** - * API 返回错误码 - * - * 由于第三方的错误码可能是字符串,所以使用 String 类型 - */ - private String apiCode; - /** - * API 返回提示 - */ - private String apiMsg; - - /** - * API 请求编号 - */ - private String apiRequestId; - - private SmsCommonResult() { - } - - public static SmsCommonResult build(String apiCode, String apiMsg, String apiRequestId, - T data, SmsCodeMapping codeMapping) { - Assert.notNull(codeMapping, "参数 codeMapping 不能为空"); - SmsCommonResult result = new SmsCommonResult().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 SmsCommonResult error(Throwable ex) { - SmsCommonResult result = new SmsCommonResult<>(); - result.setCode(SmsFrameworkErrorCodeConstants.EXCEPTION.getCode()); - result.setMsg(ExceptionUtil.getRootCauseMessage(ex)); - return result; - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsSendRespDTO.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsSendRespDTO.java index 9d320d57b..ec517508a 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsSendRespDTO.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsSendRespDTO.java @@ -10,9 +10,34 @@ import lombok.Data; @Data public class SmsSendRespDTO { + /** + * 是否成功 + */ + private Boolean success; + + /** + * API 请求编号 + */ + private String apiRequestId; + + // ==================== 成功时字段 ==================== + /** * 短信 API 发送返回的序号 */ private String serialNo; + // ==================== 失败时字段 ==================== + + /** + * API 返回错误码 + * + * 由于第三方的错误码可能是字符串,所以使用 String 类型 + */ + private String apiCode; + /** + * API 返回提示 + */ + private String apiMsg; + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/AbstractSmsClient.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/AbstractSmsClient.java index f91083ddd..42f00f968 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/AbstractSmsClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/AbstractSmsClient.java @@ -1,17 +1,9 @@ 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.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 lombok.extern.slf4j.Slf4j; -import java.util.List; - /** * 短信客户端的抽象类,提供模板方法,减少子类的冗余代码 * @@ -25,14 +17,9 @@ public abstract class AbstractSmsClient implements SmsClient { * 短信渠道配置 */ protected volatile SmsChannelProperties properties; - /** - * 错误码枚举类 - */ - protected final SmsCodeMapping codeMapping; - public AbstractSmsClient(SmsChannelProperties properties, SmsCodeMapping codeMapping) { - this.properties = prepareProperties(properties); - this.codeMapping = codeMapping; + public AbstractSmsClient(SmsChannelProperties properties) { + this.properties = properties; } /** @@ -54,74 +41,13 @@ public abstract class AbstractSmsClient implements SmsClient { return; } log.info("[refresh][配置({})发生变化,重新初始化]", properties); - this.properties = prepareProperties(properties); // 初始化 this.init(); } - /** - * 在赋值给{@link this#properties}前,子类可根据需要预处理短信渠道配置 - * - * @param properties 数据库中存储的短信渠道配置 - * @return 满足子类实现的短信渠道配置 - */ - protected SmsChannelProperties prepareProperties(SmsChannelProperties properties) { - return properties; - } - @Override public Long getId() { return properties.getId(); } - @Override - public final SmsCommonResult sendSms(Long logId, String mobile, - String apiTemplateId, List> templateParams) { - // 执行短信发送 - SmsCommonResult 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 doSendSms(Long sendLogId, String mobile, - String apiTemplateId, List> templateParams) - throws Throwable; - - @Override - public List parseSmsReceiveStatus(String text) throws Throwable { - try { - return doParseSmsReceiveStatus(text); - } catch (Throwable ex) { - log.error("[parseSmsReceiveStatus][text({}) 解析发生异常]", text, ex); - throw ex; - } - } - - protected abstract List doParseSmsReceiveStatus(String text) throws Throwable; - - @Override - public SmsCommonResult getSmsTemplate(String apiTemplateId) { - // 执行短信发送 - SmsCommonResult result; - try { - result = doGetSmsTemplate(apiTemplateId); - } catch (Throwable ex) { - // 打印异常日志 - log.error("[getSmsTemplate][获得短信模板({}) 发生异常]", apiTemplateId, ex); - // 封装返回 - return SmsCommonResult.error(ex); - } - return result; - } - - protected abstract SmsCommonResult doGetSmsTemplate(String apiTemplateId) throws Throwable; - } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java index 59723c4c9..d32a5bb11 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java @@ -1,25 +1,21 @@ package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun; 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.util.collection.MapUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient; import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; -import com.aliyuncs.AcsRequest; -import com.aliyuncs.AcsResponse; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest; +import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse; 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.IClientProfile; import com.fasterxml.jackson.annotation.JsonFormat; @@ -31,9 +27,8 @@ import lombok.extern.slf4j.Slf4j; import java.time.LocalDateTime; import java.util.List; 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.TIME_ZONE_DEFAULT; @@ -46,6 +41,11 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DE @Slf4j public class AliyunSmsClient extends AbstractSmsClient { + /** + * 调用成功 code + */ + public static final String API_CODE_SUCCESS = "OK"; + /** * REGION, 使用杭州 */ @@ -57,7 +57,7 @@ public class AliyunSmsClient extends AbstractSmsClient { private volatile IAcsClient client; public AliyunSmsClient(SmsChannelProperties properties) { - super(properties, new AliyunSmsCodeMapping()); + super(properties); Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); } @@ -69,9 +69,9 @@ public class AliyunSmsClient extends AbstractSmsClient { } @Override - protected SmsCommonResult doSendSms(Long sendLogId, String mobile, - String apiTemplateId, List> templateParams) { - // 构建参数 + public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId, + List> templateParams) throws Throwable { + // 构建请求 SendSmsRequest request = new SendSmsRequest(); request.setPhoneNumbers(mobile); request.setSignName(properties.getSignature()); @@ -79,34 +79,32 @@ public class AliyunSmsClient extends AbstractSmsClient { request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams))); 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 - protected List doParseSmsReceiveStatus(String text) throws Throwable { + public List parseSmsReceiveStatus(String text) { List statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class); - return statuses.stream().map(status -> { - SmsReceiveRespDTO resp = new SmsReceiveRespDTO(); - resp.setSuccess(status.getSuccess()); - resp.setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg()); - resp.setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime()); - resp.setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId())); - return resp; - }).collect(Collectors.toList()); + return convertList(statuses, status -> new SmsReceiveRespDTO().setSuccess(status.getSuccess()) + .setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg()) + .setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime()) + .setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId()))); } @Override - protected SmsCommonResult doGetSmsTemplate(String apiTemplateId) { - // 构建参数 + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { + // 构建请求 QuerySmsTemplateRequest request = new QuerySmsTemplateRequest(); request.setTemplateCode(apiTemplateId); // 执行请求 - return invoke(request, response -> { - SmsTemplateRespDTO data = new SmsTemplateRespDTO(); - data.setId(response.getTemplateCode()).setContent(response.getTemplateContent()); - data.setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason()); - return data; - }); + QuerySmsTemplateResponse response = client.getAcsResponse(request); + if (response.getTemplateStatus() == null) { + return null; + } + return new SmsTemplateRespDTO().setId(response.getTemplateCode()).setContent(response.getTemplateContent()) + .setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason()); } @VisibleForTesting @@ -119,37 +117,10 @@ public class AliyunSmsClient extends AbstractSmsClient { } } - @VisibleForTesting - SmsCommonResult invoke(AcsRequest request, Function 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 文档 + * 参见 文档 * * @author 芋道源码 */ diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java deleted file mode 100644 index fd188d9b2..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java +++ /dev/null @@ -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; - } - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkCodeMapping.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkCodeMapping.java deleted file mode 100644 index 0a7e48bfc..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkCodeMapping.java +++ /dev/null @@ -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; - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java index cedcfb6dc..315edf942 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkSmsClient.java @@ -8,19 +8,19 @@ import cn.hutool.crypto.digest.DigestUtil; import cn.hutool.crypto.digest.HmacAlgorithm; import cn.hutool.http.HttpUtil; 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.SmsSendRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient; import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** * 基于钉钉 WebHook 实现的调试的短信客户端实现类 @@ -32,7 +32,7 @@ import java.util.Map; public class DebugDingTalkSmsClient extends AbstractSmsClient { public DebugDingTalkSmsClient(SmsChannelProperties properties) { - super(properties, new DebugDingTalkCodeMapping()); + super(properties); Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空"); Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); } @@ -42,8 +42,8 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient { } @Override - protected SmsCommonResult doSendSms(Long sendLogId, String mobile, - String apiTemplateId, List> templateParams) throws Throwable { + public SmsSendRespDTO sendSms(Long sendLogId, String mobile, + String apiTemplateId, List> templateParams) throws Throwable { // 构建请求 String url = buildUrl("robot/send"); Map params = new HashMap<>(); @@ -55,14 +55,15 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient { String responseText = HttpUtil.post(url, JsonUtils.toJsonString(params)); // 解析结果 Map responseObj = JsonUtils.parseObject(responseText, Map.class); - return SmsCommonResult.build(MapUtil.getStr(responseObj, "errcode"), MapUtil.getStr(responseObj, "errorMsg"), - null, new SmsSendRespDTO().setSerialNo(StrUtil.uuid()), codeMapping); + String errorCode = MapUtil.getStr(responseObj, "errcode"); + 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 文档 + * 参见 文档 * * @param path 请求路径 * @return 请求地址 @@ -82,15 +83,14 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient { } @Override - protected List doParseSmsReceiveStatus(String text) throws Throwable { + public List parseSmsReceiveStatus(String text) { throw new UnsupportedOperationException("模拟短信客户端,暂时无需解析回调"); } @Override - protected SmsCommonResult doGetSmsTemplate(String apiTemplateId) { - SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("") + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) { + return new SmsTemplateRespDTO().setId(apiTemplateId).setContent("") .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(""); - return SmsCommonResult.build("0", "success", null, data, codeMapping); } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsChannelProperties.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsChannelProperties.java deleted file mode 100644 index 6b539c602..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsChannelProperties.java +++ /dev/null @@ -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; - } -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java index 743c1aed8..eeb287ffe 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClient.java @@ -4,9 +4,7 @@ import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; 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.CollectionUtils; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; @@ -17,23 +15,22 @@ import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; import com.tencentcloudapi.common.Credential; -import com.tencentcloudapi.common.exception.TencentCloudSDKException; import com.tencentcloudapi.sms.v20210111.SmsClient; import com.tencentcloudapi.sms.v20210111.models.*; import lombok.Data; import java.time.LocalDateTime; import java.util.List; -import java.util.function.Function; -import java.util.function.Supplier; +import java.util.Objects; +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.TIME_ZONE_DEFAULT; /** * 腾讯云短信功能实现 - *

- * 参见 https://cloud.tencent.com/document/product/382/52077 + * + * 参见 文档 * * @author shiwp */ @@ -42,7 +39,7 @@ public class TencentSmsClient extends AbstractSmsClient { /** * 调用成功 code */ - public static final String API_SUCCESS_CODE = "Ok"; + public static final String API_CODE_SUCCESS = "Ok"; /** * REGION,使用南京 @@ -51,180 +48,103 @@ public class TencentSmsClient extends AbstractSmsClient { /** * 是否国际/港澳台短信: + * * 0:表示国内短信。 * 1:表示国际/港澳台短信。 */ - private static final long INTERNATIONAL = 0L; + private static final long INTERNATIONAL_CHINA = 0L; private SmsClient client; public TencentSmsClient(SmsChannelProperties properties) { - super(properties, new TencentSmsCodeMapping()); + super(properties); Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空"); + validateSdkAppId(properties); } @Override protected void doInit() { // 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId,secretKey - Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret()); + Credential credential = new Credential(getApiKey(), properties.getApiSecret()); 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 - protected SmsCommonResult doSendSms(Long sendLogId, - String mobile, - String apiTemplateId, - List> 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> templateParams) { + public SmsSendRespDTO sendSms(Long sendLogId, String mobile, + String apiTemplateId, List> templateParams) throws Throwable { + // 构建请求 SendSmsRequest request = new SendSmsRequest(); - request.setSmsSdkAppId(((TencentSmsChannelProperties) properties).getSdkAppId()); + request.setSmsSdkAppId(getSdkAppId()); request.setPhoneNumberSet(new String[]{mobile}); request.setSignName(properties.getSignature()); request.setTemplateId(apiTemplateId); request.setTemplateParamSet(ArrayUtils.toArray(templateParams, e -> String.valueOf(e.getValue()))); 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 - protected List doParseSmsReceiveStatus(String text) throws Throwable { + public List parseSmsReceiveStatus(String text) { List callback = JsonUtils.parseArray(text, SmsReceiveStatus.class); - return CollectionUtils.convertList(callback, status -> { - SmsReceiveRespDTO data = new SmsReceiveRespDTO(); - data.setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription()); - data.setReceiveTime(status.getReceiveTime()).setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus())); - data.setMobile(status.getMobile()).setSerialNo(status.getSerialNo()); - SessionContext context; - Long logId; - Assert.notNull(context = status.getSessionContext(), "回执信息中未解析出 context,请联系腾讯云小助手"); - Assert.notNull(logId = context.getLogId(), "回执信息中未解析出 logId,请联系腾讯云小助手"); - data.setLogId(logId); - return data; - }); + return convertList(callback, status -> new SmsReceiveRespDTO() + .setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus())) + .setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription()) + .setMobile(status.getMobile()).setReceiveTime(status.getReceiveTime()) + .setSerialNo(status.getSerialNo()).setLogId(status.getSessionContext().getLogId())); } @Override - protected SmsCommonResult doGetSmsTemplate(String apiTemplateId) throws Throwable { - return invoke(() -> this.buildSmsTemplateStatusRequest(apiTemplateId), - this::doGetSmsTemplate0, - response -> { - SmsTemplateRespDTO data = convertTemplateStatusDTO(response.getDescribeTemplateStatusSet()[0]); - return SmsCommonResult.build(API_SUCCESS_CODE, null, response.getRequestId(), data, codeMapping); - }); + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable { + // 构建请求 + DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest(); + request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)}); + request.setInternational(INTERNATIONAL_CHINA); + // 执行请求 + 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 - SmsTemplateRespDTO convertTemplateStatusDTO(DescribeTemplateListStatus templateStatus) { - if (templateStatus == null) { - return null; + Integer convertSmsTemplateAuditStatus(int templateStatus) { + switch (templateStatus) { + 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); - } - - SmsCommonResult invoke(Supplier requestSupplier, - SdkFunction responseSupplier, - Function> 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 @@ -278,7 +198,7 @@ public class TencentSmsClient extends AbstractSmsClient { private String serialNo; /** - * 用户的 session 内容(与发送接口的请求参数SessionContext一致) + * 用户的 session 内容(与发送接口的请求参数 SessionContext 一致) */ @JsonProperty("ext") private SessionContext sessionContext; @@ -293,10 +213,7 @@ public class TencentSmsClient extends AbstractSmsClient { * 发送短信记录id */ private Long logId; - } - private interface SdkFunction { - R apply(T t) throws TencentCloudSDKException; } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMapping.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMapping.java deleted file mode 100644 index 05ad355ef..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMapping.java +++ /dev/null @@ -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; - } -} \ No newline at end of file diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java index 4b02c5121..c60cd26a1 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java @@ -1,26 +1,20 @@ package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun; import cn.hutool.core.util.ReflectUtil; -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; import cn.iocoder.yudao.framework.common.core.KeyValue; -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; +import cn.iocoder.yudao.framework.common.util.collection.MapUtils; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum; import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties; -import cn.iocoder.yudao.framework.common.util.collection.MapUtils; -import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants; -import com.aliyuncs.AcsRequest; +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; import com.aliyuncs.IAcsClient; 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.SendSmsResponse; -import com.aliyuncs.exceptions.ClientException; import com.google.common.collect.Lists; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatcher; import org.mockito.InjectMocks; @@ -28,12 +22,10 @@ import org.mockito.Mock; import java.time.LocalDateTime; 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.test.core.util.RandomUtils.*; import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.when; @@ -67,8 +59,7 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest { } @Test - @SuppressWarnings("unchecked") - public void testDoSendSms() throws ClientException { + public void tesSendSms_success() throws Throwable { // 准备参数 Long sendLogId = randomLongId(); String mobile = randomString(); @@ -87,20 +78,47 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest { }))).thenReturn(response); // 调用 - SmsCommonResult result = smsClient.doSendSms(sendLogId, mobile, + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams); // 断言 + assertTrue(result.getSuccess()); + assertEquals(response.getRequestId(), result.getApiRequestId()); 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.getBizId(), result.getData().getSerialNo()); + assertEquals(response.getBizId(), result.getSerialNo()); } @Test - public void testDoTParseSmsReceiveStatus() throws Throwable { + public void tesSendSms_fail() throws Throwable { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString(); + List> 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) 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" + " {\n" + @@ -118,20 +136,21 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest { // mock 方法 // 调用 - List statuses = smsClient.doParseSmsReceiveStatus(text); + List statuses = smsClient.parseSmsReceiveStatus(text); // 断言 assertEquals(1, statuses.size()); assertTrue(statuses.get(0).getSuccess()); assertEquals("DELIVERED", statuses.get(0).getErrorCode()); assertEquals("用户接收成功", statuses.get(0).getErrorMsg()); 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(67890L, statuses.get(0).getLogId()); } @Test - public void testDoGetSmsTemplate() throws ClientException { + public void testGetSmsTemplate() throws Throwable { // 准备参数 String apiTemplateId = randomString(); // mock 方法 @@ -145,18 +164,12 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest { }))).thenReturn(response); // 调用 - SmsCommonResult result = smsClient.doGetSmsTemplate(apiTemplateId); + SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId); // 断言 - 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()); + assertEquals(response.getTemplateCode(), result.getId()); + assertEquals(response.getTemplateContent(), result.getContent()); + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); + assertEquals(response.getReason(), result.getAuditReason()); } @Test @@ -171,55 +184,4 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest { "未知审核状态(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 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 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()); - } - } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMappingTest.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMappingTest.java deleted file mode 100644 index ef8901daf..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMappingTest.java +++ /dev/null @@ -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")); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClientTest.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClientTest.java index 9269de724..d62eed1e1 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClientTest.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClientTest.java @@ -1,13 +1,10 @@ package cn.iocoder.yudao.framework.sms.core.client.impl.tencent; 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.exception.enums.GlobalErrorCodeConstants; 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.json.JsonUtils; -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO; import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO; @@ -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.test.core.util.RandomUtils.*; 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.Mockito.when; @@ -78,7 +74,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest { } @Test - public void testDoSendSms() throws Throwable { + public void testDoSendSms_success() throws Throwable { // 准备参数 Long sendLogId = randomLongId(); String mobile = randomString(); @@ -94,7 +90,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest { o.setSendStatusSet(sendStatuses); SendStatus sendStatus = new SendStatus(); sendStatuses[0] = sendStatus; - sendStatus.setCode(TencentSmsClient.API_SUCCESS_CODE); + sendStatus.setCode(TencentSmsClient.API_CODE_SUCCESS); sendStatus.setMessage("send success"); sendStatus.setSerialNo(serialNo); }); @@ -109,20 +105,58 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest { }))).thenReturn(response); // 调用 - SmsCommonResult result = smsClient.doSendSms(sendLogId, mobile, - apiTemplateId, templateParams); + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams); // 断言 + assertTrue(result.getSuccess()); + assertEquals(response.getRequestId(), result.getApiRequestId()); assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode()); assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg()); - assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); - assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg()); - assertEquals(response.getRequestId(), result.getApiRequestId()); - // 断言结果 - assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getData().getSerialNo()); + assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo()); } @Test - public void testDoTParseSmsReceiveStatus() throws Throwable { + public void testDoSendSms_fail() throws Throwable { + // 准备参数 + Long sendLogId = randomLongId(); + String mobile = randomString(); + String apiTemplateId = randomString(); + List> 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" + " {\n" + @@ -139,7 +173,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest { // mock 方法 // 调用 - List statuses = smsClient.doParseSmsReceiveStatus(text); + List statuses = smsClient.parseSmsReceiveStatus(text); // 断言 assertEquals(1, statuses.size()); assertTrue(statuses.get(0).getSuccess()); @@ -152,7 +186,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest { } @Test - public void testDoGetSmsTemplate() throws Throwable { + public void testGetSmsTemplate() throws Throwable { // 准备参数 Long apiTemplateId = randomLongId(); String requestId = randomString(); @@ -173,50 +207,24 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest { }))).thenReturn(response); // 调用 - SmsCommonResult result = smsClient.doGetSmsTemplate(apiTemplateId.toString()); + SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId.toString()); // 断言 - assertEquals(TencentSmsClient.API_SUCCESS_CODE, result.getApiCode()); - assertNull(result.getApiMsg()); - assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode()); - assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg()); - 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()); + assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateId().toString(), result.getId()); + assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateContent(), result.getContent()); + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus()); + assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getAuditReason()); } @Test - public void testConvertSuccessTemplateStatus() { - testTemplateStatus(SmsTemplateAuditStatusEnum.SUCCESS, 0L); - } - - @Test - public void testConvertCheckingTemplateStatus() { - testTemplateStatus(SmsTemplateAuditStatusEnum.CHECKING, 1L); - } - - @Test - public void testConvertFailTemplateStatus() { - testTemplateStatus(SmsTemplateAuditStatusEnum.FAIL, -1L); - } - - @Test - public void testConvertUnknownTemplateStatus() { - DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus(); - templateStatus.setStatusCode(3L); - Long templateId = randomLongId(); - // 调用,并断言结果 - 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()); + public void testConvertSmsTemplateAuditStatus() { + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), + smsClient.convertSmsTemplateAuditStatus(0)); + assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(), + smsClient.convertSmsTemplateAuditStatus(1)); + assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(), + smsClient.convertSmsTemplateAuditStatus(-1)); + assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3), + "未知审核状态(3)"); } } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMappingTest.java b/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMappingTest.java deleted file mode 100644 index ebcdaf18a..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMappingTest.java +++ /dev/null @@ -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")); - } - -} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java index c0d98b648..ef5c3a624 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java @@ -82,6 +82,10 @@ public interface ErrorCodeConstants { // ========== 短信模板 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_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 ========== ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在"); diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogExcelVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogExcelVO.java index f84da74ac..3027a1213 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogExcelVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogExcelVO.java @@ -63,12 +63,6 @@ public class SmsLogExcelVO { @ExcelProperty("发送时间") private LocalDateTime sendTime; - @ExcelProperty("发送结果的编码") - private Integer sendCode; - - @ExcelProperty("发送结果的提示") - private String sendMsg; - @ExcelProperty("短信 API 发送结果的编码") private String apiSendCode; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogExportReqVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogExportReqVO.java index 79f1e7d27..a96aeb8ef 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogExportReqVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogExportReqVO.java @@ -35,4 +35,4 @@ public class SmsLogExportReqVO { @Schema(description = "开始接收时间") private LocalDateTime[] receiveTime; -} \ No newline at end of file +} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java index 6f31e6d04..71b4b39fa 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/log/SmsLogRespVO.java @@ -52,12 +52,6 @@ public class SmsLogRespVO { @Schema(description = "发送时间") private LocalDateTime sendTime; - @Schema(description = "发送结果的编码", example = "0") - private Integer sendCode; - - @Schema(description = "发送结果的提示", example = "成功") - private String sendMsg; - @Schema(description = "短信 API 发送结果的编码", example = "SUCCESS") private String apiSendCode; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java index 173c21c4d..9f250ce69 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java @@ -1,10 +1,9 @@ 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.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.TableField; import com.baomidou.mybatisplus.annotation.TableName; @@ -115,19 +114,6 @@ public class SmsLogDO extends BaseDO { * 发送时间 */ private LocalDateTime sendTime; - /** - * 发送结果的编码 - * - * 枚举 {@link SmsFrameworkErrorCodeConstants} - */ - private Integer sendCode; - /** - * 发送结果的提示 - * - * 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants} - * 异常情况下,通过格式化 Exception 的提示存储 - */ - private String sendMsg; /** * 短信 API 发送结果的编码 * diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java index e9c668ccd..ec2b39d4a 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java @@ -1,9 +1,9 @@ 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.SmsLogPageReqVO; 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 java.time.LocalDateTime; @@ -37,15 +37,15 @@ public interface SmsLogService { * 更新日志的发送结果 * * @param id 日志编号 - * @param sendCode 发送结果的编码 - * @param sendMsg 发送结果的提示 + * @param success 发送是否成功 * @param apiSendCode 短信 API 发送结果的编码 * @param apiSendMsg 短信 API 发送失败的提示 * @param apiRequestId 短信 API 发送返回的唯一请求 ID * @param apiSerialNo 短信 API 发送返回的序号 */ - void updateSmsSendResult(Long id, Integer sendCode, String sendMsg, - String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo); + void updateSmsSendResult(Long id, Boolean success, + String apiSendCode, String apiSendMsg, + String apiRequestId, String apiSerialNo); /** * 更新日志的接收结果 @@ -56,7 +56,8 @@ public interface SmsLogService { * @param apiReceiveCode 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); /** * 获得短信日志分页 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java index 40e7d1c0a..150526b2f 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java @@ -1,12 +1,11 @@ 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.SmsLogPageReqVO; 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.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.SmsSendStatusEnum; import lombok.extern.slf4j.Slf4j; @@ -55,13 +54,12 @@ public class SmsLogServiceImpl implements SmsLogService { } @Override - public void updateSmsSendResult(Long id, Integer sendCode, String sendMsg, + public void updateSmsSendResult(Long id, Boolean success, String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo) { - SmsSendStatusEnum sendStatus = CommonResult.isSuccess(sendCode) ? - SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE; - smsLogMapper.updateById(SmsLogDO.builder().id(id).sendStatus(sendStatus.getStatus()) - .sendTime(LocalDateTime.now()).sendCode(sendCode).sendMsg(sendMsg) + SmsSendStatusEnum sendStatus = success ? SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE; + smsLogMapper.updateById(SmsLogDO.builder().id(id) + .sendStatus(sendStatus.getStatus()).sendTime(LocalDateTime.now()) .apiSendCode(apiSendCode).apiSendMsg(apiSendMsg) .apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build()); } diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java index 4807f1ea6..4650bd4f8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.system.service.sms; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; 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.datapermission.core.annotation.DataPermission; 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.SmsSendRespDTO; 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.user.AdminUserService; import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -35,6 +36,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; * @author 芋道源码 */ @Service +@Slf4j public class SmsSendServiceImpl implements SmsSendService { @Resource @@ -158,11 +160,17 @@ public class SmsSendServiceImpl implements SmsSendService { SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId()); Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId()); // 发送短信 - SmsCommonResult sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(), - message.getApiTemplateId(), message.getTemplateParams()); - smsLogService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(), - sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(), - sendResult.getData() != null ? sendResult.getData().getSerialNo() : null); + try { + SmsSendRespDTO sendResponse = smsClient.sendSms(message.getLogId(), message.getMobile(), + message.getApiTemplateId(), message.getTemplateParams()); + smsLogService.updateSmsSendResult(message.getLogId(), sendResponse.getSuccess(), + 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 diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java index f3f886f55..cae797027 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java @@ -1,11 +1,11 @@ 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.SmsTemplateExportReqVO; 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.dal.dataobject.sms.SmsTemplateDO; -import cn.iocoder.yudao.framework.common.pojo.PageResult; import javax.validation.Valid; import java.util.List; diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java index ba3a7484b..876d09556 100644 --- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java @@ -1,12 +1,14 @@ 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.StrUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; 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.SmsCommonResult; 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.SmsTemplateExportReqVO; 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.Cacheable; import org.springframework.stereotype.Service; -import org.springframework.util.Assert; import javax.annotation.Resource; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.regex.Pattern; 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); Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId)); - SmsCommonResult templateResult = smsClient.getSmsTemplate(apiTemplateId); - // 校验短信模板是否正确 - templateResult.checkError(); + SmsTemplateRespDTO template; + try { + 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 diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java index 6bb32a8f3..62f26e8ba 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsChannelServiceTest.java @@ -164,31 +164,31 @@ public class SmsChannelServiceTest extends BaseDbUnitTest { @Test public void testGetSmsChannelPage() { - // mock 数据 - SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class, o -> { // 等会查询到 - o.setSignature("芋道源码"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2020, 12, 12)); - }); - smsChannelMapper.insert(dbSmsChannel); - // 测试 signature 不匹配 - smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setSignature("源码"))); - // 测试 status 不匹配 - smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11)))); - // 准备参数 - SmsChannelPageReqVO reqVO = new SmsChannelPageReqVO(); - reqVO.setSignature("芋道"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); + // mock 数据 + SmsChannelDO dbSmsChannel = randomPojo(SmsChannelDO.class, o -> { // 等会查询到 + o.setSignature("芋道源码"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2020, 12, 12)); + }); + smsChannelMapper.insert(dbSmsChannel); + // 测试 signature 不匹配 + smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setSignature("源码"))); + // 测试 status 不匹配 + smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + smsChannelMapper.insert(cloneIgnoreId(dbSmsChannel, o -> o.setCreateTime(buildTime(2020, 11, 11)))); + // 准备参数 + SmsChannelPageReqVO reqVO = new SmsChannelPageReqVO(); + reqVO.setSignature("芋道"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2020, 12, 1, 2020, 12, 24)); - // 调用 - PageResult pageResult = smsChannelService.getSmsChannelPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbSmsChannel, pageResult.getList().get(0)); + // 调用 + PageResult pageResult = smsChannelService.getSmsChannelPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbSmsChannel, pageResult.getList().get(0)); } @Test diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java index 2380ca09e..1535f2c23 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImplTest.java @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.service.sms; import cn.hutool.core.map.MapUtil; 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.util.collection.ArrayUtils; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; @@ -172,22 +171,20 @@ public class SmsLogServiceImplTest extends BaseDbUnitTest { smsLogMapper.insert(dbSmsLog); // 准备参数 Long id = dbSmsLog.getId(); - Integer sendCode = randomInteger(); - String sendMsg = randomString(); + Boolean success = randomBoolean(); String apiSendCode = randomString(); String apiSendMsg = randomString(); String apiRequestId = randomString(); String apiSerialNo = randomString(); // 调用 - smsLogService.updateSmsSendResult(id, sendCode, sendMsg, + smsLogService.updateSmsSendResult(id, success, apiSendCode, apiSendMsg, apiRequestId, apiSerialNo); // 断言 dbSmsLog = smsLogMapper.selectById(id); - assertEquals(CommonResult.isSuccess(sendCode) ? SmsSendStatusEnum.SUCCESS.getStatus() - : SmsSendStatusEnum.FAILURE.getStatus(), dbSmsLog.getSendStatus()); + assertEquals(success ? SmsSendStatusEnum.SUCCESS.getStatus() : SmsSendStatusEnum.FAILURE.getStatus(), + dbSmsLog.getSendStatus()); assertNotNull(dbSmsLog.getSendTime()); - assertEquals(sendMsg, dbSmsLog.getSendMsg()); assertEquals(apiSendCode, dbSmsLog.getApiSendCode()); assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg()); assertEquals(apiRequestId, dbSmsLog.getApiRequestId()); diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java index ee3e8b107..96742c9a8 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImplTest.java @@ -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.UserTypeEnum; 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.SmsSendRespDTO; import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; @@ -244,15 +243,14 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest { @Test @SuppressWarnings("unchecked") - public void testDoSendSms() { + public void testDoSendSms() throws Throwable { // 准备参数 SmsSendMessage message = randomPojo(SmsSendMessage.class); // mock SmsClientFactory 的方法 SmsClient smsClient = spy(SmsClient.class); when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient); // mock SmsClient 的方法 - SmsCommonResult sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class); - sendResult.setData(randomPojo(SmsSendRespDTO.class)); + SmsSendRespDTO sendResult = randomPojo(SmsSendRespDTO.class); when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()), eq(message.getTemplateParams()))).thenReturn(sendResult); @@ -260,8 +258,8 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest { smsService.doSendSms(message); // 断言 verify(smsLogService).updateSmsSendResult(eq(message.getLogId()), - eq(sendResult.getCode()), eq(sendResult.getMsg()), eq(sendResult.getApiCode()), - eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getData().getSerialNo())); + eq(sendResult.getSuccess()), eq(sendResult.getApiCode()), + eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getSerialNo())); } @Test diff --git a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java index ad6b6e733..b5d538d48 100644 --- a/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java @@ -1,13 +1,12 @@ package cn.iocoder.yudao.module.system.service.sms; 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.util.collection.ArrayUtils; 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.SmsCommonResult; 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.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO; import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO; @@ -65,7 +64,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { @Test @SuppressWarnings("unchecked") - public void testCreateSmsTemplate_success() { + public void testCreateSmsTemplate_success() throws Throwable { // 准备参数 SmsTemplateCreateReqVO reqVO = randomPojo(SmsTemplateCreateReqVO.class, o -> { o.setContent("正在进行登录操作{operation},您的验证码是{code}"); @@ -80,8 +79,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); // mock 获得 API 短信模板成功 when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); - when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class, - o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode()))); + when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn( + randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()))); // 调用 Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO); @@ -96,7 +95,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { @Test @SuppressWarnings("unchecked") - public void testUpdateSmsTemplate_success() { + public void testUpdateSmsTemplate_success() throws Throwable { // mock 数据 SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO(); smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据 @@ -115,8 +114,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest { when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO); // mock 获得 API 短信模板成功 when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient); - when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class, - o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode()))); + when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn( + randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()))); // 调用 smsTemplateService.updateSmsTemplate(reqVO);