Commit be20178f1207c76391dc45aebea0c001842e93ce

Authored by dhb52
2 parents edea17e7 41c324ac

Merge remote-tracking branch 'upstream/master'

Showing 27 changed files with 312 additions and 840 deletions
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 {
31 31 * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序
32 32 * @return 短信发送结果
33 33 */
34   - SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile, String apiTemplateId,
35   - List<KeyValue<String, Object>> templateParams);
  34 + SmsSendRespDTO sendSms(Long logId, String mobile, String apiTemplateId,
  35 + List<KeyValue<String, Object>> templateParams) throws Throwable;
36 36  
37 37 /**
38 38 * 解析接收短信的接收结果
... ... @@ -49,6 +49,6 @@ public interface SmsClient {
49 49 * @param apiTemplateId 短信 API 的模板编号
50 50 * @return 短信模板
51 51 */
52   - SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId);
  52 + SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable;
53 53  
54 54 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCodeMapping.java deleted
1   -package cn.iocoder.yudao.framework.sms.core.client;
2   -
3   -import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4   -import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
5   -
6   -import java.util.function.Function;
7   -
8   -/**
9   - * 将 API 的错误码,转换为通用的错误码
10   - *
11   - * @see SmsCommonResult
12   - * @see SmsFrameworkErrorCodeConstants
13   - *
14   - * @author 芋道源码
15   - */
16   -public interface SmsCodeMapping extends Function<String, ErrorCode> {
17   -}
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/SmsCommonResult.java deleted
1   -package cn.iocoder.yudao.framework.sms.core.client;
2   -
3   -import cn.hutool.core.exceptions.ExceptionUtil;
4   -import cn.hutool.core.lang.Assert;
5   -import cn.iocoder.yudao.framework.common.exception.ErrorCode;
6   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7   -import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
8   -import lombok.Data;
9   -import lombok.EqualsAndHashCode;
10   -import lombok.ToString;
11   -
12   -/**
13   - * 短信的 CommonResult 拓展类
14   - *
15   - * 考虑到不同的平台,返回的 code 和 msg 是不同的,所以统一额外返回 {@link #apiCode} 和 {@link #apiMsg} 字段
16   - *
17   - * 另外,一些短信平台(例如说阿里云、腾讯云)会返回一个请求编号,用于排查请求失败的问题,我们设置到 {@link #apiRequestId} 字段
18   - *
19   - * @author 芋道源码
20   - */
21   -@Data
22   -@EqualsAndHashCode(callSuper = true)
23   -@ToString(callSuper = true)
24   -public class SmsCommonResult<T> extends CommonResult<T> {
25   -
26   - /**
27   - * API 返回错误码
28   - *
29   - * 由于第三方的错误码可能是字符串,所以使用 String 类型
30   - */
31   - private String apiCode;
32   - /**
33   - * API 返回提示
34   - */
35   - private String apiMsg;
36   -
37   - /**
38   - * API 请求编号
39   - */
40   - private String apiRequestId;
41   -
42   - private SmsCommonResult() {
43   - }
44   -
45   - public static <T> SmsCommonResult<T> build(String apiCode, String apiMsg, String apiRequestId,
46   - T data, SmsCodeMapping codeMapping) {
47   - Assert.notNull(codeMapping, "参数 codeMapping 不能为空");
48   - SmsCommonResult<T> result = new SmsCommonResult<T>().setApiCode(apiCode).setApiMsg(apiMsg).setApiRequestId(apiRequestId);
49   - result.setData(data);
50   - // 翻译错误码
51   - if (codeMapping != null) {
52   - ErrorCode errorCode = codeMapping.apply(apiCode);
53   - if (errorCode == null) {
54   - errorCode = SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
55   - }
56   - result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg());
57   - }
58   - return result;
59   - }
60   -
61   - public static <T> SmsCommonResult<T> error(Throwable ex) {
62   - SmsCommonResult<T> result = new SmsCommonResult<>();
63   - result.setCode(SmsFrameworkErrorCodeConstants.EXCEPTION.getCode());
64   - result.setMsg(ExceptionUtil.getRootCauseMessage(ex));
65   - return result;
66   - }
67   -
68   -}
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/dto/SmsSendRespDTO.java
... ... @@ -11,8 +11,33 @@ import lombok.Data;
11 11 public class SmsSendRespDTO {
12 12  
13 13 /**
  14 + * 是否成功
  15 + */
  16 + private Boolean success;
  17 +
  18 + /**
  19 + * API 请求编号
  20 + */
  21 + private String apiRequestId;
  22 +
  23 + // ==================== 成功时字段 ====================
  24 +
  25 + /**
14 26 * 短信 API 发送返回的序号
15 27 */
16 28 private String serialNo;
17 29  
  30 + // ==================== 失败时字段 ====================
  31 +
  32 + /**
  33 + * API 返回错误码
  34 + *
  35 + * 由于第三方的错误码可能是字符串,所以使用 String 类型
  36 + */
  37 + private String apiCode;
  38 + /**
  39 + * API 返回提示
  40 + */
  41 + private String apiMsg;
  42 +
18 43 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/AbstractSmsClient.java
1 1 package cn.iocoder.yudao.framework.sms.core.client.impl;
2 2  
3   -import cn.iocoder.yudao.framework.common.core.KeyValue;
4 3 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
5   -import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
6   -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
7   -import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
8   -import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
9   -import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
10 4 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
11 5 import lombok.extern.slf4j.Slf4j;
12 6  
13   -import java.util.List;
14   -
15 7 /**
16 8 * 短信客户端的抽象类,提供模板方法,减少子类的冗余代码
17 9 *
... ... @@ -25,14 +17,9 @@ public abstract class AbstractSmsClient implements SmsClient {
25 17 * 短信渠道配置
26 18 */
27 19 protected volatile SmsChannelProperties properties;
28   - /**
29   - * 错误码枚举类
30   - */
31   - protected final SmsCodeMapping codeMapping;
32 20  
33   - public AbstractSmsClient(SmsChannelProperties properties, SmsCodeMapping codeMapping) {
34   - this.properties = prepareProperties(properties);
35   - this.codeMapping = codeMapping;
  21 + public AbstractSmsClient(SmsChannelProperties properties) {
  22 + this.properties = properties;
36 23 }
37 24  
38 25 /**
... ... @@ -54,74 +41,13 @@ public abstract class AbstractSmsClient implements SmsClient {
54 41 return;
55 42 }
56 43 log.info("[refresh][配置({})发生变化,重新初始化]", properties);
57   - this.properties = prepareProperties(properties);
58 44 // 初始化
59 45 this.init();
60 46 }
61 47  
62   - /**
63   - * 在赋值给{@link this#properties}前,子类可根据需要预处理短信渠道配置
64   - *
65   - * @param properties 数据库中存储的短信渠道配置
66   - * @return 满足子类实现的短信渠道配置
67   - */
68   - protected SmsChannelProperties prepareProperties(SmsChannelProperties properties) {
69   - return properties;
70   - }
71   -
72 48 @Override
73 49 public Long getId() {
74 50 return properties.getId();
75 51 }
76 52  
77   - @Override
78   - public final SmsCommonResult<SmsSendRespDTO> sendSms(Long logId, String mobile,
79   - String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
80   - // 执行短信发送
81   - SmsCommonResult<SmsSendRespDTO> result;
82   - try {
83   - result = doSendSms(logId, mobile, apiTemplateId, templateParams);
84   - } catch (Throwable ex) {
85   - // 打印异常日志
86   - log.error("[sendSms][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]",
87   - logId, mobile, apiTemplateId, templateParams, ex);
88   - // 封装返回
89   - return SmsCommonResult.error(ex);
90   - }
91   - return result;
92   - }
93   -
94   - protected abstract SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
95   - String apiTemplateId, List<KeyValue<String, Object>> templateParams)
96   - throws Throwable;
97   -
98   - @Override
99   - public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) throws Throwable {
100   - try {
101   - return doParseSmsReceiveStatus(text);
102   - } catch (Throwable ex) {
103   - log.error("[parseSmsReceiveStatus][text({}) 解析发生异常]", text, ex);
104   - throw ex;
105   - }
106   - }
107   -
108   - protected abstract List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable;
109   -
110   - @Override
111   - public SmsCommonResult<SmsTemplateRespDTO> getSmsTemplate(String apiTemplateId) {
112   - // 执行短信发送
113   - SmsCommonResult<SmsTemplateRespDTO> result;
114   - try {
115   - result = doGetSmsTemplate(apiTemplateId);
116   - } catch (Throwable ex) {
117   - // 打印异常日志
118   - log.error("[getSmsTemplate][获得短信模板({}) 发生异常]", apiTemplateId, ex);
119   - // 封装返回
120   - return SmsCommonResult.error(ex);
121   - }
122   - return result;
123   - }
124   -
125   - protected abstract SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable;
126   -
127 53 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java
1 1 package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
2 2  
3 3 import cn.hutool.core.lang.Assert;
4   -import cn.hutool.core.util.ReflectUtil;
5   -import cn.hutool.core.util.StrUtil;
6 4 import cn.iocoder.yudao.framework.common.core.KeyValue;
7   -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  6 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
8 7 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
9 8 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
10 9 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
11 10 import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
12 11 import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
13 12 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
14   -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
15   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
16   -import com.aliyuncs.AcsRequest;
17   -import com.aliyuncs.AcsResponse;
18 13 import com.aliyuncs.DefaultAcsClient;
19 14 import com.aliyuncs.IAcsClient;
20 15 import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
  16 +import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse;
21 17 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
22   -import com.aliyuncs.exceptions.ClientException;
  18 +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
23 19 import com.aliyuncs.profile.DefaultProfile;
24 20 import com.aliyuncs.profile.IClientProfile;
25 21 import com.fasterxml.jackson.annotation.JsonFormat;
... ... @@ -31,9 +27,8 @@ import lombok.extern.slf4j.Slf4j;
31 27 import java.time.LocalDateTime;
32 28 import java.util.List;
33 29 import java.util.Objects;
34   -import java.util.function.Function;
35   -import java.util.stream.Collectors;
36 30  
  31 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
37 32 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
38 33 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
39 34  
... ... @@ -47,6 +42,11 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DE
47 42 public class AliyunSmsClient extends AbstractSmsClient {
48 43  
49 44 /**
  45 + * 调用成功 code
  46 + */
  47 + public static final String API_CODE_SUCCESS = "OK";
  48 +
  49 + /**
50 50 * REGION, 使用杭州
51 51 */
52 52 private static final String ENDPOINT = "cn-hangzhou";
... ... @@ -57,7 +57,7 @@ public class AliyunSmsClient extends AbstractSmsClient {
57 57 private volatile IAcsClient client;
58 58  
59 59 public AliyunSmsClient(SmsChannelProperties properties) {
60   - super(properties, new AliyunSmsCodeMapping());
  60 + super(properties);
61 61 Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
62 62 Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
63 63 }
... ... @@ -69,9 +69,9 @@ public class AliyunSmsClient extends AbstractSmsClient {
69 69 }
70 70  
71 71 @Override
72   - protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
73   - String apiTemplateId, List<KeyValue<String, Object>> templateParams) {
74   - // 构建参数
  72 + public SmsSendRespDTO sendSms(Long sendLogId, String mobile, String apiTemplateId,
  73 + List<KeyValue<String, Object>> templateParams) throws Throwable {
  74 + // 构建请求
75 75 SendSmsRequest request = new SendSmsRequest();
76 76 request.setPhoneNumbers(mobile);
77 77 request.setSignName(properties.getSignature());
... ... @@ -79,34 +79,32 @@ public class AliyunSmsClient extends AbstractSmsClient {
79 79 request.setTemplateParam(JsonUtils.toJsonString(MapUtils.convertMap(templateParams)));
80 80 request.setOutId(String.valueOf(sendLogId));
81 81 // 执行请求
82   - return invoke(request, response -> new SmsSendRespDTO().setSerialNo(response.getBizId()));
  82 + SendSmsResponse response = client.getAcsResponse(request);
  83 + return new SmsSendRespDTO().setSuccess(Objects.equals(response.getCode(), API_CODE_SUCCESS)).setSerialNo(response.getBizId())
  84 + .setApiRequestId(response.getRequestId()).setApiCode(response.getCode()).setApiMsg(response.getMessage());
83 85 }
84 86  
85 87 @Override
86   - protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
  88 + public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
87 89 List<SmsReceiveStatus> statuses = JsonUtils.parseArray(text, SmsReceiveStatus.class);
88   - return statuses.stream().map(status -> {
89   - SmsReceiveRespDTO resp = new SmsReceiveRespDTO();
90   - resp.setSuccess(status.getSuccess());
91   - resp.setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg());
92   - resp.setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime());
93   - resp.setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId()));
94   - return resp;
95   - }).collect(Collectors.toList());
  90 + return convertList(statuses, status -> new SmsReceiveRespDTO().setSuccess(status.getSuccess())
  91 + .setErrorCode(status.getErrCode()).setErrorMsg(status.getErrMsg())
  92 + .setMobile(status.getPhoneNumber()).setReceiveTime(status.getReportTime())
  93 + .setSerialNo(status.getBizId()).setLogId(Long.valueOf(status.getOutId())));
96 94 }
97 95  
98 96 @Override
99   - protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) {
100   - // 构建参数
  97 + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
  98 + // 构建请求
101 99 QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
102 100 request.setTemplateCode(apiTemplateId);
103 101 // 执行请求
104   - return invoke(request, response -> {
105   - SmsTemplateRespDTO data = new SmsTemplateRespDTO();
106   - data.setId(response.getTemplateCode()).setContent(response.getTemplateContent());
107   - data.setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason());
108   - return data;
109   - });
  102 + QuerySmsTemplateResponse response = client.getAcsResponse(request);
  103 + if (response.getTemplateStatus() == null) {
  104 + return null;
  105 + }
  106 + return new SmsTemplateRespDTO().setId(response.getTemplateCode()).setContent(response.getTemplateContent())
  107 + .setAuditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus())).setAuditReason(response.getReason());
110 108 }
111 109  
112 110 @VisibleForTesting
... ... @@ -119,37 +117,10 @@ public class AliyunSmsClient extends AbstractSmsClient {
119 117 }
120 118 }
121 119  
122   - @VisibleForTesting
123   - <T extends AcsResponse, R> SmsCommonResult<R> invoke(AcsRequest<T> request, Function<T, R> responseConsumer) {
124   - try {
125   - // 执行发送. 由于阿里云 sms 短信没有统一的 Response,但是有统一的 code、message、requestId 属性,所以只好反射
126   - T sendResult = client.getAcsResponse(request);
127   - String code = (String) ReflectUtil.getFieldValue(sendResult, "code");
128   - String message = (String) ReflectUtil.getFieldValue(sendResult, "message");
129   - String requestId = (String) ReflectUtil.getFieldValue(sendResult, "requestId");
130   - // 解析结果
131   - R data = null;
132   - if (Objects.equals(code, "OK")) { // 请求成功的情况下
133   - data = responseConsumer.apply(sendResult);
134   - }
135   - // 拼接结果
136   - return SmsCommonResult.build(code, message, requestId, data, codeMapping);
137   - } catch (ClientException ex) {
138   - return SmsCommonResult.build(ex.getErrCode(), formatResultMsg(ex), ex.getRequestId(), null, codeMapping);
139   - }
140   - }
141   -
142   - private static String formatResultMsg(ClientException ex) {
143   - if (StrUtil.isEmpty(ex.getErrorDescription())) {
144   - return ex.getErrMsg();
145   - }
146   - return ex.getErrMsg() + " => " + ex.getErrorDescription();
147   - }
148   -
149 120 /**
150 121 * 短信接收状态
151 122 *
152   - * 参见 https://help.aliyun.com/document_detail/101867.html 文档
  123 + * 参见 <a href="https://help.aliyun.com/document_detail/101867.html">文档</a>
153 124 *
154 125 * @author 芋道源码
155 126 */
... ...
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMapping.java deleted
1   -package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
2   -
3   -import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4   -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
5   -import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
6   -import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
7   -
8   -/**
9   - * 阿里云的 SmsCodeMapping 实现类
10   - *
11   - * 参见 https://help.aliyun.com/document_detail/101346.htm 文档
12   - *
13   - * @author 芋道源码
14   - */
15   -public class AliyunSmsCodeMapping implements SmsCodeMapping {
16   -
17   - @Override
18   - public ErrorCode apply(String apiCode) {
19   - switch (apiCode) {
20   - case "OK": return GlobalErrorCodeConstants.SUCCESS;
21   - case "isv.ACCOUNT_NOT_EXISTS":
22   - case "isv.ACCOUNT_ABNORMAL":
23   - case "MissingAccessKeyId": return SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID;
24   - case "isp.RAM_PERMISSION_DENY": return SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY;
25   - case "isv.INVALID_JSON_PARAM":
26   - case "isv.INVALID_PARAMETERS": return SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR;
27   - case "isv.BUSINESS_LIMIT_CONTROL": return SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL;
28   - case "isv.DAY_LIMIT_CONTROL": return SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL;
29   - case "isv.SMS_CONTENT_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID;
30   - case "isv.SMS_TEMPLATE_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID;
31   - case "isv.SMS_SIGNATURE_ILLEGAL":
32   - case "isv.SIGN_NAME_ILLEGAL":
33   - case "isv.SMS_SIGN_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID;
34   - case "isv.AMOUNT_NOT_ENOUGH":
35   - case "isv.OUT_OF_SERVICE": return SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH;
36   - case "isv.MOBILE_NUMBER_ILLEGAL": return SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID;
37   - case "isv.TEMPLATE_MISSING_PARAMETERS": return SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR;
38   - default: return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
39   - }
40   - }
41   -
42   -}
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/debug/DebugDingTalkCodeMapping.java deleted
1   -package cn.iocoder.yudao.framework.sms.core.client.impl.debug;
2   -
3   -import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4   -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
5   -import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
6   -import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
7   -
8   -import java.util.Objects;
9   -
10   -/**
11   - * 钉钉的 SmsCodeMapping 实现类
12   - *
13   - * @author 芋道源码
14   - */
15   -public class DebugDingTalkCodeMapping implements SmsCodeMapping {
16   -
17   - @Override
18   - public ErrorCode apply(String apiCode) {
19   - return Objects.equals(apiCode, "0") ? GlobalErrorCodeConstants.SUCCESS : SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
20   - }
21   -
22   -}
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;
8 8 import cn.hutool.crypto.digest.HmacAlgorithm;
9 9 import cn.hutool.http.HttpUtil;
10 10 import cn.iocoder.yudao.framework.common.core.KeyValue;
11   -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
  11 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  12 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
12 13 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
13 14 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
14 15 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
15 16 import cn.iocoder.yudao.framework.sms.core.client.impl.AbstractSmsClient;
16 17 import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
17 18 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
18   -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
19   -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
20 19  
21 20 import java.util.HashMap;
22 21 import java.util.List;
23 22 import java.util.Map;
  23 +import java.util.Objects;
24 24  
25 25 /**
26 26 * 基于钉钉 WebHook 实现的调试的短信客户端实现类
... ... @@ -32,7 +32,7 @@ import java.util.Map;
32 32 public class DebugDingTalkSmsClient extends AbstractSmsClient {
33 33  
34 34 public DebugDingTalkSmsClient(SmsChannelProperties properties) {
35   - super(properties, new DebugDingTalkCodeMapping());
  35 + super(properties);
36 36 Assert.notEmpty(properties.getApiKey(), "apiKey 不能为空");
37 37 Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
38 38 }
... ... @@ -42,8 +42,8 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
42 42 }
43 43  
44 44 @Override
45   - protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId, String mobile,
46   - String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
  45 + public SmsSendRespDTO sendSms(Long sendLogId, String mobile,
  46 + String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
47 47 // 构建请求
48 48 String url = buildUrl("robot/send");
49 49 Map<String, Object> params = new HashMap<>();
... ... @@ -55,14 +55,15 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
55 55 String responseText = HttpUtil.post(url, JsonUtils.toJsonString(params));
56 56 // 解析结果
57 57 Map<?, ?> responseObj = JsonUtils.parseObject(responseText, Map.class);
58   - return SmsCommonResult.build(MapUtil.getStr(responseObj, "errcode"), MapUtil.getStr(responseObj, "errorMsg"),
59   - null, new SmsSendRespDTO().setSerialNo(StrUtil.uuid()), codeMapping);
  58 + String errorCode = MapUtil.getStr(responseObj, "errcode");
  59 + return new SmsSendRespDTO().setSuccess(Objects.equals(errorCode, "0")).setSerialNo(StrUtil.uuid())
  60 + .setApiCode(errorCode).setApiMsg(MapUtil.getStr(responseObj, "errorMsg"));
60 61 }
61 62  
62 63 /**
63 64 * 构建请求地址
64 65 *
65   - * 参见 https://developers.dingtalk.com/document/app/custom-robot-access/title-nfv-794-g71 文档
  66 + * 参见 <a href="https://developers.dingtalk.com/document/app/custom-robot-access/title-nfv-794-g71">文档</a>
66 67 *
67 68 * @param path 请求路径
68 69 * @return 请求地址
... ... @@ -82,15 +83,14 @@ public class DebugDingTalkSmsClient extends AbstractSmsClient {
82 83 }
83 84  
84 85 @Override
85   - protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
  86 + public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
86 87 throw new UnsupportedOperationException("模拟短信客户端,暂时无需解析回调");
87 88 }
88 89  
89 90 @Override
90   - protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) {
91   - SmsTemplateRespDTO data = new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
  91 + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) {
  92 + return new SmsTemplateRespDTO().setId(apiTemplateId).setContent("")
92 93 .setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason("");
93   - return SmsCommonResult.build("0", "success", null, data, codeMapping);
94 94 }
95 95  
96 96 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsChannelProperties.java deleted
1   -package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
2   -
3   -import cn.hutool.core.bean.BeanUtil;
4   -import cn.hutool.core.lang.Assert;
5   -import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
6   -import lombok.Data;
7   -
8   -/**
9   - * 腾讯云短信配置实现类
10   - * 腾讯云发送短信时,需要额外的参数 sdkAppId,
11   - *
12   - * @author shiwp
13   - */
14   -@Data
15   -public class TencentSmsChannelProperties extends SmsChannelProperties {
16   -
17   - /**
18   - * 应用 id
19   - */
20   - private String sdkAppId;
21   -
22   - /**
23   - * 考虑到不破坏原有的 apiKey + apiSecret 的结构,
24   - * 所以腾讯云短信存储时,将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
25   - * 因此在使用时,需要将 secretId 和 sdkAppId 解析出来,分别存储到对应字段中。
26   - */
27   - public static TencentSmsChannelProperties build(SmsChannelProperties properties) {
28   - if (properties instanceof TencentSmsChannelProperties) {
29   - return (TencentSmsChannelProperties) properties;
30   - }
31   - TencentSmsChannelProperties result = BeanUtil.toBean(properties, TencentSmsChannelProperties.class);
32   - String combineKey = properties.getApiKey();
33   - Assert.notEmpty(combineKey, "apiKey 不能为空");
34   - String[] keys = combineKey.trim().split(" ");
35   - Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]");
36   - Assert.notBlank(keys[0], "腾讯云短信 secretId 不能为空");
37   - Assert.notBlank(keys[1], "腾讯云短信 sdkAppId 不能为空");
38   - result.setSdkAppId(keys[1]).setApiKey(keys[0]);
39   - return result;
40   - }
41   -}
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;
4 4 import cn.hutool.core.util.StrUtil;
5 5 import cn.iocoder.yudao.framework.common.core.KeyValue;
6 6 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
7   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
8 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
9   -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
10 8 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
11 9 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
12 10 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
... ... @@ -17,23 +15,22 @@ import com.fasterxml.jackson.annotation.JsonFormat;
17 15 import com.fasterxml.jackson.annotation.JsonProperty;
18 16 import com.google.common.annotations.VisibleForTesting;
19 17 import com.tencentcloudapi.common.Credential;
20   -import com.tencentcloudapi.common.exception.TencentCloudSDKException;
21 18 import com.tencentcloudapi.sms.v20210111.SmsClient;
22 19 import com.tencentcloudapi.sms.v20210111.models.*;
23 20 import lombok.Data;
24 21  
25 22 import java.time.LocalDateTime;
26 23 import java.util.List;
27   -import java.util.function.Function;
28   -import java.util.function.Supplier;
  24 +import java.util.Objects;
29 25  
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
30 27 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
31 28 import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DEFAULT;
32 29  
33 30 /**
34 31 * 腾讯云短信功能实现
35   - * <p>
36   - * 参见 https://cloud.tencent.com/document/product/382/52077
  32 + *
  33 + * 参见 <a href="https://cloud.tencent.com/document/product/382/52077">文档</a>
37 34 *
38 35 * @author shiwp
39 36 */
... ... @@ -42,7 +39,7 @@ public class TencentSmsClient extends AbstractSmsClient {
42 39 /**
43 40 * 调用成功 code
44 41 */
45   - public static final String API_SUCCESS_CODE = "Ok";
  42 + public static final String API_CODE_SUCCESS = "Ok";
46 43  
47 44 /**
48 45 * REGION,使用南京
... ... @@ -51,180 +48,103 @@ public class TencentSmsClient extends AbstractSmsClient {
51 48  
52 49 /**
53 50 * 是否国际/港澳台短信:
  51 + *
54 52 * 0:表示国内短信。
55 53 * 1:表示国际/港澳台短信。
56 54 */
57   - private static final long INTERNATIONAL = 0L;
  55 + private static final long INTERNATIONAL_CHINA = 0L;
58 56  
59 57 private SmsClient client;
60 58  
61 59 public TencentSmsClient(SmsChannelProperties properties) {
62   - super(properties, new TencentSmsCodeMapping());
  60 + super(properties);
63 61 Assert.notEmpty(properties.getApiSecret(), "apiSecret 不能为空");
  62 + validateSdkAppId(properties);
64 63 }
65 64  
66 65 @Override
67 66 protected void doInit() {
68 67 // 实例化一个认证对象,入参需要传入腾讯云账户密钥对 secretId,secretKey
69   - Credential credential = new Credential(properties.getApiKey(), properties.getApiSecret());
  68 + Credential credential = new Credential(getApiKey(), properties.getApiSecret());
70 69 client = new SmsClient(credential, ENDPOINT);
71 70 }
72 71  
73   - @Override
74   - protected SmsCommonResult<SmsSendRespDTO> doSendSms(Long sendLogId,
75   - String mobile,
76   - String apiTemplateId,
77   - List<KeyValue<String, Object>> templateParams) throws Throwable {
78   - return invoke(() -> buildSendSmsRequest(sendLogId, mobile, apiTemplateId, templateParams),
79   - this::doSendSms0,
80   - response -> {
81   - SendStatus sendStatus = response.getSendStatusSet()[0];
82   - return SmsCommonResult.build(sendStatus.getCode(), sendStatus.getMessage(), response.getRequestId(),
83   - new SmsSendRespDTO().setSerialNo(sendStatus.getSerialNo()), codeMapping);
84   - });
85   - }
86   -
87   -
88 72 /**
89   - * 腾讯云发放短信的时候,需要额外的参数 sdkAppId。
90   - * 考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
91   - * 因此,这边需要使用 TencentSmsChannelProperties 做拆分,重新封装到 properties 内。
  73 + * 参数校验腾讯云的 SDK AppId
  74 + *
  75 + * 原因是:腾讯云发放短信的时候,需要额外的参数 sdkAppId
  76 + *
  77 + * 解决方案:考虑到不破坏原有的 apiKey + apiSecret 的结构,所以将 secretId 拼接到 apiKey 字段中,格式为 "secretId sdkAppId"。
92 78 *
93   - * @param properties 数据库中存储的短信渠道配置
94   - * @return TencentSmsChannelProperties
  79 + * @param properties 配置
95 80 */
96   - @Override
97   - protected SmsChannelProperties prepareProperties(SmsChannelProperties properties) {
98   - return TencentSmsChannelProperties.build(properties);
  81 + private static void validateSdkAppId(SmsChannelProperties properties) {
  82 + String combineKey = properties.getApiKey();
  83 + Assert.notEmpty(combineKey, "apiKey 不能为空");
  84 + String[] keys = combineKey.trim().split(" ");
  85 + Assert.isTrue(keys.length == 2, "腾讯云短信 apiKey 配置格式错误,请配置 为[secretId sdkAppId]");
99 86 }
100 87  
101   - /**
102   - * 调用腾讯云 SDK 发送短信
103   - *
104   - * @param request 发送短信请求
105   - * @return 发送短信响应
106   - * @throws TencentCloudSDKException SDK 用来封装发送短信失败
107   - */
108   - private SendSmsResponse doSendSms0(SendSmsRequest request) throws TencentCloudSDKException {
109   - return client.SendSms(request);
  88 + private String getSdkAppId() {
  89 + return StrUtil.subAfter(properties.getApiKey(), " ", true);
110 90 }
111 91  
112   - /**
113   - * 封装腾讯云发送短信请求
114   - *
115   - * @param sendLogId 日志编号
116   - * @param mobile 手机号
117   - * @param apiTemplateId 短信 API 的模板编号
118   - * @param templateParams 短信模板参数。通过 List 数组,保证参数的顺序
119   - * @return 腾讯云发送短信请求
120   - */
121   - private SendSmsRequest buildSendSmsRequest(Long sendLogId,
122   - String mobile,
123   - String apiTemplateId,
124   - List<KeyValue<String, Object>> templateParams) {
  92 + private String getApiKey() {
  93 + return StrUtil.subBefore(properties.getApiKey(), " ", true);
  94 + }
  95 +
  96 + @Override
  97 + public SmsSendRespDTO sendSms(Long sendLogId, String mobile,
  98 + String apiTemplateId, List<KeyValue<String, Object>> templateParams) throws Throwable {
  99 + // 构建请求
125 100 SendSmsRequest request = new SendSmsRequest();
126   - request.setSmsSdkAppId(((TencentSmsChannelProperties) properties).getSdkAppId());
  101 + request.setSmsSdkAppId(getSdkAppId());
127 102 request.setPhoneNumberSet(new String[]{mobile});
128 103 request.setSignName(properties.getSignature());
129 104 request.setTemplateId(apiTemplateId);
130 105 request.setTemplateParamSet(ArrayUtils.toArray(templateParams, e -> String.valueOf(e.getValue())));
131 106 request.setSessionContext(JsonUtils.toJsonString(new SessionContext().setLogId(sendLogId)));
132   - return request;
  107 + // 执行请求
  108 + SendSmsResponse response = client.SendSms(request);
  109 + SendStatus status = response.getSendStatusSet()[0];
  110 + return new SmsSendRespDTO().setSuccess(Objects.equals(status.getCode(), API_CODE_SUCCESS)).setSerialNo(status.getSerialNo())
  111 + .setApiRequestId(response.getRequestId()).setApiCode(status.getCode()).setApiMsg(status.getMessage());
133 112 }
134 113  
135 114 @Override
136   - protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) throws Throwable {
  115 + public List<SmsReceiveRespDTO> parseSmsReceiveStatus(String text) {
137 116 List<SmsReceiveStatus> callback = JsonUtils.parseArray(text, SmsReceiveStatus.class);
138   - return CollectionUtils.convertList(callback, status -> {
139   - SmsReceiveRespDTO data = new SmsReceiveRespDTO();
140   - data.setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription());
141   - data.setReceiveTime(status.getReceiveTime()).setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus()));
142   - data.setMobile(status.getMobile()).setSerialNo(status.getSerialNo());
143   - SessionContext context;
144   - Long logId;
145   - Assert.notNull(context = status.getSessionContext(), "回执信息中未解析出 context,请联系腾讯云小助手");
146   - Assert.notNull(logId = context.getLogId(), "回执信息中未解析出 logId,请联系腾讯云小助手");
147   - data.setLogId(logId);
148   - return data;
149   - });
  117 + return convertList(callback, status -> new SmsReceiveRespDTO()
  118 + .setSuccess(SmsReceiveStatus.SUCCESS_CODE.equalsIgnoreCase(status.getStatus()))
  119 + .setErrorCode(status.getErrCode()).setErrorMsg(status.getDescription())
  120 + .setMobile(status.getMobile()).setReceiveTime(status.getReceiveTime())
  121 + .setSerialNo(status.getSerialNo()).setLogId(status.getSessionContext().getLogId()));
150 122 }
151 123  
152 124 @Override
153   - protected SmsCommonResult<SmsTemplateRespDTO> doGetSmsTemplate(String apiTemplateId) throws Throwable {
154   - return invoke(() -> this.buildSmsTemplateStatusRequest(apiTemplateId),
155   - this::doGetSmsTemplate0,
156   - response -> {
157   - SmsTemplateRespDTO data = convertTemplateStatusDTO(response.getDescribeTemplateStatusSet()[0]);
158   - return SmsCommonResult.build(API_SUCCESS_CODE, null, response.getRequestId(), data, codeMapping);
159   - });
160   - }
161   -
162   - @VisibleForTesting
163   - SmsTemplateRespDTO convertTemplateStatusDTO(DescribeTemplateListStatus templateStatus) {
164   - if (templateStatus == null) {
165   - return null;
166   - }
167   - SmsTemplateAuditStatusEnum auditStatus;
168   - Assert.notNull(templateStatus.getStatusCode(),
169   - StrUtil.format("短信模版审核状态为 null,模版 id{}", templateStatus.getTemplateId()));
170   - switch (templateStatus.getStatusCode().intValue()) {
171   - case -1:
172   - auditStatus = SmsTemplateAuditStatusEnum.FAIL;
173   - break;
174   - case 0:
175   - auditStatus = SmsTemplateAuditStatusEnum.SUCCESS;
176   - break;
177   - case 1:
178   - auditStatus = SmsTemplateAuditStatusEnum.CHECKING;
179   - break;
180   - default:
181   - throw new IllegalStateException(StrUtil.format("不能解析短信模版审核状态{},模版 id{}",
182   - templateStatus.getStatusCode(), templateStatus.getTemplateId()));
183   - }
184   - SmsTemplateRespDTO data = new SmsTemplateRespDTO();
185   - data.setId(String.valueOf(templateStatus.getTemplateId())).setContent(templateStatus.getTemplateContent());
186   - data.setAuditStatus(auditStatus.getStatus()).setAuditReason(templateStatus.getReviewReply());
187   - return data;
188   - }
189   -
190   - /**
191   - * 封装查询模版审核状态请求
192   - * @param apiTemplateId api 的模版 id
193   - * @return 查询模版审核状态请求
194   - */
195   - private DescribeSmsTemplateListRequest buildSmsTemplateStatusRequest(String apiTemplateId) {
  125 + public SmsTemplateRespDTO getSmsTemplate(String apiTemplateId) throws Throwable {
  126 + // 构建请求
196 127 DescribeSmsTemplateListRequest request = new DescribeSmsTemplateListRequest();
197 128 request.setTemplateIdSet(new Long[]{Long.parseLong(apiTemplateId)});
198   - // 地区 0:表示国内短信。1:表示国际/港澳台短信。
199   - request.setInternational(INTERNATIONAL);
200   - return request;
201   - }
202   -
203   - /**
204   - * 调用腾讯云 SDK 查询短信模版状态
205   - *
206   - * @param request 查询短信模版状态请求
207   - * @return 查询短信模版状态响应
208   - * @throws TencentCloudSDKException SDK 用来封装查询短信模版状态失败
209   - */
210   - private DescribeSmsTemplateListResponse doGetSmsTemplate0(DescribeSmsTemplateListRequest request) throws TencentCloudSDKException {
211   - return client.DescribeSmsTemplateList(request);
  129 + request.setInternational(INTERNATIONAL_CHINA);
  130 + // 执行请求
  131 + DescribeSmsTemplateListResponse response = client.DescribeSmsTemplateList(request);
  132 + DescribeTemplateListStatus status = response.getDescribeTemplateStatusSet()[0];
  133 + if (status == null || status.getStatusCode() == null) {
  134 + return null;
  135 + }
  136 + return new SmsTemplateRespDTO().setId(status.getTemplateId().toString()).setContent(status.getTemplateContent())
  137 + .setAuditStatus(convertSmsTemplateAuditStatus(status.getStatusCode().intValue())).setAuditReason(status.getReviewReply());
212 138 }
213 139  
214   - <Q, P, R> SmsCommonResult<R> invoke(Supplier<Q> requestSupplier,
215   - SdkFunction<Q, P> responseSupplier,
216   - Function<P, SmsCommonResult<R>> resultGen) {
217   - // 构建请求body
218   - Q request = requestSupplier.get();
219   - P response;
220   - // 调用腾讯云发送短信
221   - try {
222   - response = responseSupplier.apply(request);
223   - } catch (TencentCloudSDKException e) {
224   - // 调用异常,封装结果
225   - return SmsCommonResult.build(e.getErrorCode(), e.getMessage(), e.getRequestId(), null, codeMapping);
  140 + @VisibleForTesting
  141 + Integer convertSmsTemplateAuditStatus(int templateStatus) {
  142 + switch (templateStatus) {
  143 + case 1: return SmsTemplateAuditStatusEnum.CHECKING.getStatus();
  144 + case 0: return SmsTemplateAuditStatusEnum.SUCCESS.getStatus();
  145 + case -1: return SmsTemplateAuditStatusEnum.FAIL.getStatus();
  146 + default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
226 147 }
227   - return resultGen.apply(response);
228 148 }
229 149  
230 150 @Data
... ... @@ -278,7 +198,7 @@ public class TencentSmsClient extends AbstractSmsClient {
278 198 private String serialNo;
279 199  
280 200 /**
281   - * 用户的 session 内容(与发送接口的请求参数SessionContext一致)
  201 + * 用户的 session 内容(与发送接口的请求参数 SessionContext 一致)
282 202 */
283 203 @JsonProperty("ext")
284 204 private SessionContext sessionContext;
... ... @@ -293,10 +213,7 @@ public class TencentSmsClient extends AbstractSmsClient {
293 213 * 发送短信记录id
294 214 */
295 215 private Long logId;
296   - }
297 216  
298   - private interface SdkFunction<T, R> {
299   - R apply(T t) throws TencentCloudSDKException;
300 217 }
301 218  
302 219 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-sms/src/main/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMapping.java deleted
1   -package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
2   -
3   -import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4   -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
5   -import cn.iocoder.yudao.framework.sms.core.client.SmsCodeMapping;
6   -import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
7   -
8   -import static cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants.*;
9   -
10   -/**
11   - * 腾讯云的 SmsCodeMapping 实现类
12   - *
13   - * 参见 https://cloud.tencent.com/document/api/382/52075#.E5.85.AC.E5.85.B1.E9.94.99.E8.AF.AF.E7.A0.81
14   - *
15   - * @author : shiwp
16   - */
17   -public class TencentSmsCodeMapping implements SmsCodeMapping {
18   -
19   - @Override
20   - public ErrorCode apply(String apiCode) {
21   - switch (apiCode) {
22   - case TencentSmsClient.API_SUCCESS_CODE: return GlobalErrorCodeConstants.SUCCESS;
23   - case "FailedOperation.ContainSensitiveWord": return SMS_SEND_CONTENT_INVALID;
24   - case "FailedOperation.JsonParseFail":
25   - case "MissingParameter.EmptyPhoneNumberSet":
26   - case "LimitExceeded.PhoneNumberCountLimit":
27   - case "FailedOperation.FailResolvePacket": return GlobalErrorCodeConstants.BAD_REQUEST;
28   - case "FailedOperation.InsufficientBalanceInSmsPackage": return SMS_ACCOUNT_MONEY_NOT_ENOUGH;
29   - case "FailedOperation.MarketingSendTimeConstraint": return SMS_SEND_MARKET_LIMIT_CONTROL;
30   - case "FailedOperation.PhoneNumberInBlacklist": return SMS_MOBILE_BLACK;
31   - case "FailedOperation.SignatureIncorrectOrUnapproved": return SMS_SIGN_INVALID;
32   - case "FailedOperation.MissingTemplateToModify":
33   - case "FailedOperation.TemplateIncorrectOrUnapproved": return SMS_TEMPLATE_INVALID;
34   - case "InvalidParameterValue.IncorrectPhoneNumber": return SMS_MOBILE_INVALID;
35   - case "InvalidParameterValue.SdkAppIdNotExist": return SMS_APP_ID_INVALID;
36   - case "InvalidParameterValue.TemplateParameterLengthLimit":
37   - case "InvalidParameterValue.TemplateParameterFormatError": return SMS_TEMPLATE_PARAM_ERROR;
38   - case "LimitExceeded.PhoneNumberDailyLimit": return SMS_SEND_DAY_LIMIT_CONTROL;
39   - case "LimitExceeded.PhoneNumberThirtySecondLimit":
40   - case "LimitExceeded.PhoneNumberOneHourLimit": return SMS_SEND_BUSINESS_LIMIT_CONTROL;
41   - case "UnauthorizedOperation.RequestPermissionDeny":
42   - case "FailedOperation.ForbidAddMarketingTemplates":
43   - case "FailedOperation.NotEnterpriseCertification":
44   - case "UnauthorizedOperation.IndividualUserMarketingSmsPermissionDeny": return SMS_PERMISSION_DENY;
45   - case "UnauthorizedOperation.RequestIpNotInWhitelist": return SMS_IP_DENY;
46   - case "AuthFailure.SecretIdNotFound": return SMS_ACCOUNT_INVALID;
47   - }
48   - return SmsFrameworkErrorCodeConstants.SMS_UNKNOWN;
49   - }
50   -}
51 0 \ No newline at end of file
yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsClientTest.java
1 1 package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
2 2  
3 3 import cn.hutool.core.util.ReflectUtil;
4   -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
5 4 import cn.iocoder.yudao.framework.common.core.KeyValue;
6   -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
7   -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
  5 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
8 6 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
9 7 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
10 8 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
11 9 import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
12 10 import cn.iocoder.yudao.framework.sms.core.property.SmsChannelProperties;
13   -import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
14   -import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
15   -import com.aliyuncs.AcsRequest;
  11 +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
16 12 import com.aliyuncs.IAcsClient;
17 13 import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
18 14 import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateResponse;
19 15 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
20 16 import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
21   -import com.aliyuncs.exceptions.ClientException;
22 17 import com.google.common.collect.Lists;
23   -import org.junit.jupiter.api.Assertions;
24 18 import org.junit.jupiter.api.Test;
25 19 import org.mockito.ArgumentMatcher;
26 20 import org.mockito.InjectMocks;
... ... @@ -28,12 +22,10 @@ import org.mockito.Mock;
28 22  
29 23 import java.time.LocalDateTime;
30 24 import java.util.List;
31   -import java.util.function.Function;
32 25  
33 26 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
34 27 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
35 28 import static org.junit.jupiter.api.Assertions.*;
36   -import static org.mockito.ArgumentMatchers.any;
37 29 import static org.mockito.ArgumentMatchers.argThat;
38 30 import static org.mockito.Mockito.when;
39 31  
... ... @@ -67,8 +59,7 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
67 59 }
68 60  
69 61 @Test
70   - @SuppressWarnings("unchecked")
71   - public void testDoSendSms() throws ClientException {
  62 + public void tesSendSms_success() throws Throwable {
72 63 // 准备参数
73 64 Long sendLogId = randomLongId();
74 65 String mobile = randomString();
... ... @@ -87,20 +78,47 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
87 78 }))).thenReturn(response);
88 79  
89 80 // 调用
90   - SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile,
  81 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile,
91 82 apiTemplateId, templateParams);
92 83 // 断言
  84 + assertTrue(result.getSuccess());
  85 + assertEquals(response.getRequestId(), result.getApiRequestId());
93 86 assertEquals(response.getCode(), result.getApiCode());
94 87 assertEquals(response.getMessage(), result.getApiMsg());
95   - assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
96   - assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
  88 + assertEquals(response.getBizId(), result.getSerialNo());
  89 + }
  90 +
  91 + @Test
  92 + public void tesSendSms_fail() throws Throwable {
  93 + // 准备参数
  94 + Long sendLogId = randomLongId();
  95 + String mobile = randomString();
  96 + String apiTemplateId = randomString();
  97 + List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
  98 + new KeyValue<>("code", 1234), new KeyValue<>("op", "login"));
  99 + // mock 方法
  100 + SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> o.setCode("ERROR"));
  101 + when(client.getAcsResponse(argThat((ArgumentMatcher<SendSmsRequest>) acsRequest -> {
  102 + assertEquals(mobile, acsRequest.getPhoneNumbers());
  103 + assertEquals(properties.getSignature(), acsRequest.getSignName());
  104 + assertEquals(apiTemplateId, acsRequest.getTemplateCode());
  105 + assertEquals(toJsonString(MapUtils.convertMap(templateParams)), acsRequest.getTemplateParam());
  106 + assertEquals(sendLogId.toString(), acsRequest.getOutId());
  107 + return true;
  108 + }))).thenReturn(response);
  109 +
  110 + // 调用
  111 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
  112 + // 断言
  113 + assertFalse(result.getSuccess());
97 114 assertEquals(response.getRequestId(), result.getApiRequestId());
98   - // 断言结果
99   - assertEquals(response.getBizId(), result.getData().getSerialNo());
  115 + assertEquals(response.getCode(), result.getApiCode());
  116 + assertEquals(response.getMessage(), result.getApiMsg());
  117 + assertEquals(response.getBizId(), result.getSerialNo());
100 118 }
101 119  
102 120 @Test
103   - public void testDoTParseSmsReceiveStatus() throws Throwable {
  121 + public void testParseSmsReceiveStatus() {
104 122 // 准备参数
105 123 String text = "[\n" +
106 124 " {\n" +
... ... @@ -118,20 +136,21 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
118 136 // mock 方法
119 137  
120 138 // 调用
121   - List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text);
  139 + List<SmsReceiveRespDTO> statuses = smsClient.parseSmsReceiveStatus(text);
122 140 // 断言
123 141 assertEquals(1, statuses.size());
124 142 assertTrue(statuses.get(0).getSuccess());
125 143 assertEquals("DELIVERED", statuses.get(0).getErrorCode());
126 144 assertEquals("用户接收成功", statuses.get(0).getErrorMsg());
127 145 assertEquals("13900000001", statuses.get(0).getMobile());
128   - assertEquals(LocalDateTime.of(2017, 2, 2, 22, 23, 24), statuses.get(0).getReceiveTime());
  146 + assertEquals(LocalDateTime.of(2017, 2, 2, 22, 23, 24),
  147 + statuses.get(0).getReceiveTime());
129 148 assertEquals("12345", statuses.get(0).getSerialNo());
130 149 assertEquals(67890L, statuses.get(0).getLogId());
131 150 }
132 151  
133 152 @Test
134   - public void testDoGetSmsTemplate() throws ClientException {
  153 + public void testGetSmsTemplate() throws Throwable {
135 154 // 准备参数
136 155 String apiTemplateId = randomString();
137 156 // mock 方法
... ... @@ -145,18 +164,12 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
145 164 }))).thenReturn(response);
146 165  
147 166 // 调用
148   - SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId);
  167 + SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId);
149 168 // 断言
150   - assertEquals(response.getCode(), result.getApiCode());
151   - assertEquals(response.getMessage(), result.getApiMsg());
152   - assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
153   - assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
154   - assertEquals(response.getRequestId(), result.getApiRequestId());
155   - // 断言结果
156   - assertEquals(response.getTemplateCode(), result.getData().getId());
157   - assertEquals(response.getTemplateContent(), result.getData().getContent());
158   - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
159   - assertEquals(response.getReason(), result.getData().getAuditReason());
  169 + assertEquals(response.getTemplateCode(), result.getId());
  170 + assertEquals(response.getTemplateContent(), result.getContent());
  171 + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus());
  172 + assertEquals(response.getReason(), result.getAuditReason());
160 173 }
161 174  
162 175 @Test
... ... @@ -171,55 +184,4 @@ public class AliyunSmsClientTest extends BaseMockitoUnitTest {
171 184 "未知审核状态(3)");
172 185 }
173 186  
174   - @Test
175   - @SuppressWarnings("unchecked")
176   - public void testInvoke_throwable() throws ClientException {
177   - // 准备参数
178   - QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
179   - // mock 方法
180   - ClientException ex = new ClientException("isv.INVALID_PARAMETERS", "参数不正确", randomString());
181   - when(client.getAcsResponse(any(AcsRequest.class))).thenThrow(ex);
182   -
183   - // 调用,并断言异常
184   - SmsCommonResult<?> result = smsClient.invoke(request, null);
185   - // 断言
186   - assertEquals(ex.getErrCode(), result.getApiCode());
187   - assertEquals(ex.getErrMsg(), result.getApiMsg());
188   - Assertions.assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR.getCode(), result.getCode());
189   - Assertions.assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR.getMsg(), result.getMsg());
190   - assertEquals(ex.getRequestId(), result.getApiRequestId());
191   - }
192   -
193   - @Test
194   - public void testInvoke_success() throws ClientException {
195   - // 准备参数
196   - QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
197   - Function<QuerySmsTemplateResponse, SmsTemplateRespDTO> responseConsumer = response -> {
198   - SmsTemplateRespDTO data = new SmsTemplateRespDTO();
199   - data.setId(response.getTemplateCode()).setContent(response.getTemplateContent());
200   - data.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus()).setAuditReason(response.getReason());
201   - return data;
202   - };
203   - // mock 方法
204   - QuerySmsTemplateResponse response = randomPojo(QuerySmsTemplateResponse.class, o -> {
205   - o.setCode("OK");
206   - o.setTemplateStatus(1); // 设置模板通过
207   - });
208   - when(client.getAcsResponse(any(AcsRequest.class))).thenReturn(response);
209   -
210   - // 调用
211   - SmsCommonResult<SmsTemplateRespDTO> result = smsClient.invoke(request, responseConsumer);
212   - // 断言
213   - assertEquals(response.getCode(), result.getApiCode());
214   - assertEquals(response.getMessage(), result.getApiMsg());
215   - assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
216   - assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
217   - assertEquals(response.getRequestId(), result.getApiRequestId());
218   - // 断言结果
219   - assertEquals(response.getTemplateCode(), result.getData().getId());
220   - assertEquals(response.getTemplateContent(), result.getData().getContent());
221   - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
222   - assertEquals(response.getReason(), result.getData().getAuditReason());
223   - }
224   -
225 187 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/aliyun/AliyunSmsCodeMappingTest.java deleted
1   -package cn.iocoder.yudao.framework.sms.core.client.impl.aliyun;
2   -
3   -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
4   -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
5   -import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
6   -import org.junit.jupiter.api.Test;
7   -import org.mockito.InjectMocks;
8   -
9   -import static org.junit.jupiter.api.Assertions.assertEquals;
10   -
11   -/**
12   - * {@link AliyunSmsCodeMapping} 的单元测试
13   - *
14   - * @author 芋道源码
15   - */
16   -public class AliyunSmsCodeMappingTest extends BaseMockitoUnitTest {
17   -
18   - @InjectMocks
19   - private AliyunSmsCodeMapping codeMapping;
20   -
21   - @Test
22   - public void testApply() {
23   - assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply("OK"));
24   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("MissingAccessKeyId"));
25   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("isv.ACCOUNT_NOT_EXISTS"));
26   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("isv.ACCOUNT_ABNORMAL"));
27   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL, codeMapping.apply("isv.DAY_LIMIT_CONTROL"));
28   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID, codeMapping.apply("isv.SMS_CONTENT_ILLEGAL"));
29   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SMS_SIGN_ILLEGAL"));
30   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SIGN_NAME_ILLEGAL"));
31   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("isp.RAM_PERMISSION_DENY"));
32   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("isv.OUT_OF_SERVICE"));
33   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("isv.AMOUNT_NOT_ENOUGH"));
34   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("isv.SMS_TEMPLATE_ILLEGAL"));
35   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("isv.SMS_SIGNATURE_ILLEGAL"));
36   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply("isv.INVALID_PARAMETERS"));
37   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_API_PARAM_ERROR, codeMapping.apply("isv.INVALID_JSON_PARAM"));
38   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID, codeMapping.apply("isv.MOBILE_NUMBER_ILLEGAL"));
39   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("isv.TEMPLATE_MISSING_PARAMETERS"));
40   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("isv.BUSINESS_LIMIT_CONTROL"));
41   - }
42   -
43   -}
yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsClientTest.java
1 1 package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
2 2  
3 3 import cn.hutool.core.util.ReflectUtil;
4   -import cn.hutool.core.util.StrUtil;
5 4 import cn.iocoder.yudao.framework.common.core.KeyValue;
6   -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
7 5 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
8 6 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
9 7 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
10   -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
11 8 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
12 9 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
13 10 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
... ... @@ -31,7 +28,6 @@ import java.util.List;
31 28 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
32 29 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
33 30 import static org.junit.jupiter.api.Assertions.*;
34   -import static org.junit.jupiter.api.Assertions.assertThrows;
35 31 import static org.mockito.ArgumentMatchers.argThat;
36 32 import static org.mockito.Mockito.when;
37 33  
... ... @@ -78,7 +74,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
78 74 }
79 75  
80 76 @Test
81   - public void testDoSendSms() throws Throwable {
  77 + public void testDoSendSms_success() throws Throwable {
82 78 // 准备参数
83 79 Long sendLogId = randomLongId();
84 80 String mobile = randomString();
... ... @@ -94,7 +90,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
94 90 o.setSendStatusSet(sendStatuses);
95 91 SendStatus sendStatus = new SendStatus();
96 92 sendStatuses[0] = sendStatus;
97   - sendStatus.setCode(TencentSmsClient.API_SUCCESS_CODE);
  93 + sendStatus.setCode(TencentSmsClient.API_CODE_SUCCESS);
98 94 sendStatus.setMessage("send success");
99 95 sendStatus.setSerialNo(serialNo);
100 96 });
... ... @@ -109,20 +105,58 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
109 105 }))).thenReturn(response);
110 106  
111 107 // 调用
112   - SmsCommonResult<SmsSendRespDTO> result = smsClient.doSendSms(sendLogId, mobile,
113   - apiTemplateId, templateParams);
  108 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
114 109 // 断言
  110 + assertTrue(result.getSuccess());
  111 + assertEquals(response.getRequestId(), result.getApiRequestId());
115 112 assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode());
116 113 assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg());
117   - assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
118   - assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
  114 + assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo());
  115 + }
  116 +
  117 + @Test
  118 + public void testDoSendSms_fail() throws Throwable {
  119 + // 准备参数
  120 + Long sendLogId = randomLongId();
  121 + String mobile = randomString();
  122 + String apiTemplateId = randomString();
  123 + List<KeyValue<String, Object>> templateParams = Lists.newArrayList(
  124 + new KeyValue<>("1", 1234), new KeyValue<>("2", "login"));
  125 + String requestId = randomString();
  126 + String serialNo = randomString();
  127 + // mock 方法
  128 + SendSmsResponse response = randomPojo(SendSmsResponse.class, o -> {
  129 + o.setRequestId(requestId);
  130 + SendStatus[] sendStatuses = new SendStatus[1];
  131 + o.setSendStatusSet(sendStatuses);
  132 + SendStatus sendStatus = new SendStatus();
  133 + sendStatuses[0] = sendStatus;
  134 + sendStatus.setCode("ERROR");
  135 + sendStatus.setMessage("send success");
  136 + sendStatus.setSerialNo(serialNo);
  137 + });
  138 + when(client.SendSms(argThat(request -> {
  139 + assertEquals(mobile, request.getPhoneNumberSet()[0]);
  140 + assertEquals(properties.getSignature(), request.getSignName());
  141 + assertEquals(apiTemplateId, request.getTemplateId());
  142 + assertEquals(toJsonString(ArrayUtils.toArray(new ArrayList<>(MapUtils.convertMap(templateParams).values()), String::valueOf)),
  143 + toJsonString(request.getTemplateParamSet()));
  144 + assertEquals(sendLogId, ReflectUtil.getFieldValue(JsonUtils.parseObject(request.getSessionContext(), TencentSmsClient.SessionContext.class), "logId"));
  145 + return true;
  146 + }))).thenReturn(response);
  147 +
  148 + // 调用
  149 + SmsSendRespDTO result = smsClient.sendSms(sendLogId, mobile, apiTemplateId, templateParams);
  150 + // 断言
  151 + assertFalse(result.getSuccess());
119 152 assertEquals(response.getRequestId(), result.getApiRequestId());
120   - // 断言结果
121   - assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getData().getSerialNo());
  153 + assertEquals(response.getSendStatusSet()[0].getCode(), result.getApiCode());
  154 + assertEquals(response.getSendStatusSet()[0].getMessage(), result.getApiMsg());
  155 + assertEquals(response.getSendStatusSet()[0].getSerialNo(), result.getSerialNo());
122 156 }
123 157  
124 158 @Test
125   - public void testDoTParseSmsReceiveStatus() throws Throwable {
  159 + public void testParseSmsReceiveStatus() {
126 160 // 准备参数
127 161 String text = "[\n" +
128 162 " {\n" +
... ... @@ -139,7 +173,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
139 173 // mock 方法
140 174  
141 175 // 调用
142   - List<SmsReceiveRespDTO> statuses = smsClient.doParseSmsReceiveStatus(text);
  176 + List<SmsReceiveRespDTO> statuses = smsClient.parseSmsReceiveStatus(text);
143 177 // 断言
144 178 assertEquals(1, statuses.size());
145 179 assertTrue(statuses.get(0).getSuccess());
... ... @@ -152,7 +186,7 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
152 186 }
153 187  
154 188 @Test
155   - public void testDoGetSmsTemplate() throws Throwable {
  189 + public void testGetSmsTemplate() throws Throwable {
156 190 // 准备参数
157 191 Long apiTemplateId = randomLongId();
158 192 String requestId = randomString();
... ... @@ -173,50 +207,24 @@ public class TencentSmsClientTest extends BaseMockitoUnitTest {
173 207 }))).thenReturn(response);
174 208  
175 209 // 调用
176   - SmsCommonResult<SmsTemplateRespDTO> result = smsClient.doGetSmsTemplate(apiTemplateId.toString());
  210 + SmsTemplateRespDTO result = smsClient.getSmsTemplate(apiTemplateId.toString());
177 211 // 断言
178   - assertEquals(TencentSmsClient.API_SUCCESS_CODE, result.getApiCode());
179   - assertNull(result.getApiMsg());
180   - assertEquals(GlobalErrorCodeConstants.SUCCESS.getCode(), result.getCode());
181   - assertEquals(GlobalErrorCodeConstants.SUCCESS.getMsg(), result.getMsg());
182   - assertEquals(response.getRequestId(), result.getApiRequestId());
183   - // 断言结果
184   - assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateId().toString(), result.getData().getId());
185   - assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateContent(), result.getData().getContent());
186   - assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getData().getAuditStatus());
187   - assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getData().getAuditReason());
188   - }
189   -
190   - @Test
191   - public void testConvertSuccessTemplateStatus() {
192   - testTemplateStatus(SmsTemplateAuditStatusEnum.SUCCESS, 0L);
193   - }
194   -
195   - @Test
196   - public void testConvertCheckingTemplateStatus() {
197   - testTemplateStatus(SmsTemplateAuditStatusEnum.CHECKING, 1L);
198   - }
199   -
200   - @Test
201   - public void testConvertFailTemplateStatus() {
202   - testTemplateStatus(SmsTemplateAuditStatusEnum.FAIL, -1L);
  212 + assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateId().toString(), result.getId());
  213 + assertEquals(response.getDescribeTemplateStatusSet()[0].getTemplateContent(), result.getContent());
  214 + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(), result.getAuditStatus());
  215 + assertEquals(response.getDescribeTemplateStatusSet()[0].getReviewReply(), result.getAuditReason());
203 216 }
204 217  
205 218 @Test
206   - public void testConvertUnknownTemplateStatus() {
207   - DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
208   - templateStatus.setStatusCode(3L);
209   - Long templateId = randomLongId();
210   - // 调用,并断言结果
211   - assertThrows(IllegalStateException.class, () -> smsClient.convertTemplateStatusDTO(templateStatus),
212   - StrUtil.format("不能解析短信模版审核状态[3],模版id[{}]", templateId));
213   - }
214   -
215   - private void testTemplateStatus(SmsTemplateAuditStatusEnum expected, Long value) {
216   - DescribeTemplateListStatus templateStatus = new DescribeTemplateListStatus();
217   - templateStatus.setStatusCode(value);
218   - SmsTemplateRespDTO result = smsClient.convertTemplateStatusDTO(templateStatus);
219   - assertEquals(expected.getStatus(), result.getAuditStatus());
  219 + public void testConvertSmsTemplateAuditStatus() {
  220 + assertEquals(SmsTemplateAuditStatusEnum.SUCCESS.getStatus(),
  221 + smsClient.convertSmsTemplateAuditStatus(0));
  222 + assertEquals(SmsTemplateAuditStatusEnum.CHECKING.getStatus(),
  223 + smsClient.convertSmsTemplateAuditStatus(1));
  224 + assertEquals(SmsTemplateAuditStatusEnum.FAIL.getStatus(),
  225 + smsClient.convertSmsTemplateAuditStatus(-1));
  226 + assertThrows(IllegalArgumentException.class, () -> smsClient.convertSmsTemplateAuditStatus(3),
  227 + "未知审核状态(3)");
220 228 }
221 229  
222 230 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-sms/src/test/java/cn/iocoder/yudao/framework/sms/core/client/impl/tencent/TencentSmsCodeMappingTest.java deleted
1   -package cn.iocoder.yudao.framework.sms.core.client.impl.tencent;
2   -
3   -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
4   -import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
5   -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
6   -import org.junit.jupiter.api.Test;
7   -import org.mockito.InjectMocks;
8   -
9   -import static org.junit.jupiter.api.Assertions.assertEquals;
10   -
11   -/**
12   - * {@link TencentSmsCodeMapping} 的单元测试
13   - *
14   - * @author : shiwp
15   - */
16   -public class TencentSmsCodeMappingTest extends BaseMockitoUnitTest {
17   -
18   - @InjectMocks
19   - private TencentSmsCodeMapping codeMapping;
20   -
21   - @Test
22   - public void testApply() {
23   - assertEquals(GlobalErrorCodeConstants.SUCCESS, codeMapping.apply(TencentSmsClient.API_SUCCESS_CODE));
24   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_CONTENT_INVALID, codeMapping.apply("FailedOperation.ContainSensitiveWord"));
25   - assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("FailedOperation.JsonParseFail"));
26   - assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("MissingParameter.EmptyPhoneNumberSet"));
27   - assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("LimitExceeded.PhoneNumberCountLimit"));
28   - assertEquals(GlobalErrorCodeConstants.BAD_REQUEST, codeMapping.apply("FailedOperation.FailResolvePacket"));
29   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH, codeMapping.apply("FailedOperation.InsufficientBalanceInSmsPackage"));
30   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_MARKET_LIMIT_CONTROL, codeMapping.apply("FailedOperation.MarketingSendTimeConstraint"));
31   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_BLACK, codeMapping.apply("FailedOperation.PhoneNumberInBlacklist"));
32   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SIGN_INVALID, codeMapping.apply("FailedOperation.SignatureIncorrectOrUnapproved"));
33   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("FailedOperation.MissingTemplateToModify"));
34   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_INVALID, codeMapping.apply("FailedOperation.TemplateIncorrectOrUnapproved"));
35   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_MOBILE_INVALID, codeMapping.apply("InvalidParameterValue.IncorrectPhoneNumber"));
36   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_APP_ID_INVALID, codeMapping.apply("InvalidParameterValue.SdkAppIdNotExist"));
37   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("InvalidParameterValue.TemplateParameterLengthLimit"));
38   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR, codeMapping.apply("InvalidParameterValue.TemplateParameterFormatError"));
39   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL, codeMapping.apply("LimitExceeded.PhoneNumberDailyLimit"));
40   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("LimitExceeded.PhoneNumberThirtySecondLimit"));
41   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL, codeMapping.apply("LimitExceeded.PhoneNumberOneHourLimit"));
42   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("UnauthorizedOperation.RequestPermissionDeny"));
43   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("FailedOperation.ForbidAddMarketingTemplates"));
44   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("FailedOperation.NotEnterpriseCertification"));
45   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_PERMISSION_DENY, codeMapping.apply("UnauthorizedOperation.IndividualUserMarketingSmsPermissionDeny"));
46   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_IP_DENY, codeMapping.apply("UnauthorizedOperation.RequestIpNotInWhitelist"));
47   - assertEquals(SmsFrameworkErrorCodeConstants.SMS_ACCOUNT_INVALID, codeMapping.apply("AuthFailure.SecretIdNotFound"));
48   - }
49   -
50   -}
51 0 \ No newline at end of file
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 {
82 82 // ========== 短信模板 1-002-012-000 ==========
83 83 ErrorCode SMS_TEMPLATE_NOT_EXISTS = new ErrorCode(1_002_012_000, "短信模板不存在");
84 84 ErrorCode SMS_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_002_012_001, "已经存在编码为【{}】的短信模板");
  85 + ErrorCode SMS_TEMPLATE_API_ERROR = new ErrorCode(1_002_012_002, "短信 API 模板调用失败,原因是:{}");
  86 + ErrorCode SMS_TEMPLATE_API_AUDIT_CHECKING = new ErrorCode(1_002_012_003, "短信 API 模版无法使用,原因:审批中");
  87 + ErrorCode SMS_TEMPLATE_API_AUDIT_FAIL = new ErrorCode(1_002_012_004, "短信 API 模版无法使用,原因:审批不通过,{}");
  88 + ErrorCode SMS_TEMPLATE_API_NOT_FOUND = new ErrorCode(1_002_012_005, "短信 API 模版无法使用,原因:模版不存在");
85 89  
86 90 // ========== 短信发送 1-002-013-000 ==========
87 91 ErrorCode SMS_SEND_MOBILE_NOT_EXISTS = new ErrorCode(1_002_013_000, "手机号不存在");
... ...
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 {
63 63 @ExcelProperty("发送时间")
64 64 private LocalDateTime sendTime;
65 65  
66   - @ExcelProperty("发送结果的编码")
67   - private Integer sendCode;
68   -
69   - @ExcelProperty("发送结果的提示")
70   - private String sendMsg;
71   -
72 66 @ExcelProperty("短信 API 发送结果的编码")
73 67 private String apiSendCode;
74 68  
... ...
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 {
52 52 @Schema(description = "发送时间")
53 53 private LocalDateTime sendTime;
54 54  
55   - @Schema(description = "发送结果的编码", example = "0")
56   - private Integer sendCode;
57   -
58   - @Schema(description = "发送结果的提示", example = "成功")
59   - private String sendMsg;
60   -
61 55 @Schema(description = "短信 API 发送结果的编码", example = "SUCCESS")
62 56 private String apiSendCode;
63 57  
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/sms/SmsLogDO.java
1 1 package cn.iocoder.yudao.module.system.dal.dataobject.sms;
2 2  
3   -import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum;
4   -import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
5 3 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
6 4 import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
7   -import cn.iocoder.yudao.framework.sms.core.enums.SmsFrameworkErrorCodeConstants;
  5 +import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum;
  6 +import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
8 7 import com.baomidou.mybatisplus.annotation.KeySequence;
9 8 import com.baomidou.mybatisplus.annotation.TableField;
10 9 import com.baomidou.mybatisplus.annotation.TableName;
... ... @@ -116,19 +115,6 @@ public class SmsLogDO extends BaseDO {
116 115 */
117 116 private LocalDateTime sendTime;
118 117 /**
119   - * 发送结果的编码
120   - *
121   - * 枚举 {@link SmsFrameworkErrorCodeConstants}
122   - */
123   - private Integer sendCode;
124   - /**
125   - * 发送结果的提示
126   - *
127   - * 一般情况下,使用 {@link SmsFrameworkErrorCodeConstants}
128   - * 异常情况下,通过格式化 Exception 的提示存储
129   - */
130   - private String sendMsg;
131   - /**
132 118 * 短信 API 发送结果的编码
133 119 *
134 120 * 由于第三方的错误码可能是字符串,所以使用 String 类型
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogService.java
... ... @@ -37,15 +37,15 @@ public interface SmsLogService {
37 37 * 更新日志的发送结果
38 38 *
39 39 * @param id 日志编号
40   - * @param sendCode 发送结果的编码
41   - * @param sendMsg 发送结果的提示
  40 + * @param success 发送是否成功
42 41 * @param apiSendCode 短信 API 发送结果的编码
43 42 * @param apiSendMsg 短信 API 发送失败的提示
44 43 * @param apiRequestId 短信 API 发送返回的唯一请求 ID
45 44 * @param apiSerialNo 短信 API 发送返回的序号
46 45 */
47   - void updateSmsSendResult(Long id, Integer sendCode, String sendMsg,
48   - String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo);
  46 + void updateSmsSendResult(Long id, Boolean success,
  47 + String apiSendCode, String apiSendMsg,
  48 + String apiRequestId, String apiSerialNo);
49 49  
50 50 /**
51 51 * 更新日志的接收结果
... ... @@ -56,7 +56,8 @@ public interface SmsLogService {
56 56 * @param apiReceiveCode API 接收结果的编码
57 57 * @param apiReceiveMsg API 接收结果的说明
58 58 */
59   - void updateSmsReceiveResult(Long id, Boolean success, LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg);
  59 + void updateSmsReceiveResult(Long id, Boolean success,
  60 + LocalDateTime receiveTime, String apiReceiveCode, String apiReceiveMsg);
60 61  
61 62 /**
62 63 * 获得短信日志分页
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsLogServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.service.sms;
2 2  
3   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  3 +import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogExportReqVO;
5 5 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.log.SmsLogPageReqVO;
6 6 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsLogDO;
7 7 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsTemplateDO;
8 8 import cn.iocoder.yudao.module.system.dal.mysql.sms.SmsLogMapper;
9   -import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 9 import cn.iocoder.yudao.module.system.enums.sms.SmsReceiveStatusEnum;
11 10 import cn.iocoder.yudao.module.system.enums.sms.SmsSendStatusEnum;
12 11 import lombok.extern.slf4j.Slf4j;
... ... @@ -55,13 +54,12 @@ public class SmsLogServiceImpl implements SmsLogService {
55 54 }
56 55  
57 56 @Override
58   - public void updateSmsSendResult(Long id, Integer sendCode, String sendMsg,
  57 + public void updateSmsSendResult(Long id, Boolean success,
59 58 String apiSendCode, String apiSendMsg,
60 59 String apiRequestId, String apiSerialNo) {
61   - SmsSendStatusEnum sendStatus = CommonResult.isSuccess(sendCode) ?
62   - SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE;
63   - smsLogMapper.updateById(SmsLogDO.builder().id(id).sendStatus(sendStatus.getStatus())
64   - .sendTime(LocalDateTime.now()).sendCode(sendCode).sendMsg(sendMsg)
  60 + SmsSendStatusEnum sendStatus = success ? SmsSendStatusEnum.SUCCESS : SmsSendStatusEnum.FAILURE;
  61 + smsLogMapper.updateById(SmsLogDO.builder().id(id)
  62 + .sendStatus(sendStatus.getStatus()).sendTime(LocalDateTime.now())
65 63 .apiSendCode(apiSendCode).apiSendMsg(apiSendMsg)
66 64 .apiRequestId(apiRequestId).apiSerialNo(apiSerialNo).build());
67 65 }
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsSendServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.service.sms;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.exceptions.ExceptionUtil;
4 5 import cn.hutool.core.lang.Assert;
5 6 import cn.hutool.core.util.StrUtil;
6 7 import cn.iocoder.yudao.framework.common.core.KeyValue;
... ... @@ -8,7 +9,6 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
8 9 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
9 10 import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
10 11 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
11   -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
12 12 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
13 13 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
14 14 import cn.iocoder.yudao.module.system.dal.dataobject.sms.SmsChannelDO;
... ... @@ -19,6 +19,7 @@ import cn.iocoder.yudao.module.system.mq.producer.sms.SmsProducer;
19 19 import cn.iocoder.yudao.module.system.service.member.MemberService;
20 20 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
21 21 import com.google.common.annotations.VisibleForTesting;
  22 +import lombok.extern.slf4j.Slf4j;
22 23 import org.springframework.stereotype.Service;
23 24  
24 25 import javax.annotation.Resource;
... ... @@ -35,6 +36,7 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
35 36 * @author 芋道源码
36 37 */
37 38 @Service
  39 +@Slf4j
38 40 public class SmsSendServiceImpl implements SmsSendService {
39 41  
40 42 @Resource
... ... @@ -158,11 +160,17 @@ public class SmsSendServiceImpl implements SmsSendService {
158 160 SmsClient smsClient = smsChannelService.getSmsClient(message.getChannelId());
159 161 Assert.notNull(smsClient, "短信客户端({}) 不存在", message.getChannelId());
160 162 // 发送短信
161   - SmsCommonResult<SmsSendRespDTO> sendResult = smsClient.sendSms(message.getLogId(), message.getMobile(),
162   - message.getApiTemplateId(), message.getTemplateParams());
163   - smsLogService.updateSmsSendResult(message.getLogId(), sendResult.getCode(), sendResult.getMsg(),
164   - sendResult.getApiCode(), sendResult.getApiMsg(), sendResult.getApiRequestId(),
165   - sendResult.getData() != null ? sendResult.getData().getSerialNo() : null);
  163 + try {
  164 + SmsSendRespDTO sendResponse = smsClient.sendSms(message.getLogId(), message.getMobile(),
  165 + message.getApiTemplateId(), message.getTemplateParams());
  166 + smsLogService.updateSmsSendResult(message.getLogId(), sendResponse.getSuccess(),
  167 + sendResponse.getApiCode(), sendResponse.getApiMsg(),
  168 + sendResponse.getApiRequestId(), sendResponse.getSerialNo());
  169 + } catch (Throwable ex) {
  170 + log.error("[doSendSms][发送短信异常,日志编号({})]", message.getLogId(), ex);
  171 + smsLogService.updateSmsSendResult(message.getLogId(), false,
  172 + "EXCEPTION", ExceptionUtil.getRootCauseMessage(ex), null, null);
  173 + }
166 174 }
167 175  
168 176 @Override
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java
1 1 package cn.iocoder.yudao.module.system.service.sms;
2 2  
  3 +import cn.hutool.core.exceptions.ExceptionUtil;
  4 +import cn.hutool.core.lang.Assert;
3 5 import cn.hutool.core.util.ReUtil;
4 6 import cn.hutool.core.util.StrUtil;
5 7 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 9 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
8   -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
9 10 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
  11 +import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
10 12 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO;
11 13 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
12 14 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplatePageReqVO;
... ... @@ -21,11 +23,11 @@ import lombok.extern.slf4j.Slf4j;
21 23 import org.springframework.cache.annotation.CacheEvict;
22 24 import org.springframework.cache.annotation.Cacheable;
23 25 import org.springframework.stereotype.Service;
24   -import org.springframework.util.Assert;
25 26  
26 27 import javax.annotation.Resource;
27 28 import java.util.List;
28 29 import java.util.Map;
  30 +import java.util.Objects;
29 31 import java.util.regex.Pattern;
30 32  
31 33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
... ... @@ -171,9 +173,24 @@ public class SmsTemplateServiceImpl implements SmsTemplateService {
171 173 // 获得短信模板
172 174 SmsClient smsClient = smsChannelService.getSmsClient(channelId);
173 175 Assert.notNull(smsClient, String.format("短信客户端(%d) 不存在", channelId));
174   - SmsCommonResult<SmsTemplateRespDTO> templateResult = smsClient.getSmsTemplate(apiTemplateId);
175   - // 校验短信模板是否正确
176   - templateResult.checkError();
  176 + SmsTemplateRespDTO template;
  177 + try {
  178 + template = smsClient.getSmsTemplate(apiTemplateId);
  179 + } catch (Throwable ex) {
  180 + throw exception(SMS_TEMPLATE_API_ERROR, ExceptionUtil.getRootCauseMessage(ex));
  181 + }
  182 + // 校验短信模版
  183 + if (template == null) {
  184 + throw exception(SMS_TEMPLATE_API_NOT_FOUND);
  185 + }
  186 + if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.CHECKING.getStatus())) {
  187 + throw exception(SMS_TEMPLATE_API_AUDIT_CHECKING);
  188 + }
  189 + if (Objects.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.FAIL.getStatus())) {
  190 + throw exception(SMS_TEMPLATE_API_AUDIT_FAIL, template.getAuditReason());
  191 + }
  192 + Assert.equals(template.getAuditStatus(), SmsTemplateAuditStatusEnum.SUCCESS.getStatus(),
  193 + String.format("短信模板(%s) 审核状态(%d) 不正确", apiTemplateId, template.getAuditStatus()));
177 194 }
178 195  
179 196 @Override
... ...
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;
2 2  
3 3 import cn.hutool.core.map.MapUtil;
4 4 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
5   -import cn.iocoder.yudao.framework.common.pojo.CommonResult;
6 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
7 6 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
8 7 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
... ... @@ -172,22 +171,20 @@ public class SmsLogServiceImplTest extends BaseDbUnitTest {
172 171 smsLogMapper.insert(dbSmsLog);
173 172 // 准备参数
174 173 Long id = dbSmsLog.getId();
175   - Integer sendCode = randomInteger();
176   - String sendMsg = randomString();
  174 + Boolean success = randomBoolean();
177 175 String apiSendCode = randomString();
178 176 String apiSendMsg = randomString();
179 177 String apiRequestId = randomString();
180 178 String apiSerialNo = randomString();
181 179  
182 180 // 调用
183   - smsLogService.updateSmsSendResult(id, sendCode, sendMsg,
  181 + smsLogService.updateSmsSendResult(id, success,
184 182 apiSendCode, apiSendMsg, apiRequestId, apiSerialNo);
185 183 // 断言
186 184 dbSmsLog = smsLogMapper.selectById(id);
187   - assertEquals(CommonResult.isSuccess(sendCode) ? SmsSendStatusEnum.SUCCESS.getStatus()
188   - : SmsSendStatusEnum.FAILURE.getStatus(), dbSmsLog.getSendStatus());
  185 + assertEquals(success ? SmsSendStatusEnum.SUCCESS.getStatus() : SmsSendStatusEnum.FAILURE.getStatus(),
  186 + dbSmsLog.getSendStatus());
189 187 assertNotNull(dbSmsLog.getSendTime());
190   - assertEquals(sendMsg, dbSmsLog.getSendMsg());
191 188 assertEquals(apiSendCode, dbSmsLog.getApiSendCode());
192 189 assertEquals(apiSendMsg, dbSmsLog.getApiSendMsg());
193 190 assertEquals(apiRequestId, dbSmsLog.getApiRequestId());
... ...
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;
5 5 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
6 6 import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
7 7 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
8   -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
9 8 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsReceiveRespDTO;
10 9 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsSendRespDTO;
11 10 import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
... ... @@ -244,15 +243,14 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
244 243  
245 244 @Test
246 245 @SuppressWarnings("unchecked")
247   - public void testDoSendSms() {
  246 + public void testDoSendSms() throws Throwable {
248 247 // 准备参数
249 248 SmsSendMessage message = randomPojo(SmsSendMessage.class);
250 249 // mock SmsClientFactory 的方法
251 250 SmsClient smsClient = spy(SmsClient.class);
252 251 when(smsChannelService.getSmsClient(eq(message.getChannelId()))).thenReturn(smsClient);
253 252 // mock SmsClient 的方法
254   - SmsCommonResult<SmsSendRespDTO> sendResult = randomPojo(SmsCommonResult.class, SmsSendRespDTO.class);
255   - sendResult.setData(randomPojo(SmsSendRespDTO.class));
  253 + SmsSendRespDTO sendResult = randomPojo(SmsSendRespDTO.class);
256 254 when(smsClient.sendSms(eq(message.getLogId()), eq(message.getMobile()), eq(message.getApiTemplateId()),
257 255 eq(message.getTemplateParams()))).thenReturn(sendResult);
258 256  
... ... @@ -260,8 +258,8 @@ public class SmsSendServiceImplTest extends BaseMockitoUnitTest {
260 258 smsService.doSendSms(message);
261 259 // 断言
262 260 verify(smsLogService).updateSmsSendResult(eq(message.getLogId()),
263   - eq(sendResult.getCode()), eq(sendResult.getMsg()), eq(sendResult.getApiCode()),
264   - eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getData().getSerialNo()));
  261 + eq(sendResult.getSuccess()), eq(sendResult.getApiCode()),
  262 + eq(sendResult.getApiMsg()), eq(sendResult.getApiRequestId()), eq(sendResult.getSerialNo()));
265 263 }
266 264  
267 265 @Test
... ...
yudao-module-system/yudao-module-system-biz/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java
1 1 package cn.iocoder.yudao.module.system.service.sms;
2 2  
3 3 import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4   -import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
5 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 5 import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
7 6 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
8 7 import cn.iocoder.yudao.framework.sms.core.client.SmsClient;
9   -import cn.iocoder.yudao.framework.sms.core.client.SmsCommonResult;
10 8 import cn.iocoder.yudao.framework.sms.core.client.dto.SmsTemplateRespDTO;
  9 +import cn.iocoder.yudao.framework.sms.core.enums.SmsTemplateAuditStatusEnum;
11 10 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
12 11 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateCreateReqVO;
13 12 import cn.iocoder.yudao.module.system.controller.admin.sms.vo.template.SmsTemplateExportReqVO;
... ... @@ -65,7 +64,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
65 64  
66 65 @Test
67 66 @SuppressWarnings("unchecked")
68   - public void testCreateSmsTemplate_success() {
  67 + public void testCreateSmsTemplate_success() throws Throwable {
69 68 // 准备参数
70 69 SmsTemplateCreateReqVO reqVO = randomPojo(SmsTemplateCreateReqVO.class, o -> {
71 70 o.setContent("正在进行登录操作{operation},您的验证码是{code}");
... ... @@ -80,8 +79,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
80 79 when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO);
81 80 // mock 获得 API 短信模板成功
82 81 when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient);
83   - when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class,
84   - o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode())));
  82 + when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(
  83 + randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus())));
85 84  
86 85 // 调用
87 86 Long smsTemplateId = smsTemplateService.createSmsTemplate(reqVO);
... ... @@ -96,7 +95,7 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
96 95  
97 96 @Test
98 97 @SuppressWarnings("unchecked")
99   - public void testUpdateSmsTemplate_success() {
  98 + public void testUpdateSmsTemplate_success() throws Throwable {
100 99 // mock 数据
101 100 SmsTemplateDO dbSmsTemplate = randomSmsTemplateDO();
102 101 smsTemplateMapper.insert(dbSmsTemplate);// @Sql: 先插入出一条存在的数据
... ... @@ -115,8 +114,8 @@ public class SmsTemplateServiceImplTest extends BaseDbUnitTest {
115 114 when(smsChannelService.getSmsChannel(eq(channelDO.getId()))).thenReturn(channelDO);
116 115 // mock 获得 API 短信模板成功
117 116 when(smsChannelService.getSmsClient(eq(reqVO.getChannelId()))).thenReturn(smsClient);
118   - when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(randomPojo(SmsCommonResult.class, SmsTemplateRespDTO.class,
119   - o -> o.setCode(GlobalErrorCodeConstants.SUCCESS.getCode())));
  117 + when(smsClient.getSmsTemplate(eq(reqVO.getApiTemplateId()))).thenReturn(
  118 + randomPojo(SmsTemplateRespDTO.class, o -> o.setAuditStatus(SmsTemplateAuditStatusEnum.SUCCESS.getStatus())));
120 119  
121 120 // 调用
122 121 smsTemplateService.updateSmsTemplate(reqVO);
... ...