Commit 515fca5c41819e8187167c483e05d21cd4b4b029

Authored by YunaiV
1 parent 46ed64ba

短信提交 2021-03-28,增加发送日志

Showing 33 changed files with 544 additions and 930 deletions
@@ -46,6 +46,7 @@ @@ -46,6 +46,7 @@
46 <easyexcel.verion>2.2.7</easyexcel.verion> 46 <easyexcel.verion>2.2.7</easyexcel.verion>
47 <velocity.version>2.2</velocity.version> 47 <velocity.version>2.2</velocity.version>
48 <screw.version>1.0.5</screw.version> 48 <screw.version>1.0.5</screw.version>
  49 + <!-- 三方云服务相关 -->
49 </properties> 50 </properties>
50 51
51 <!-- 依赖声明 --> 52 <!-- 依赖声明 -->
@@ -271,6 +272,8 @@ @@ -271,6 +272,8 @@
271 <version>${screw.version}</version> 272 <version>${screw.version}</version>
272 </dependency> 273 </dependency>
273 274
  275 + <!-- 三方云服务相关 -->
  276 +
274 <!-- SMS SDK begin --> 277 <!-- SMS SDK begin -->
275 <dependency> 278 <dependency>
276 <groupId>com.yunpian.sdk</groupId> 279 <groupId>com.yunpian.sdk</groupId>
src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java deleted
1 -package cn.iocoder.dashboard.framework.sms.client;  
2 -  
3 -import cn.iocoder.dashboard.framework.sms.core.SmsBody;  
4 -import cn.iocoder.dashboard.framework.sms.core.SmsResult;  
5 -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;  
6 -import lombok.extern.slf4j.Slf4j;  
7 -  
8 -/**  
9 - * 抽象短息客户端  
10 - *  
11 - * @author zzf  
12 - * @date 2021/2/1 9:28  
13 - */  
14 -@Slf4j  
15 -public abstract class AbstractSmsClient implements SmsClient {  
16 -  
17 - /**  
18 - * 短信渠道参数  
19 - */  
20 - protected final SmsChannelProperty channelVO;  
21 -  
22 - /**  
23 - * 短信客户端有参构造函数  
24 - *  
25 - * @param property 短信配置  
26 - */  
27 - public AbstractSmsClient(SmsChannelProperty property) {  
28 - this.channelVO = property;  
29 - }  
30 -  
31 - public SmsChannelProperty getProperty() {  
32 - return channelVO;  
33 - }  
34 -  
35 - @Override  
36 - public final SmsResult send(String templateApiId, SmsBody smsBody, String target) {  
37 - SmsResult result;  
38 - try {  
39 - beforeSend(templateApiId, smsBody, target);  
40 - result = doSend(templateApiId, smsBody, target);  
41 - afterSend(templateApiId, smsBody, target, result);  
42 - } catch (Exception e) {  
43 - // exception handle  
44 - log.debug(e.getMessage(), e);  
45 - return SmsResult.failResult("发送异常: " + e.getMessage());  
46 - }  
47 - return result;  
48 - }  
49 -  
50 - /**  
51 - * 发送消息  
52 - *  
53 - * @param templateApiId 短信模板唯一标识  
54 - * @param smsBody 消息内容  
55 - * @param targetPhone 发送对象手机号  
56 - * @return 短信发送结果  
57 - * @throws Exception 调用发送失败,抛出异常  
58 - */  
59 - protected abstract SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception;  
60 -  
61 - protected void beforeSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception {  
62 - }  
63 -  
64 - protected void afterSend(String templateApiId, SmsBody smsBody, String targetPhone, SmsResult result) throws Exception {  
65 - }  
66 -  
67 -}  
src/main/java/cn/iocoder/dashboard/framework/sms/config/SmsConfiguration.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.config;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
  4 +import cn.iocoder.dashboard.framework.sms.core.client.impl.SmsClientFactoryImpl;
  5 +import org.springframework.context.annotation.Bean;
  6 +import org.springframework.context.annotation.Configuration;
  7 +
  8 +/**
  9 + * 短信配置类
  10 + *
  11 + * @author 芋道源码
  12 + */
  13 +@Configuration
  14 +public class SmsConfiguration {
  15 +
  16 + @Bean
  17 + public SmsClientFactory smsClientFactory() {
  18 + return new SmsClientFactoryImpl();
  19 + }
  20 +
  21 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java deleted
1 -package cn.iocoder.dashboard.framework.sms.core;  
2 -  
3 -import cn.iocoder.dashboard.util.json.JsonUtils;  
4 -import lombok.Data;  
5 -  
6 -import java.util.Map;  
7 -  
8 -/**  
9 - * 消息内容实体类  
10 - */  
11 -@Data  
12 -public class SmsBody {  
13 -  
14 - /**  
15 - * 消息日志id  
16 - */  
17 - private Long smsLogId;  
18 -  
19 - /**  
20 - * 模板编码  
21 - */  
22 - private String templateCode;  
23 -  
24 - /**  
25 - * 模板编码  
26 - */  
27 - private String templateContent;  
28 -  
29 - /**  
30 - * 参数列表  
31 - */  
32 - private Map<String, String> params;  
33 -  
34 - public String getParamsStr() {  
35 - return JsonUtils.toJsonString(params);  
36 - }  
37 -  
38 -}  
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java deleted
1 -package cn.iocoder.dashboard.framework.sms.core;  
2 -  
3 -import cn.iocoder.dashboard.common.exception.ServiceException;  
4 -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;  
5 -import cn.iocoder.dashboard.framework.sms.client.impl.ali.AliyunSmsClient;  
6 -import cn.iocoder.dashboard.framework.sms.client.impl.yunpian.YunpianSmsClient;  
7 -import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;  
8 -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;  
9 -import cn.iocoder.dashboard.framework.sms.core.property.SmsTemplateProperty;  
10 -import cn.iocoder.dashboard.util.json.JsonUtils;  
11 -import org.springframework.stereotype.Component;  
12 -  
13 -import javax.servlet.ServletRequest;  
14 -import java.util.Collection;  
15 -import java.util.Map;  
16 -import java.util.concurrent.ConcurrentHashMap;  
17 -  
18 -import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;  
19 -  
20 -/**  
21 - * 短信客户端工厂  
22 - *  
23 - * @author zzf  
24 - * @date 2021/1/28 14:01  
25 - */  
26 -@Component  
27 -public class SmsClientFactory {  
28 -  
29 - /**  
30 - * channelId: client map  
31 - * 保存 渠道id: 对应短信客户端 的map  
32 - */  
33 - private final Map<Long, AbstractSmsClient> smsSenderMap = new ConcurrentHashMap<>(8);  
34 -  
35 - /**  
36 - * templateCode: TemplateProperty map  
37 - * 保存 模板编码:模板信息 的map  
38 - */  
39 - private final Map<String, SmsTemplateProperty> templatePropertyMap = new ConcurrentHashMap<>(16);  
40 -  
41 - /**  
42 - * 创建短信客户端  
43 - *  
44 - * @param propertyVO 参数对象  
45 - * @return 客户端id(默认channelId)  
46 - */  
47 - public Long createClient(SmsChannelProperty propertyVO) {  
48 - AbstractSmsClient sender = createClient(SmsChannelEnum.getByCode(propertyVO.getCode()), propertyVO);  
49 - smsSenderMap.put(propertyVO.getId(), sender);  
50 - return propertyVO.getId();  
51 - }  
52 -  
53 - private AbstractSmsClient createClient(SmsChannelEnum channelEnum, SmsChannelProperty channelVO) {  
54 - if (channelEnum == null) {  
55 - throw new ServiceException(INVALID_CHANNEL_CODE);  
56 - }  
57 - switch (channelEnum) {  
58 - case ALIYUN:  
59 - return new AliyunSmsClient(channelVO);  
60 - case YUN_PIAN:  
61 - return new YunpianSmsClient(channelVO);  
62 - // TODO fill more channel  
63 - default:  
64 - break;  
65 - }  
66 - throw new ServiceException(SMS_SENDER_NOT_FOUND);  
67 - }  
68 -  
69 - /**  
70 - * 获取短信客户端  
71 - *  
72 - * @param channelId 渠道id  
73 - * @return 短信id  
74 - */  
75 - public AbstractSmsClient getClient(Long channelId) {  
76 - return smsSenderMap.get(channelId);  
77 - }  
78 -  
79 -  
80 - /**  
81 - * 添加或修改短信模板信息缓存  
82 - */  
83 - public void addOrUpdateTemplateCache(Collection<SmsTemplateProperty> templateProperties) {  
84 - templateProperties.forEach(this::addOrUpdateTemplateCache);  
85 - }  
86 -  
87 -  
88 - /**  
89 - * 添加或修改短信模板信息缓存  
90 - */  
91 - public void addOrUpdateTemplateCache(SmsTemplateProperty templateProperty) {  
92 - templatePropertyMap.put(templateProperty.getCode(), templateProperty);  
93 - }  
94 -  
95 -  
96 - /**  
97 - * 根据短信模板编码获取模板唯一标识  
98 - *  
99 - * @param templateCode 短信模板编码  
100 - * @return 短信id  
101 - */  
102 - public String getTemplateApiIdByCode(String templateCode) {  
103 - SmsTemplateProperty smsTemplateProperty = templatePropertyMap.get(templateCode);  
104 - if (smsTemplateProperty == null) {  
105 - throw new ServiceException(SMS_TEMPLATE_NOT_EXISTS);  
106 - }  
107 - return smsTemplateProperty.getApiTemplateId();  
108 - }  
109 -  
110 -  
111 - /**  
112 - * 从短信发送回调函数请求中获取用于唯一确定一条send_lod的apiId  
113 - *  
114 - * @param callbackRequest 短信发送回调函数请求  
115 - * @return 第三方平台短信唯一标识  
116 - */  
117 - public SmsResultDetail getSmsResultDetailFromCallbackQuery(ServletRequest callbackRequest) {  
118 - for (Long channelId : smsSenderMap.keySet()) {  
119 - AbstractSmsClient smsClient = smsSenderMap.get(channelId);  
120 - try {  
121 - SmsResultDetail smsSendResult = smsClient.smsSendCallbackHandle(callbackRequest);  
122 - if (smsSendResult != null) {  
123 - return smsSendResult;  
124 - }  
125 - } catch (Exception ignored) {  
126 - }  
127 - }  
128 - throw new IllegalArgumentException("getSmsResultDetailFromCallbackQuery fail! don't match SmsClient by RequestParam: "  
129 - + JsonUtils.toJsonString(callbackRequest.getParameterMap()));  
130 - }  
131 -  
132 -  
133 -}  
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsConstants.java
@@ -15,4 +15,5 @@ public interface SmsConstants { @@ -15,4 +15,5 @@ public interface SmsConstants {
15 String COMMA = ","; 15 String COMMA = ",";
16 16
17 String SUCCESS = "SUCCESS"; 17 String SUCCESS = "SUCCESS";
  18 +
18 } 19 }
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java
1 package cn.iocoder.dashboard.framework.sms.core; 1 package cn.iocoder.dashboard.framework.sms.core;
2 2
  3 +import cn.hutool.core.exceptions.ExceptionUtil;
  4 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum;
3 import lombok.Data; 5 import lombok.Data;
4 -import lombok.experimental.Accessors;  
5 6
6 import java.io.Serializable; 7 import java.io.Serializable;
7 8
@@ -9,33 +10,64 @@ import java.io.Serializable; @@ -9,33 +10,64 @@ import java.io.Serializable;
9 * 消息内容实体类 10 * 消息内容实体类
10 */ 11 */
11 @Data 12 @Data
12 -@Accessors(chain = true)  
13 public class SmsResult implements Serializable { 13 public class SmsResult implements Serializable {
14 14
15 /** 15 /**
16 - * 是否成功(发送短信的请求是否成功) 16 + * 是否成功
  17 + *
  18 + * 注意,是调用 API 短信平台的请求是否成功
17 */ 19 */
18 private Boolean success; 20 private Boolean success;
19 -  
20 /** 21 /**
21 - * 第三方唯一标识 22 + * 发送失败的类型
  23 + *
  24 + * 枚举 {@link SmsSendFailureTypeEnum#getType()}
22 */ 25 */
23 - private String apiId;  
24 - 26 + private Integer sendFailureType;
25 /** 27 /**
26 - * 状态码 28 + * 发送失败的提示
  29 + *
  30 + * 一般情况下,使用 {@link SmsSendFailureTypeEnum#getMsg()}
  31 + * 异常情况下,通过格式化 Exception 的提示存储
27 */ 32 */
28 - private String code; 33 + private String sendFailureMsg;
29 34
30 /** 35 /**
31 - * 提示 36 + * 短信 API 发送的错误码
  37 + *
  38 + * 由于第三方的错误码可能是字符串,所以使用 String 类型
  39 + */
  40 + private String apiSendCode;
  41 + /**
  42 + * 短信 API 发送的提示
32 */ 43 */
33 - private String message; 44 + private String apiSendMsg;
  45 + /**
  46 + * 短信 API 发送返回的唯一请求 ID
  47 + *
  48 + * 用于和短信 API 进行定位于排错
  49 + */
  50 + private String apiRequestId;
  51 + /**
  52 + * 短信 API 发送返回的序号
  53 + *
  54 + * 用于和短信 API 平台的发送记录关联
  55 + */
  56 + private String apiSerialNo;
  57 +
  58 + private SmsResult() {
  59 + }
34 60
35 - public static SmsResult failResult(String message) {  
36 - SmsResult resultBody = new SmsResult();  
37 - resultBody.setSuccess(false);  
38 - resultBody.setMessage(message);  
39 - return resultBody; 61 + public static SmsResult success(SmsSendFailureTypeEnum sendFailureType,
  62 + String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo) {
  63 + return new SmsResult().setSuccess(true).setSendFailureType(sendFailureType.getType()).setSendFailureMsg(sendFailureType.getMsg())
  64 + .setApiSendCode(apiSendCode).setApiSendMsg(apiSendMsg).setApiRequestId(apiRequestId).setApiSerialNo(apiSerialNo);
40 } 65 }
  66 +
  67 + public static SmsResult error(Throwable ex) {
  68 + return new SmsResult().setSuccess(false)
  69 + .setSendFailureType(SmsSendFailureTypeEnum.SMS_SEND_EXCEPTION.getType())
  70 + .setSendFailureMsg(ExceptionUtil.getRootCauseMessage(ex));
  71 + }
  72 +
41 } 73 }
src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java
1 -package cn.iocoder.dashboard.framework.sms.client; 1 +package cn.iocoder.dashboard.framework.sms.core.client;
2 2
3 -import cn.iocoder.dashboard.framework.sms.core.SmsBody;  
4 import cn.iocoder.dashboard.framework.sms.core.SmsResult; 3 import cn.iocoder.dashboard.framework.sms.core.SmsResult;
5 import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; 4 import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
6 5
7 import javax.servlet.ServletRequest; 6 import javax.servlet.ServletRequest;
  7 +import java.util.Map;
8 8
9 /** 9 /**
10 - * 短信父接口 10 + * 短信客户端接口
11 * 11 *
12 * @author zzf 12 * @author zzf
13 * @date 2021/1/25 14:14 13 * @date 2021/1/25 14:14
@@ -15,14 +15,22 @@ import javax.servlet.ServletRequest; @@ -15,14 +15,22 @@ import javax.servlet.ServletRequest;
15 public interface SmsClient { 15 public interface SmsClient {
16 16
17 /** 17 /**
  18 + * 获得渠道编号
  19 + *
  20 + * @return 渠道编号
  21 + */
  22 + Long getId();
  23 +
  24 + /**
18 * 发送消息 25 * 发送消息
19 * 26 *
20 - * @param templateApiId 短信模板唯一标识  
21 - * @param smsBody 消息内容  
22 - * @param targets 发送对象列表 27 + * @param sendLogId 发送日志编号
  28 + * @param mobile 手机号
  29 + * @param apiTemplateId 短信 API 的模板编号
  30 + * @param templateParams 短信模板参数
23 * @return 短信发送结果 31 * @return 短信发送结果
24 */ 32 */
25 - SmsResult send(String templateApiId, SmsBody smsBody, String targets); 33 + SmsResult send(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams);
26 34
27 // TODO FROM 芋艿 to ZZF:是不是可以改成意图更明确的解析返回结果,例如说 parseXXXX 35 // TODO FROM 芋艿 to ZZF:是不是可以改成意图更明确的解析返回结果,例如说 parseXXXX
28 /** 36 /**
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
  4 +
  5 +/**
  6 + * 短信客户端工厂接口
  7 + *
  8 + * @author zzf
  9 + * @date 2021/1/28 14:01
  10 + */
  11 +public interface SmsClientFactory {
  12 +
  13 + /**
  14 + * 获得短信 Client
  15 + *
  16 + * @param channelId 渠道编号
  17 + * @return 短信 Client
  18 + */
  19 + SmsClient getSmsClient(Long channelId);
  20 +
  21 + /**
  22 + * 创建短信 Client
  23 + *
  24 + * @param properties 配置对象
  25 + */
  26 + void createOrUpdateSmsClient(SmsChannelProperties properties);
  27 +
  28 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.impl;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.core.SmsResult;
  4 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
  5 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
  6 +import lombok.extern.slf4j.Slf4j;
  7 +
  8 +import java.util.Map;
  9 +
  10 +/**
  11 + * 短信客户端抽象类
  12 + *
  13 + * @author zzf
  14 + * @date 2021/2/1 9:28
  15 + */
  16 +@Slf4j
  17 +public abstract class AbstractSmsClient implements SmsClient {
  18 +
  19 + /**
  20 + * 短信渠道配置
  21 + */
  22 + protected volatile SmsChannelProperties properties;
  23 +
  24 + /**
  25 + * 短信客户端有参构造函数
  26 + *
  27 + * @param properties 短信配置
  28 + */
  29 + public AbstractSmsClient(SmsChannelProperties properties) {
  30 + this.properties = properties;
  31 + }
  32 +
  33 + /**
  34 + * 初始化
  35 + */
  36 + public final void init() {
  37 + doInit();
  38 + log.info("[init][配置({}) 初始化完成]", properties);
  39 + }
  40 +
  41 + public final void refresh(SmsChannelProperties properties) {
  42 + // 判断是否更新
  43 + if (!properties.equals(this.properties)) {
  44 + return;
  45 + }
  46 + log.info("[refresh][配置({})发生变化,重新初始化]", properties);
  47 + this.properties = properties;
  48 + // 初始化
  49 + this.init();
  50 + }
  51 +
  52 + /**
  53 + * 自定义初始化
  54 + */
  55 + protected abstract void doInit();
  56 +
  57 + @Override
  58 + public Long getId() {
  59 + return properties.getId();
  60 + }
  61 +
  62 + @Override
  63 + public final SmsResult send(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams) {
  64 + SmsResult result;
  65 + try {
  66 + result = doSend(sendLogId, mobile, apiTemplateId, templateParams);
  67 + } catch (Throwable ex) {
  68 + // 打印异常日志
  69 + log.error("[send][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]",
  70 + sendLogId, mobile, apiTemplateId, templateParams, ex);
  71 + // 封装返回
  72 + return SmsResult.error(ex);
  73 + }
  74 + return result;
  75 + }
  76 +
  77 + /**
  78 + * 发送消息
  79 + *
  80 + * @param sendLogId 发送日志编号
  81 + * @param mobile 手机号
  82 + * @param apiTemplateId 短信 API 的模板编号
  83 + * @param templateParams 短信模板参数
  84 + * @return 短信发送结果
  85 + */
  86 + protected abstract SmsResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams)
  87 + throws Throwable;
  88 +
  89 +}
src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java 0 → 100644
  1 +package cn.iocoder.dashboard.framework.sms.core.client.impl;
  2 +
  3 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
  4 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
  5 +import cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun.AliyunSmsClient;
  6 +import cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian.YunpianSmsClient;
  7 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
  8 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
  9 +import lombok.extern.slf4j.Slf4j;
  10 +import org.springframework.util.Assert;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import java.util.Map;
  14 +import java.util.concurrent.ConcurrentHashMap;
  15 +
  16 +/**
  17 + * 短信客户端工厂接口
  18 + *
  19 + * @author zzf
  20 + */
  21 +@Validated
  22 +@Slf4j
  23 +public class SmsClientFactoryImpl implements SmsClientFactory {
  24 +
  25 + /**
  26 + * 短信客户端 Map
  27 + * key:渠道编号,使用 {@link SmsChannelProperties#getId()}
  28 + */
  29 + private final Map<Long, AbstractSmsClient> clients = new ConcurrentHashMap<>();
  30 +
  31 + @Override
  32 + public SmsClient getSmsClient(Long channelId) {
  33 + return clients.get(channelId);
  34 + }
  35 +
  36 + @Override
  37 + public void createOrUpdateSmsClient(SmsChannelProperties properties) {
  38 + AbstractSmsClient client = clients.get(properties.getId());
  39 + if (client == null) {
  40 + client = this.createSmsClient(properties);
  41 + clients.put(client.getId(), client);
  42 + } else {
  43 + client.refresh(properties);
  44 + }
  45 + }
  46 +
  47 + private AbstractSmsClient createSmsClient(SmsChannelProperties properties) {
  48 + SmsChannelEnum channelEnum = SmsChannelEnum.getByCode(properties.getCode());
  49 + Assert.notNull(channelEnum, String.format("渠道类型(%s) 为空", channelEnum));
  50 + // 创建客户端
  51 + switch (channelEnum) {
  52 + case ALIYUN:
  53 + return new AliyunSmsClient(properties);
  54 + case YUN_PIAN:
  55 + return new YunpianSmsClient(properties);
  56 + }
  57 + // 创建失败,错误日志 + 抛出异常
  58 + log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties);
  59 + throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", properties));
  60 + }
  61 +
  62 +// /**
  63 +// * 从短信发送回调函数请求中获取用于唯一确定一条send_lod的apiId
  64 +// *
  65 +// * @param callbackRequest 短信发送回调函数请求
  66 +// * @return 第三方平台短信唯一标识
  67 +// */
  68 +// public SmsResultDetail getSmsResultDetailFromCallbackQuery(ServletRequest callbackRequest) {
  69 +// for (Long channelId : clients.keySet()) {
  70 +// AbstractSmsClient smsClient = clients.get(channelId);
  71 +// try {
  72 +// SmsResultDetail smsSendResult = smsClient.smsSendCallbackHandle(callbackRequest);
  73 +// if (smsSendResult != null) {
  74 +// return smsSendResult;
  75 +// }
  76 +// } catch (Exception ignored) {
  77 +// }
  78 +// }
  79 +// throw new IllegalArgumentException("getSmsResultDetailFromCallbackQuery fail! don't match SmsClient by RequestParam: "
  80 +// + JsonUtils.toJsonString(callbackRequest.getParameterMap()));
  81 +// }
  82 +
  83 +}
src/main/java/cn/iocoder/dashboard/framework/sms/client/impl/ali/AliyunSmsClient.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java
1 -package cn.iocoder.dashboard.framework.sms.client.impl.ali; 1 +package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun;
2 2
3 import cn.hutool.core.collection.CollectionUtil; 3 import cn.hutool.core.collection.CollectionUtil;
4 import cn.hutool.core.date.DateUtil; 4 import cn.hutool.core.date.DateUtil;
5 -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;  
6 -import cn.iocoder.dashboard.framework.sms.core.SmsBody;  
7 import cn.iocoder.dashboard.framework.sms.core.SmsResult; 5 import cn.iocoder.dashboard.framework.sms.core.SmsResult;
8 import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; 6 import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
9 -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; 7 +import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient;
  8 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
10 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; 9 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
11 import cn.iocoder.dashboard.util.json.JsonUtils; 10 import cn.iocoder.dashboard.util.json.JsonUtils;
12 import com.aliyuncs.DefaultAcsClient; 11 import com.aliyuncs.DefaultAcsClient;
13 import com.aliyuncs.IAcsClient; 12 import com.aliyuncs.IAcsClient;
14 -import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;  
15 -import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;  
16 -import com.aliyuncs.http.MethodType;  
17 import com.aliyuncs.profile.DefaultProfile; 13 import com.aliyuncs.profile.DefaultProfile;
18 import com.aliyuncs.profile.IClientProfile; 14 import com.aliyuncs.profile.IClientProfile;
19 import com.fasterxml.jackson.core.type.TypeReference; 15 import com.fasterxml.jackson.core.type.TypeReference;
@@ -28,7 +24,7 @@ import java.util.List; @@ -28,7 +24,7 @@ import java.util.List;
28 import java.util.Map; 24 import java.util.Map;
29 25
30 /** 26 /**
31 - * 阿里短信实现类 27 + * 阿里短信客户端的实现类
32 * 28 *
33 * @author zzf 29 * @author zzf
34 * @date 2021/1/25 14:17 30 * @date 2021/1/25 14:17
@@ -36,54 +32,54 @@ import java.util.Map; @@ -36,54 +32,54 @@ import java.util.Map;
36 @Slf4j 32 @Slf4j
37 public class AliyunSmsClient extends AbstractSmsClient { 33 public class AliyunSmsClient extends AbstractSmsClient {
38 34
39 - private static final String OK = "OK";  
40 -  
41 private static final String PRODUCT = "Dystopi"; 35 private static final String PRODUCT = "Dystopi";
42 -  
43 private static final String DOMAIN = "dysmsapi.aliyuncs.com"; 36 private static final String DOMAIN = "dysmsapi.aliyuncs.com";
44 -  
45 private static final String ENDPOINT = "cn-hangzhou"; 37 private static final String ENDPOINT = "cn-hangzhou";
46 38
47 - private final IAcsClient acsClient; 39 + private static final String OK = "OK";
48 40
49 /** 41 /**
50 - * 构造阿里云短信发送处理  
51 - *  
52 - * @param channelVO 阿里云短信配置 42 + * 阿里云客户端
53 */ 43 */
54 - public AliyunSmsClient(SmsChannelProperty channelVO) {  
55 - super(channelVO); 44 + private volatile IAcsClient acsClient;
56 45
57 - String accessKeyId = channelVO.getApiKey();  
58 - String accessKeySecret = channelVO.getApiSecret(); 46 + public AliyunSmsClient(SmsChannelProperties properties) {
  47 + super(properties);
  48 + }
59 49
60 - IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, accessKeyId, accessKeySecret); 50 + @Override
  51 + protected void doInit() {
  52 + IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, properties.getApiKey(), properties.getApiSecret());
61 DefaultProfile.addEndpoint(ENDPOINT, PRODUCT, DOMAIN); 53 DefaultProfile.addEndpoint(ENDPOINT, PRODUCT, DOMAIN);
62 -  
63 acsClient = new DefaultAcsClient(profile); 54 acsClient = new DefaultAcsClient(profile);
64 } 55 }
65 56
66 @Override 57 @Override
67 - public SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception {  
68 - SendSmsRequest request = new SendSmsRequest();  
69 - request.setSysMethod(MethodType.POST);  
70 - request.setPhoneNumbers(targetPhone);  
71 - request.setSignName(channelVO.getApiSignatureId());  
72 - request.setTemplateCode(templateApiId);  
73 - request.setTemplateParam(smsBody.getParamsStr());  
74 - SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);  
75 -  
76 - boolean success = OK.equals(sendSmsResponse.getCode());  
77 - if (!success) {  
78 - log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage());  
79 - }  
80 - return new SmsResult()  
81 - .setSuccess(success)  
82 - .setMessage(sendSmsResponse.getMessage())  
83 - .setCode(sendSmsResponse.getCode())  
84 - .setApiId(sendSmsResponse.getBizId()); 58 + protected SmsResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams) throws Exception {
  59 + return null;
85 } 60 }
86 61
  62 +// @Override
  63 +// public SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception {
  64 +// SendSmsRequest request = new SendSmsRequest();
  65 +// request.setSysMethod(MethodType.POST);
  66 +// request.setPhoneNumbers(targetPhone);
  67 +// request.setSignName(properties.getSignature());
  68 +// request.setTemplateCode(templateApiId);
  69 +// request.setTemplateParam(smsBody.getParamsStr());
  70 +// SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request);
  71 +//
  72 +// boolean success = OK.equals(sendSmsResponse.getCode());
  73 +// if (!success) {
  74 +// log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage());
  75 +// }
  76 +// return new SmsResult()
  77 +// .setSuccess(success)
  78 +// .setMessage(sendSmsResponse.getMessage())
  79 +// .setCode(sendSmsResponse.getCode())
  80 +// .setApiId(sendSmsResponse.getBizId());
  81 +// }
  82 +
87 /** 83 /**
88 * [{ 84 * [{
89 * "send_time" : "2017-08-30 00:00:00", 85 * "send_time" : "2017-08-30 00:00:00",
@@ -131,8 +127,8 @@ public class AliyunSmsClient extends AbstractSmsClient { @@ -131,8 +127,8 @@ public class AliyunSmsClient extends AbstractSmsClient {
131 127
132 public Integer getSendStatus() { 128 public Integer getSendStatus() {
133 return ((Boolean) sendResultParamMap.get(CallbackField.SUCCESS)) 129 return ((Boolean) sendResultParamMap.get(CallbackField.SUCCESS))
134 - ? SysSmsSendStatusEnum.SEND_SUCCESS.getStatus()  
135 - : SysSmsSendStatusEnum.SEND_FAIL.getStatus(); 130 + ? SysSmsSendStatusEnum.SUCCESS.getStatus()
  131 + : SysSmsSendStatusEnum.FAILURE.getStatus();
136 } 132 }
137 133
138 public String getBizId() { 134 public String getBizId() {
src/main/java/cn/iocoder/dashboard/framework/sms/client/impl/yunpian/YunpianSmsClient.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java
1 -package cn.iocoder.dashboard.framework.sms.client.impl.yunpian; 1 +package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian;
2 2
  3 +import cn.hutool.core.collection.CollUtil;
3 import cn.hutool.core.collection.CollectionUtil; 4 import cn.hutool.core.collection.CollectionUtil;
4 import cn.hutool.core.date.DateUtil; 5 import cn.hutool.core.date.DateUtil;
5 import cn.hutool.core.util.CharsetUtil; 6 import cn.hutool.core.util.CharsetUtil;
6 -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;  
7 -import cn.iocoder.dashboard.framework.sms.core.SmsBody; 7 +import cn.hutool.core.util.URLUtil;
8 import cn.iocoder.dashboard.framework.sms.core.SmsConstants; 8 import cn.iocoder.dashboard.framework.sms.core.SmsConstants;
9 import cn.iocoder.dashboard.framework.sms.core.SmsResult; 9 import cn.iocoder.dashboard.framework.sms.core.SmsResult;
10 import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; 10 import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
11 -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; 11 +import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient;
  12 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum;
  13 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
12 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; 14 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
13 import cn.iocoder.dashboard.util.json.JsonUtils; 15 import cn.iocoder.dashboard.util.json.JsonUtils;
14 import com.fasterxml.jackson.core.type.TypeReference; 16 import com.fasterxml.jackson.core.type.TypeReference;
15 import com.yunpian.sdk.YunpianClient; 17 import com.yunpian.sdk.YunpianClient;
16 -import com.yunpian.sdk.constant.Code;  
17 import com.yunpian.sdk.constant.YunpianConstant; 18 import com.yunpian.sdk.constant.YunpianConstant;
18 import com.yunpian.sdk.model.Result; 19 import com.yunpian.sdk.model.Result;
19 import com.yunpian.sdk.model.SmsSingleSend; 20 import com.yunpian.sdk.model.SmsSingleSend;
@@ -25,9 +26,10 @@ import java.net.URLEncoder; @@ -25,9 +26,10 @@ import java.net.URLEncoder;
25 import java.util.HashMap; 26 import java.util.HashMap;
26 import java.util.List; 27 import java.util.List;
27 import java.util.Map; 28 import java.util.Map;
  29 +import java.util.StringJoiner;
28 30
29 /** 31 /**
30 - * 云片短信实现类 32 + * 云片短信客户端的实现类
31 * 33 *
32 * @author zzf 34 * @author zzf
33 * @date 9:48 2021/3/5 35 * @date 9:48 2021/3/5
@@ -35,70 +37,58 @@ import java.util.Map; @@ -35,70 +37,58 @@ import java.util.Map;
35 @Slf4j 37 @Slf4j
36 public class YunpianSmsClient extends AbstractSmsClient { 38 public class YunpianSmsClient extends AbstractSmsClient {
37 39
38 - private final YunpianClient client; 40 + /**
  41 + * 云信短信客户端
  42 + */
  43 + private volatile YunpianClient client;
39 44
40 private final TypeReference<List<Map<String, String>>> callbackType = new TypeReference<List<Map<String, String>>>() { 45 private final TypeReference<List<Map<String, String>>> callbackType = new TypeReference<List<Map<String, String>>>() {
41 }; 46 };
42 47
43 - /**  
44 - * 构造云片短信发送处理  
45 - *  
46 - * @param channelVO 阿里云短信配置  
47 - */  
48 - public YunpianSmsClient(SmsChannelProperty channelVO) {  
49 - super(channelVO);  
50 - client = new YunpianClient(channelVO.getApiKey()); 48 + public YunpianSmsClient(SmsChannelProperties properties) {
  49 + super(properties);
51 } 50 }
52 51
53 @Override 52 @Override
54 - public SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) {  
55 - Map<String, String> paramMap = new HashMap<>();  
56 - paramMap.put(YunpianConstant.APIKEY, getProperty().getApiKey());  
57 - paramMap.put(YunpianConstant.MOBILE, String.join(SmsConstants.COMMA, targetPhone));  
58 - paramMap.put(YunpianConstant.TEXT, formatContent(smsBody));  
59 - paramMap.put(Helper.CALLBACK, getProperty().getCallbackUrl());  
60 -  
61 - Result<SmsSingleSend> sendResult = client.sms().single_send(paramMap);  
62 - boolean success = sendResult.getCode().equals(Code.OK);  
63 -  
64 - if (!success) {  
65 - log.debug("send fail[code={}, message={}]", sendResult.getCode(), sendResult.getDetail());  
66 - }  
67 - return new SmsResult()  
68 - .setSuccess(success)  
69 - .setMessage(sendResult.getDetail())  
70 - .setCode(sendResult.getCode().toString())  
71 - .setApiId(sendResult.getData().getSid().toString()); 53 + public void doInit() {
  54 + client = new YunpianClient(properties.getApiKey());
72 } 55 }
73 56
74 -  
75 - /**  
76 - * 格式化短信内容,将参数注入到模板中  
77 - *  
78 - * @param smsBody 短信信息  
79 - * @return 格式化后的短信内容  
80 - */  
81 - private String formatContent(SmsBody smsBody) {  
82 - StringBuilder result = new StringBuilder(smsBody.getTemplateContent());  
83 - smsBody.getParams().forEach((key, val) -> {  
84 - String param = parseParamToPlaceholder(key);  
85 - result.replace(result.indexOf(param), result.indexOf(param + param.length()), val);  
86 - });  
87 - return result.toString(); 57 + @Override
  58 + protected SmsResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map<String, Object> templateParams) throws Throwable {
  59 + // 构建参数
  60 + Map<String, String> request = new HashMap<>();
  61 + request.put(YunpianConstant.APIKEY, properties.getApiKey());
  62 + request.put(YunpianConstant.MOBILE, mobile);
  63 + request.put(YunpianConstant.TPL_ID, apiTemplateId);
  64 + request.put(YunpianConstant.TPL_VALUE, formatTplValue(templateParams));
  65 + request.put(YunpianConstant.UID, String.valueOf(sendLogId));
  66 + request.put(Helper.CALLBACK, properties.getCallbackUrl());
  67 +
  68 + // 执行发送
  69 + Result<SmsSingleSend> sendResult = client.sms().tpl_single_send(request);
  70 + if (sendResult.getThrowable() != null) {
  71 + throw sendResult.getThrowable();
  72 + }
  73 + // 解析结果
  74 + SmsSingleSend data = sendResult.getData();
  75 + return SmsResult.success(parseSendFailureType(sendResult), // 将 API 短信平台,解析成统一的错误码
  76 + String.valueOf(data.getCode()), data.getMsg(), null, String.valueOf(data.getSid()));
88 } 77 }
89 78
90 - /**  
91 - * 将指定参数改成对应的占位字符  
92 - * <p>  
93 - * 云片的是 #param# 的形式作为占位符  
94 - *  
95 - * @param key 参数名  
96 - * @return 对应的占位字符  
97 - */  
98 - private String parseParamToPlaceholder(String key) {  
99 - return SmsConstants.JING_HAO + key + SmsConstants.JING_HAO; 79 + private static String formatTplValue(Map<String, Object> templateParams) {
  80 + if (CollUtil.isEmpty(templateParams)) {
  81 + return "";
  82 + }
  83 + // 参考 https://www.yunpian.com/official/document/sms/zh_cn/introduction_demos_encode_sample 格式化
  84 + StringJoiner joiner = new StringJoiner("&");
  85 + templateParams.forEach((key, value) -> joiner.add(String.format("#%s#=%s", key, URLUtil.encode(String.valueOf(value)))));
  86 + return joiner.toString();
100 } 87 }
101 88
  89 + private static SmsSendFailureTypeEnum parseSendFailureType(Result<SmsSingleSend> sendResult) {
  90 + return SmsSendFailureTypeEnum.SMS_UNKNOWN;
  91 + }
102 92
103 /** 93 /**
104 * 云片的比较复杂,又是加密又是套娃的 94 * 云片的比较复杂,又是加密又是套娃的
@@ -109,7 +99,6 @@ public class YunpianSmsClient extends AbstractSmsClient { @@ -109,7 +99,6 @@ public class YunpianSmsClient extends AbstractSmsClient {
109 return Helper.getSmsResultDetailByParam(map); 99 return Helper.getSmsResultDetailByParam(map);
110 } 100 }
111 101
112 -  
113 /** 102 /**
114 * 从 request 中获取请求中传入的短信发送结果信息 103 * 从 request 中获取请求中传入的短信发送结果信息
115 * 104 *
@@ -155,8 +144,8 @@ public class YunpianSmsClient extends AbstractSmsClient { @@ -155,8 +144,8 @@ public class YunpianSmsClient extends AbstractSmsClient {
155 private static int getSendStatus(Map<String, String> map) { 144 private static int getSendStatus(Map<String, String> map) {
156 String reportStatus = map.get(REPORT_STATUS); 145 String reportStatus = map.get(REPORT_STATUS);
157 return SmsConstants.SUCCESS.equals(reportStatus) 146 return SmsConstants.SUCCESS.equals(reportStatus)
158 - ? SysSmsSendStatusEnum.SEND_SUCCESS.getStatus()  
159 - : SysSmsSendStatusEnum.SEND_FAIL.getStatus(); 147 + ? SysSmsSendStatusEnum.SUCCESS.getStatus()
  148 + : SysSmsSendStatusEnum.FAILURE.getStatus();
160 } 149 }
161 150
162 public static SmsResultDetail getSmsResultDetailByParam(Map<String, String> map) { 151 public static SmsResultDetail getSmsResultDetailByParam(Map<String, String> map) {
src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsSendFailureTypeEnum.java
@@ -13,14 +13,23 @@ import lombok.Getter; @@ -13,14 +13,23 @@ import lombok.Getter;
13 public enum SmsSendFailureTypeEnum { 13 public enum SmsSendFailureTypeEnum {
14 14
15 // ========== 模板相关(100 开头) ========== 15 // ========== 模板相关(100 开头) ==========
16 - SMS_TEMPLATE_DISABLE(100), // 短信模板被禁用 16 + SMS_CHANNEL_CLIENT_NOT_EXISTS(100, "短信渠道的客户端不存在"),
17 17
18 - // ========== 其它相关 ========== 18 + // ========== 模板相关(200 开头) ==========
  19 + SMS_TEMPLATE_DISABLE(200, "短信模板被禁用"),
  20 +
  21 + // ========== 其它相关(900 开头) ==========
  22 + SMS_SEND_EXCEPTION(900, "发送异常"),
  23 + SMS_UNKNOWN(999, "未知错误,需要解析")
19 ; 24 ;
20 25
21 /** 26 /**
22 * 失败类型 27 * 失败类型
23 */ 28 */
24 private final int type; 29 private final int type;
  30 + /**
  31 + * 失败提示
  32 + */
  33 + private final String msg;
25 34
26 } 35 }
src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperty.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperties.java
1 package cn.iocoder.dashboard.framework.sms.core.property; 1 package cn.iocoder.dashboard.framework.sms.core.property;
2 2
  3 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
3 import lombok.Data; 4 import lombok.Data;
4 -import lombok.EqualsAndHashCode; 5 +import org.springframework.validation.annotation.Validated;
5 6
6 import javax.validation.constraints.NotEmpty; 7 import javax.validation.constraints.NotEmpty;
7 import javax.validation.constraints.NotNull; 8 import javax.validation.constraints.NotNull;
8 -import java.io.Serializable;  
9 -import java.util.List;  
10 9
11 /** 10 /**
12 - * 渠道(包含模板)信息VO类 11 + * 短信渠道配置类
13 * 12 *
14 * @author zzf 13 * @author zzf
15 * @date 2021/1/25 17:01 14 * @date 2021/1/25 17:01
16 */ 15 */
17 @Data 16 @Data
18 -@EqualsAndHashCode  
19 -public class SmsChannelProperty implements Serializable { 17 +@Validated
  18 +public class SmsChannelProperties {
20 19
21 /** 20 /**
22 - * id 21 + * 渠道编号
23 */ 22 */
24 - @NotNull(message = "短信渠道ID不能为空") 23 + @NotNull(message = "短信渠道 ID 不能为空")
25 private Long id; 24 private Long id;
26 -  
27 /** 25 /**
28 - * 编码(来自枚举类 阿里、华为、七牛等) 26 + * 短信签名
29 */ 27 */
30 - @NotEmpty(message = "短信渠道编码不能为空") 28 + @NotEmpty(message = "短信签名不能为空")
  29 + private String signature;
  30 + /**
  31 + * 渠道编码
  32 + *
  33 + * 枚举 {@link SmsChannelEnum}
  34 + */
  35 + @NotEmpty(message = "渠道编码不能为空")
31 private String code; 36 private String code;
32 -  
33 /** 37 /**
34 - * 渠道账号id 38 + * 短信 API 的账号
35 */ 39 */
36 - @NotEmpty(message = "渠道账号id不能为空") 40 + @NotEmpty(message = "短信 API 的账号不能为空")
37 private String apiKey; 41 private String apiKey;
38 -  
39 /** 42 /**
40 - * 渠道账号秘钥 43 + * 短信 API 的秘钥
41 */ 44 */
42 - @NotEmpty(message = "渠道账号秘钥不能为空") 45 + @NotEmpty(message = "短信 API 的秘钥不能为空")
43 private String apiSecret; 46 private String apiSecret;
44 -  
45 - /**  
46 - * 实际渠道签名唯一标识  
47 - */  
48 - @NotEmpty(message = "实际渠道签名唯一标识不能为空")  
49 - private String apiSignatureId;  
50 -  
51 - /**  
52 - * 签名值  
53 - */  
54 - @NotEmpty(message = "签名值不能为空")  
55 - private String signature;  
56 -  
57 - /**  
58 - * 是否拥有回调函数(0否 1是)  
59 - */  
60 - @NotNull(message = "是否拥有回调函数不能为空")  
61 - private Integer hadCallback;  
62 -  
63 /** 47 /**
64 - * 短信发送回调url 48 + * 短信发送回调 URL
65 */ 49 */
66 private String callbackUrl; 50 private String callbackUrl;
67 51
src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsTemplateProperty.java deleted
1 -package cn.iocoder.dashboard.framework.sms.core.property;  
2 -  
3 -import lombok.Data;  
4 -import lombok.EqualsAndHashCode;  
5 -  
6 -import javax.validation.constraints.NotEmpty;  
7 -  
8 -/**  
9 - * 渠道模板VO类  
10 - *  
11 - * @author zzf  
12 - * @date 2021/1/25 17:03  
13 - */  
14 -@Data  
15 -@EqualsAndHashCode  
16 -public class SmsTemplateProperty {  
17 -  
18 - /**  
19 - * 渠道id  
20 - */  
21 - @NotEmpty(message = "短信渠道编码不能为空")  
22 - private Long channelId;  
23 -  
24 - /**  
25 - * 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)  
26 - */  
27 - private String bizCode;  
28 -  
29 - /**  
30 - * 编码  
31 - */  
32 - @NotEmpty(message = "短信模板编码不能为空")  
33 - private String code;  
34 -  
35 - /**  
36 - * 实际渠道模板唯一标识  
37 - */  
38 - @NotEmpty(message = "短信模板唯一标识不能为空")  
39 - private String apiTemplateId;  
40 -  
41 - /**  
42 - * 内容  
43 - */  
44 - @NotEmpty(message = "短信模板内容不能为空")  
45 - private String content;  
46 -  
47 -}  
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/req/SmsChannelPageReqVO.java
@@ -15,9 +15,6 @@ import lombok.NoArgsConstructor; @@ -15,9 +15,6 @@ import lombok.NoArgsConstructor;
15 @EqualsAndHashCode(callSuper = true) 15 @EqualsAndHashCode(callSuper = true)
16 public class SmsChannelPageReqVO extends PageParam { 16 public class SmsChannelPageReqVO extends PageParam {
17 17
18 - @ApiModelProperty(value = "渠道名", example = "阿里", notes = "模糊匹配")  
19 - private String name;  
20 -  
21 @ApiModelProperty(value = "签名值", example = "源码", notes = "模糊匹配") 18 @ApiModelProperty(value = "签名值", example = "源码", notes = "模糊匹配")
22 private String signature; 19 private String signature;
23 20
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java
1 package cn.iocoder.dashboard.modules.system.convert.sms; 1 package cn.iocoder.dashboard.modules.system.convert.sms;
2 2
3 import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; 3 import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
4 -import cn.iocoder.dashboard.common.pojo.PageResult;  
5 -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; 4 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; 5 import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; 6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
8 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; 7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
9 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO; 8 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
10 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; 9 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
11 -import com.baomidou.mybatisplus.core.metadata.IPage;  
12 import org.mapstruct.Mapper; 10 import org.mapstruct.Mapper;
13 -import org.mapstruct.Mapping;  
14 import org.mapstruct.factory.Mappers; 11 import org.mapstruct.factory.Mappers;
15 12
16 import java.util.List; 13 import java.util.List;
@@ -20,9 +17,6 @@ public interface SmsChannelConvert { @@ -20,9 +17,6 @@ public interface SmsChannelConvert {
20 17
21 SmsChannelConvert INSTANCE = Mappers.getMapper(SmsChannelConvert.class); 18 SmsChannelConvert INSTANCE = Mappers.getMapper(SmsChannelConvert.class);
22 19
23 - @Mapping(source = "records", target = "list")  
24 - PageResult<SysSmsChannelDO> convertPage(IPage<SysSmsChannelDO> page);  
25 -  
26 SysSmsChannelDO convert(SmsChannelCreateReqVO bean); 20 SysSmsChannelDO convert(SmsChannelCreateReqVO bean);
27 21
28 SysSmsChannelDO convert(SysUserUpdateReqVO bean); 22 SysSmsChannelDO convert(SysUserUpdateReqVO bean);
@@ -31,9 +25,8 @@ public interface SmsChannelConvert { @@ -31,9 +25,8 @@ public interface SmsChannelConvert {
31 25
32 List<SmsChannelAllVO> convert(List<SysSmsChannelDO> bean); 26 List<SmsChannelAllVO> convert(List<SysSmsChannelDO> bean);
33 27
34 - List<SmsChannelProperty> convertProperty(List<SmsChannelAllVO> list);  
35 -  
36 - List<SmsChannelProperty> convertProperties(List<SysSmsChannelDO> list); 28 + List<SmsChannelProperties> convertProperty(List<SmsChannelAllVO> list);
37 29
  30 + List<SmsChannelProperties> convertList(List<SysSmsChannelDO> list);
38 31
39 } 32 }
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsTemplateConvert.java
1 package cn.iocoder.dashboard.modules.system.convert.sms; 1 package cn.iocoder.dashboard.modules.system.convert.sms;
2 2
3 import cn.iocoder.dashboard.common.pojo.PageResult; 3 import cn.iocoder.dashboard.common.pojo.PageResult;
4 -import cn.iocoder.dashboard.framework.sms.core.property.SmsTemplateProperty;  
5 import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO; 4 import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO;
6 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; 5 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
7 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; 6 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
@@ -24,6 +23,4 @@ public interface SmsTemplateConvert { @@ -24,6 +23,4 @@ public interface SmsTemplateConvert {
24 23
25 SmsTemplateVO convert(SysSmsTemplateDO bean); 24 SmsTemplateVO convert(SysSmsTemplateDO bean);
26 25
27 - List<SmsTemplateProperty> convertProperty(List<SysSmsTemplateDO> bean);  
28 -  
29 } 26 }
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java
1 package cn.iocoder.dashboard.modules.system.dal.dataobject.sms; 1 package cn.iocoder.dashboard.modules.system.dal.dataobject.sms;
2 2
  3 +import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
3 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; 4 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
4 import com.baomidou.mybatisplus.annotation.TableName; 6 import com.baomidou.mybatisplus.annotation.TableName;
5 import lombok.Data; 7 import lombok.Data;
6 import lombok.EqualsAndHashCode; 8 import lombok.EqualsAndHashCode;
7 9
8 -  
9 /** 10 /**
10 * 短信渠道 11 * 短信渠道
11 * 12 *
12 * @author zzf 13 * @author zzf
13 * @since 2021-01-25 14 * @since 2021-01-25
14 */ 15 */
  16 +@TableName(value = "sms_channel", autoResultMap = true)
15 @Data 17 @Data
16 @EqualsAndHashCode(callSuper = true) 18 @EqualsAndHashCode(callSuper = true)
17 -@TableName(value = "sms_channel", autoResultMap = true)  
18 public class SysSmsChannelDO extends BaseDO { 19 public class SysSmsChannelDO extends BaseDO {
19 20
20 /** 21 /**
21 - * 自增编号 22 + * 渠道编号
22 */ 23 */
23 private Long id; 24 private Long id;
24 -  
25 - /**  
26 - * 编码(来自枚举类 阿里、华为、七牛等)  
27 - */  
28 - private String code;  
29 -  
30 /** 25 /**
31 - * 短信发送回调url 26 + * 短信签名
32 */ 27 */
33 - private String callback_url;  
34 -  
35 - /**  
36 - * 渠道账号id  
37 - */  
38 - private String apiKey;  
39 - 28 + private String signature;
40 /** 29 /**
41 - * 渠道账号秘钥 30 + * 渠道编码
  31 + *
  32 + * 枚举 {@link SmsChannelEnum}
42 */ 33 */
43 - private String apiSecret;  
44 - 34 + private String code;
45 /** 35 /**
46 - * 实际渠道签名唯一标识 36 + * 启用状态
  37 + *
  38 + * 枚举 {@link CommonStatusEnum}
47 */ 39 */
48 - private String apiSignatureId;  
49 - 40 + private Integer status;
50 /** 41 /**
51 - * 名称 42 + * 备注
52 */ 43 */
53 - private String name;  
54 - 44 + private String remark;
55 /** 45 /**
56 - * 签名值 46 + * 短信 API 的账号
57 */ 47 */
58 - private String signature;  
59 - 48 + private String apiKey;
60 /** 49 /**
61 - * 备注 50 + * 短信 API 的秘钥
62 */ 51 */
63 - private String remark;  
64 - 52 + private String apiSecret;
65 /** 53 /**
66 - * 启用状态(0正常 1停用) 54 + * 短信发送回调 URL
67 */ 55 */
68 - private Integer status; 56 + private String callbackUrl;
69 57
70 } 58 }
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDO.java
@@ -106,15 +106,22 @@ public class SysSmsSendLogDO extends BaseDO { @@ -106,15 +106,22 @@ public class SysSmsSendLogDO extends BaseDO {
106 */ 106 */
107 private Integer sendStatus; 107 private Integer sendStatus;
108 /** 108 /**
  109 + * 时间发送时间
  110 + */
  111 + private Date sendTime;
  112 + /**
109 * 发送失败的类型 113 * 发送失败的类型
110 * 114 *
111 - * 枚举 {@link SmsSendFailureTypeEnum} 115 + * 枚举 {@link SmsSendFailureTypeEnum#getType()}
112 */ 116 */
113 private Integer sendFailureType; 117 private Integer sendFailureType;
114 /** 118 /**
115 - * 发送成功时间 119 + * 发送失败的提示
  120 + *
  121 + * 一般情况下,使用 {@link SmsSendFailureTypeEnum#getMsg()}
  122 + * 异常情况下,通过格式化 Exception 的提示存储
116 */ 123 */
117 - private Date sendTime; 124 + private String sendFailureMsg;
118 /** 125 /**
119 * 短信 API 发送失败的类型 126 * 短信 API 发送失败的类型
120 * 127 *
src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDOX.java deleted
1 -package cn.iocoder.dashboard.modules.system.dal.dataobject.sms;  
2 -  
3 -import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;  
4 -import com.baomidou.mybatisplus.annotation.TableName;  
5 -import lombok.Data;  
6 -import lombok.EqualsAndHashCode;  
7 -import lombok.experimental.Accessors;  
8 -  
9 -import java.io.Serializable;  
10 -import java.util.Date;  
11 -  
12 -/**  
13 - * 短信日志  
14 - *  
15 - * @author zzf  
16 - * @since 2021-01-25  
17 - */  
18 -@Data  
19 -@EqualsAndHashCode  
20 -@Accessors(chain = true)  
21 -@TableName(value = "sms_send_log", autoResultMap = true)  
22 -public class SysSmsSendLogDOX implements Serializable {  
23 -  
24 - /**  
25 - * 自增编号  
26 - */  
27 - private Long id;  
28 -  
29 - /**  
30 - * 短信渠道编码(来自枚举类)  
31 - */  
32 - private String channelCode;  
33 -  
34 - /**  
35 - * 短信渠道id  
36 - */  
37 - private Long channelId;  
38 -  
39 - /**  
40 - * 模板id  
41 - */  
42 - private String templateCode;  
43 -  
44 - /**  
45 - * 手机号  
46 - */  
47 - private String phone;  
48 -  
49 - /**  
50 - * 备注  
51 - */  
52 - private String remark;  
53 -  
54 - /**  
55 - * 发送状态  
56 - *  
57 - * @see SysSmsSendStatusEnum  
58 - */  
59 - private Integer sendStatus;  
60 -  
61 - /**  
62 - * 发送时间  
63 - */  
64 - private Date sendTime;  
65 -  
66 -}  
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsChannelMapper.java
1 package cn.iocoder.dashboard.modules.system.dal.mysql.sms; 1 package cn.iocoder.dashboard.modules.system.dal.mysql.sms;
2 2
3 import cn.hutool.core.util.StrUtil; 3 import cn.hutool.core.util.StrUtil;
4 -import cn.iocoder.dashboard.common.enums.CommonStatusEnum;  
5 -import cn.iocoder.dashboard.framework.mybatis.core.util.MyBatisUtils; 4 +import cn.iocoder.dashboard.common.pojo.PageResult;
  5 +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; 6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
7 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; 7 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
8 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; 8 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
9 -import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
10 -import com.baomidou.mybatisplus.core.metadata.IPage;  
11 import org.apache.ibatis.annotations.Mapper; 9 import org.apache.ibatis.annotations.Mapper;
12 10
13 import java.util.List; 11 import java.util.List;
14 12
15 @Mapper 13 @Mapper
16 -public interface SysSmsChannelMapper extends BaseMapper<SysSmsChannelDO> { 14 +public interface SysSmsChannelMapper extends BaseMapperX<SysSmsChannelDO> {
17 15
18 - default IPage<SysSmsChannelDO> selectChannelPage(SmsChannelPageReqVO reqVO) {  
19 - return selectPage(MyBatisUtils.buildPage(reqVO), new LambdaQueryWrapper<SysSmsChannelDO>()  
20 - .like(StrUtil.isNotBlank(reqVO.getName()), SysSmsChannelDO::getName, reqVO.getName())  
21 - .like(StrUtil.isNotBlank(reqVO.getSignature()), SysSmsChannelDO::getName, reqVO.getSignature())  
22 - ); 16 + default PageResult<SysSmsChannelDO> selectChannelPage(SmsChannelPageReqVO reqVO) {
  17 + return selectPage(reqVO, new LambdaQueryWrapper<SysSmsChannelDO>()
  18 + .like(StrUtil.isNotBlank(reqVO.getSignature()), SysSmsChannelDO::getSignature, reqVO.getSignature()));
23 } 19 }
24 20
25 - default List<SysSmsChannelDO> selectEnabledList() { 21 + default List<SysSmsChannelDO> selectListByStatus(Integer status) {
26 return selectList(new LambdaQueryWrapper<SysSmsChannelDO>() 22 return selectList(new LambdaQueryWrapper<SysSmsChannelDO>()
27 - .eq(SysSmsChannelDO::getStatus, CommonStatusEnum.ENABLE.getStatus())  
28 - .orderByAsc(SysSmsChannelDO::getId)  
29 - ); 23 + .eq(SysSmsChannelDO::getStatus, status)
  24 + .orderByAsc(SysSmsChannelDO::getId));
30 } 25 }
  26 +
31 } 27 }
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsQueryLogMapper.java deleted
1 -package cn.iocoder.dashboard.modules.system.dal.mysql.sms;  
2 -  
3 -import cn.iocoder.dashboard.common.enums.DefaultBitFieldEnum;  
4 -import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO;  
5 -import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;  
6 -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;  
7 -import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
8 -import org.apache.ibatis.annotations.Mapper;  
9 -  
10 -import java.util.List;  
11 -  
12 -@Mapper  
13 -public interface SysSmsQueryLogMapper extends BaseMapper<SysSmsSendLogDO> {  
14 -  
15 - /**  
16 - * 查询还没有获取发送结果的短信请求信息  
17 - */  
18 - default List<SysSmsSendLogDO> selectNoResultQueryLogList() {  
19 - return this.selectList(new LambdaQueryWrapper<SysSmsSendLogDO>()  
20 - .eq(SysSmsSendLogDO::getSendStatus, SysSmsSendStatusEnum.QUERY_SUCCESS)  
21 - .eq(SysSmsSendLogDO::getGotResult, DefaultBitFieldEnum.NO)  
22 - );  
23 - }  
24 -  
25 -  
26 - /**  
27 - * 根据APIId修改对象  
28 - */  
29 - default boolean updateByApiId(SysSmsSendLogDO queryLogDO, String apiId) {  
30 - return update(queryLogDO, new LambdaQueryWrapper<SysSmsSendLogDO>()  
31 - .eq(SysSmsSendLogDO::getApiId, apiId)  
32 - ) > 0;  
33 - }  
34 -}  
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsSendLogMapper.java
1 package cn.iocoder.dashboard.modules.system.dal.mysql.sms; 1 package cn.iocoder.dashboard.modules.system.dal.mysql.sms;
2 2
  3 +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
3 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO; 4 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO;
4 -import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
5 import org.apache.ibatis.annotations.Mapper; 5 import org.apache.ibatis.annotations.Mapper;
6 6
7 @Mapper 7 @Mapper
8 -public interface SysSmsSendLogMapper extends BaseMapper<SysSmsSendLogDO> {  
9 - 8 +public interface SysSmsSendLogMapper extends BaseMapperX<SysSmsSendLogDO> {
10 } 9 }
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java
1 package cn.iocoder.dashboard.modules.system.mq.consumer.sms; 1 package cn.iocoder.dashboard.modules.system.mq.consumer.sms;
2 2
3 import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; 3 import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener;
4 -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;  
5 -import cn.iocoder.dashboard.framework.sms.core.SmsBody;  
6 -import cn.iocoder.dashboard.framework.sms.core.SmsResult;  
7 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; 4 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage;
8 -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;  
9 -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService;  
10 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; 5 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;
11 -import cn.iocoder.dashboard.util.json.JsonUtils;  
12 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
13 -import org.springframework.data.redis.connection.stream.ObjectRecord;  
14 -import org.springframework.data.redis.stream.StreamListener;  
15 import org.springframework.stereotype.Component; 7 import org.springframework.stereotype.Component;
16 8
17 import javax.annotation.Resource; 9 import javax.annotation.Resource;
@@ -27,24 +19,9 @@ import javax.annotation.Resource; @@ -27,24 +19,9 @@ import javax.annotation.Resource;
27 public class SmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> { 19 public class SmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> {
28 20
29 @Resource 21 @Resource
30 - private SysSmsChannelService smsChannelService;  
31 -  
32 - @Resource  
33 - private SysSmsQueryLogService smsQueryLogService;  
34 -  
35 - @Resource  
36 private SysSmsService smsService; 22 private SysSmsService smsService;
37 23
38 @Override 24 @Override
39 - public void onMessage(ObjectRecord<String, SmsSendMessage> record) {  
40 - AbstractSmsClient smsClient = smsChannelService.getSmsClient(body.getTemplateCode());  
41 - String templateApiId = smsChannelService.getSmsTemplateApiIdByCode(body.getTemplateCode());  
42 -  
43 - SmsResult result = smsClient.send(templateApiId, body, message.getTargetPhone());  
44 - smsQueryLogService.afterSendLog(body.getSmsLogId(), result);  
45 - }  
46 -  
47 - @Override  
48 public void onMessage(SysSmsSendMessage message) { 25 public void onMessage(SysSmsSendMessage message) {
49 log.info("[onMessage][消息内容({})]", message); 26 log.info("[onMessage][消息内容({})]", message);
50 smsService.doSendSms(message); 27 smsService.doSendSms(message);
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java
1 package cn.iocoder.dashboard.modules.system.service.sms; 1 package cn.iocoder.dashboard.modules.system.service.sms;
2 2
3 import cn.iocoder.dashboard.common.pojo.PageResult; 3 import cn.iocoder.dashboard.common.pojo.PageResult;
4 -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;  
5 -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;  
6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; 4 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; 5 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
8 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; 6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
@@ -46,26 +44,4 @@ public interface SysSmsChannelService { @@ -46,26 +44,4 @@ public interface SysSmsChannelService {
46 */ 44 */
47 List<SmsChannelEnumRespVO> getSmsChannelEnums(); 45 List<SmsChannelEnumRespVO> getSmsChannelEnums();
48 46
49 - /**  
50 - * 根据短信模板编码获取短信客户端  
51 - *  
52 - * @param templateCode 短信模板编码  
53 - * @return 短信客户端  
54 - */  
55 - AbstractSmsClient getSmsClient(String templateCode);  
56 -  
57 - /**  
58 - * 根据短信模板编码获取模板唯一标识  
59 - *  
60 - * @param templateCode 短信模板编码  
61 - * @return 短信客户端  
62 - */  
63 - String getSmsTemplateApiIdByCode(String templateCode);  
64 -  
65 - /**  
66 - * 查询渠道(包含名下模块)信息集合  
67 - *  
68 - * @return 渠道(包含名下模块)信息集合  
69 - */  
70 - List<SmsChannelAllVO> listSmsChannelAllEnabledInfo();  
71 } 47 }
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsQueryLogService.java deleted
1 -package cn.iocoder.dashboard.modules.system.service.sms;  
2 -  
3 -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;  
4 -import cn.iocoder.dashboard.framework.sms.core.SmsBody;  
5 -import cn.iocoder.dashboard.framework.sms.core.SmsResult;  
6 -import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;  
7 -  
8 -/**  
9 - * 短信请求日志服务接口  
10 - *  
11 - * @author zzf  
12 - * @date 2021/1/25 9:24  
13 - */  
14 -public interface SysSmsQueryLogService {  
15 -  
16 - /**  
17 - * 发送短信前的日志处理  
18 - *  
19 - * @param smsBody 短信内容  
20 - * @param targetPhone 发送对象手机号  
21 - * @param client 短信客户端  
22 - * @return 生成的日志id  
23 - */  
24 - void beforeSendLog(SmsBody smsBody, String targetPhone, AbstractSmsClient client);  
25 -  
26 - /**  
27 - * 发送消息后的日志处理  
28 - *  
29 - * @param logId 日志id  
30 - * @param result 消息结果  
31 - */  
32 - void afterSendLog(Long logId, SmsResult result);  
33 -  
34 - void updateSendLogByResultDetail(SmsResultDetail smsResultDetail);  
35 -}  
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java
1 package cn.iocoder.dashboard.modules.system.service.sms; 1 package cn.iocoder.dashboard.modules.system.service.sms;
2 2
  3 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum;
3 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; 4 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
4 5
5 import java.util.Map; 6 import java.util.Map;
@@ -12,17 +13,38 @@ import java.util.Map; @@ -12,17 +13,38 @@ import java.util.Map;
12 */ 13 */
13 public interface SysSmsSendLogService { 14 public interface SysSmsSendLogService {
14 15
  16 + /**
  17 + * 创建发送日志
  18 + *
  19 + * @param mobile 手机号
  20 + * @param userId 用户编号
  21 + * @param userType 用户类型
  22 + * @param template 短信模板
  23 + * @param templateContent 短信内容
  24 + * @param templateParams 短信参数
  25 + * @return
  26 + */
15 Long createSmsSendLog(String mobile, Long userId, Integer userType, 27 Long createSmsSendLog(String mobile, Long userId, Integer userType,
16 SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams); 28 SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams);
17 29
18 /** 30 /**
19 - * 更新发送日志为失败 31 + * 更新发送日志的结果
20 * 32 *
21 - * @param id 发送日志编号  
22 - * @param sendFailureType 失败类型 33 + * @param id 日志编号
  34 + * @param success 是否成功
  35 + * @param sendFailureType 发送失败的类型
  36 + * @param sendFailureMsg 发送失败的提示
  37 + * @param apiSendFailureType 短信 API 发送失败的类型
  38 + * @param apiSendFailureMsg 短信 API 发送失败的提示
  39 + * @param apiRequestId 短信 API 发送返回的唯一请求 ID
  40 + * @param apiSerialNo 短信 API 发送返回的序号
23 */ 41 */
24 - void updateSmsSendLogFailure(Long id, Integer sendFailureType); 42 + void updateSmsSendLogResult(Long id, Boolean success, Integer sendFailureType, String sendFailureMsg,
  43 + String apiSendFailureType, String apiSendFailureMsg, String apiRequestId, String apiSerialNo);
25 44
26 - void getAndSaveSmsSendLog(); 45 + default void updateSmsSendLogFailure(Long id, SmsSendFailureTypeEnum sendFailureType) {
  46 + updateSmsSendLogResult(id, false, sendFailureType.getType(), sendFailureType.getMsg(),
  47 + null, null, null, null);
  48 + }
27 49
28 } 50 }
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsChannelServiceImpl.java
1 package cn.iocoder.dashboard.modules.system.service.sms.impl; 1 package cn.iocoder.dashboard.modules.system.service.sms.impl;
2 2
3 -import cn.hutool.core.util.ObjectUtil; 3 +import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
4 import cn.iocoder.dashboard.common.pojo.PageResult; 4 import cn.iocoder.dashboard.common.pojo.PageResult;
5 -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;  
6 -import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory; 5 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
7 import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; 6 import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
8 -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;  
9 -import cn.iocoder.dashboard.framework.sms.core.property.SmsTemplateProperty;  
10 -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; 7 +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties;
11 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; 8 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
12 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; 9 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
13 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; 10 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
14 import cn.iocoder.dashboard.modules.system.convert.sms.SmsChannelConvert; 11 import cn.iocoder.dashboard.modules.system.convert.sms.SmsChannelConvert;
15 -import cn.iocoder.dashboard.modules.system.convert.sms.SmsTemplateConvert;  
16 -import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsChannelMapper;  
17 -import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsTemplateMapper;  
18 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; 12 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
19 -import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; 13 +import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsChannelMapper;
  14 +import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsTemplateMapper;
20 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; 15 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
21 import org.springframework.stereotype.Service; 16 import org.springframework.stereotype.Service;
22 17
23 import javax.annotation.PostConstruct; 18 import javax.annotation.PostConstruct;
24 import javax.annotation.Resource; 19 import javax.annotation.Resource;
25 -import java.util.ArrayList;  
26 import java.util.Arrays; 20 import java.util.Arrays;
27 import java.util.List; 21 import java.util.List;
28 -import java.util.Map;  
29 -import java.util.concurrent.ConcurrentHashMap;  
30 22
31 /** 23 /**
32 * 短信渠道Service实现类 24 * 短信渠道Service实现类
@@ -37,10 +29,8 @@ import java.util.concurrent.ConcurrentHashMap; @@ -37,10 +29,8 @@ import java.util.concurrent.ConcurrentHashMap;
37 @Service 29 @Service
38 public class SysSmsChannelServiceImpl implements SysSmsChannelService { 30 public class SysSmsChannelServiceImpl implements SysSmsChannelService {
39 31
40 - private final Map<String, Long> templateCode2ChannelIdMap = new ConcurrentHashMap<>(32);  
41 -  
42 @Resource 32 @Resource
43 - private SmsClientFactory clientFactory; 33 + private SmsClientFactory smsClientFactory;
44 34
45 @Resource 35 @Resource
46 private SysSmsChannelMapper channelMapper; 36 private SysSmsChannelMapper channelMapper;
@@ -48,30 +38,19 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService { @@ -48,30 +38,19 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService {
48 @Resource 38 @Resource
49 private SysSmsTemplateMapper templateMapper; 39 private SysSmsTemplateMapper templateMapper;
50 40
51 -  
52 - @PostConstruct  
53 @Override 41 @Override
  42 + @PostConstruct
54 public void initSmsClientAndCacheSmsTemplate() { 43 public void initSmsClientAndCacheSmsTemplate() {
55 // 查询有效渠道信息 44 // 查询有效渠道信息
56 - List<SysSmsChannelDO> channelDOList = channelMapper.selectEnabledList();  
57 - List<SmsChannelProperty> propertyList = SmsChannelConvert.INSTANCE.convertProperties(channelDOList);  
58 -  
59 - // 遍历渠道生成client、获取模板并缓存  
60 - propertyList.forEach(channelProperty -> {  
61 - List<SysSmsTemplateDO> templateDOList = templateMapper.selectListByChannelId(channelProperty.getId());  
62 - if (ObjectUtil.isNotEmpty(templateDOList)) {  
63 - Long clientId = clientFactory.createClient(channelProperty);  
64 - templateDOList.forEach(template -> templateCode2ChannelIdMap.put(template.getCode(), clientId));  
65 -  
66 - List<SmsTemplateProperty> templatePropertyList = SmsTemplateConvert.INSTANCE.convertProperty(templateDOList);  
67 - clientFactory.addOrUpdateTemplateCache(templatePropertyList);  
68 - }  
69 - }); 45 + List<SysSmsChannelDO> channelDOList = channelMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus());
  46 + // 创建渠道 Client
  47 + List<SmsChannelProperties> propertiesList = SmsChannelConvert.INSTANCE.convertList(channelDOList);
  48 + propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties));
70 } 49 }
71 50
72 @Override 51 @Override
73 public PageResult<SysSmsChannelDO> pageSmsChannels(SmsChannelPageReqVO reqVO) { 52 public PageResult<SysSmsChannelDO> pageSmsChannels(SmsChannelPageReqVO reqVO) {
74 - return SmsChannelConvert.INSTANCE.convertPage(channelMapper.selectChannelPage(reqVO)); 53 + return channelMapper.selectChannelPage(reqVO);
75 } 54 }
76 55
77 @Override 56 @Override
@@ -86,30 +65,20 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService { @@ -86,30 +65,20 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService {
86 return SmsChannelConvert.INSTANCE.convertEnum(Arrays.asList(SmsChannelEnum.values())); 65 return SmsChannelConvert.INSTANCE.convertEnum(Arrays.asList(SmsChannelEnum.values()));
87 } 66 }
88 67
89 - @Override  
90 - public AbstractSmsClient getSmsClient(String templateCode) {  
91 - return clientFactory.getClient(templateCode2ChannelIdMap.get(templateCode));  
92 - }  
93 -  
94 - @Override  
95 - public String getSmsTemplateApiIdByCode(String templateCode) {  
96 - return clientFactory.getTemplateApiIdByCode(templateCode);  
97 - }  
98 -  
99 - @Override  
100 - public List<SmsChannelAllVO> listSmsChannelAllEnabledInfo() {  
101 - List<SysSmsChannelDO> channelDOList = channelMapper.selectEnabledList();  
102 - if (ObjectUtil.isNull(channelDOList)) {  
103 - return null;  
104 - }  
105 - List<SmsChannelAllVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList);  
106 - channelAllVOList.forEach(smsChannelDO -> {  
107 - List<SysSmsTemplateDO> templateDOList = templateMapper.selectListByChannelId(smsChannelDO.getId());  
108 - if (ObjectUtil.isNull(templateDOList)) {  
109 - templateDOList = new ArrayList<>();  
110 - }  
111 - smsChannelDO.setTemplateList(SmsTemplateConvert.INSTANCE.convert(templateDOList));  
112 - });  
113 - return channelAllVOList;  
114 - } 68 +// @Override
  69 +// public List<SmsChannelAllVO> listSmsChannelAllEnabledInfo() {
  70 +// List<SysSmsChannelDO> channelDOList = channelMapper.selectListByStatus();
  71 +// if (ObjectUtil.isNull(channelDOList)) {
  72 +// return null;
  73 +// }
  74 +// List<SmsChannelAllVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList);
  75 +// channelAllVOList.forEach(smsChannelDO -> {
  76 +// List<SysSmsTemplateDO> templateDOList = templateMapper.selectListByChannelId(smsChannelDO.getId());
  77 +// if (ObjectUtil.isNull(templateDOList)) {
  78 +// templateDOList = new ArrayList<>();
  79 +// }
  80 +// smsChannelDO.setTemplateList(SmsTemplateConvert.INSTANCE.convert(templateDOList));
  81 +// });
  82 +// return channelAllVOList;
  83 +// }
115 } 84 }
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsQueryLogServiceImpl.java deleted
1 -package cn.iocoder.dashboard.modules.system.service.sms.impl;  
2 -  
3 -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;  
4 -import cn.iocoder.dashboard.framework.sms.core.SmsBody;  
5 -import cn.iocoder.dashboard.framework.sms.core.SmsResult;  
6 -import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;  
7 -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty;  
8 -import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsQueryLogMapper;  
9 -import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO;  
10 -import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;  
11 -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService;  
12 -import org.springframework.stereotype.Service;  
13 -  
14 -import javax.annotation.Resource;  
15 -  
16 -/**  
17 - * 短信请求日志服务实现类  
18 - *  
19 - * @author zzf  
20 - * @date 13:50 2021/3/2  
21 - */  
22 -@Service  
23 -public class SysSmsQueryLogServiceImpl implements SysSmsQueryLogService {  
24 -  
25 - @Resource  
26 - private SysSmsQueryLogMapper logMapper;  
27 -  
28 - @Override  
29 - public void beforeSendLog(SmsBody smsBody, String targetPhone, AbstractSmsClient client) {  
30 - SysSmsSendLogDO smsLog = new SysSmsSendLogDO();  
31 - SmsChannelProperty property = client.getProperty();  
32 -  
33 - smsLog.setChannelCode(property.getCode())  
34 - .setChannelId(property.getId())  
35 - .setTemplateCode(smsBody.getTemplateCode())  
36 - .setPhone(targetPhone)  
37 - .setContent(smsBody.getParams().toString());  
38 -  
39 - smsLog.setSendStatus(SysSmsSendStatusEnum.ASYNC.getStatus());  
40 - logMapper.insert(smsLog);  
41 - smsBody.setSmsLogId(smsLog.getId());  
42 - }  
43 -  
44 - @Override  
45 - public void afterSendLog(Long logId, SmsResult result) {  
46 - SysSmsSendLogDO smsLog = new SysSmsSendLogDO();  
47 - smsLog.setId(logId);  
48 - smsLog.setApiId(result.getApiId());  
49 - smsLog.setSendStatus(SysSmsSendStatusEnum.QUERY_FAIL.getStatus());  
50 - smsLog.setRemark(result.getCode() + ": " + result.getMessage());  
51 - logMapper.updateById(smsLog);  
52 - }  
53 -  
54 - @Override  
55 - public void updateSendLogByResultDetail(SmsResultDetail smsResultDetail) {  
56 - SysSmsSendLogDO queryLogDO = new SysSmsSendLogDO();  
57 - queryLogDO.setSendStatus(smsResultDetail.getSendStatus());  
58 - queryLogDO.setSendTime(smsResultDetail.getSendTime());  
59 - queryLogDO.setRemark(smsResultDetail.getMessage());  
60 - logMapper.updateByApiId(queryLogDO, smsResultDetail.getApiId());  
61 - }  
62 -  
63 -}  
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java
1 package cn.iocoder.dashboard.modules.system.service.sms.impl; 1 package cn.iocoder.dashboard.modules.system.service.sms.impl;
2 2
3 -import cn.hutool.core.collection.CollectionUtil;  
4 -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient;  
5 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO; 3 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO;
6 -import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDOX;  
7 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; 4 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
8 -import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsQueryLogMapper;  
9 import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsSendLogMapper; 5 import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsSendLogMapper;
10 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; 6 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
11 -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;  
12 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsSendLogService; 7 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsSendLogService;
13 import lombok.extern.slf4j.Slf4j; 8 import lombok.extern.slf4j.Slf4j;
14 -import org.springframework.scheduling.annotation.Scheduled;  
15 import org.springframework.stereotype.Service; 9 import org.springframework.stereotype.Service;
16 10
17 import javax.annotation.Resource; 11 import javax.annotation.Resource;
18 -import java.util.List; 12 +import java.util.Date;
19 import java.util.Map; 13 import java.util.Map;
  14 +import java.util.Objects;
20 15
21 /** 16 /**
22 * 短信发送日志服务实现类 17 * 短信发送日志服务实现类
@@ -29,19 +24,8 @@ import java.util.Map; @@ -29,19 +24,8 @@ import java.util.Map;
29 public class SysSmsSendLogServiceImpl implements SysSmsSendLogService { 24 public class SysSmsSendLogServiceImpl implements SysSmsSendLogService {
30 25
31 @Resource 26 @Resource
32 - private SysSmsQueryLogMapper smsQueryLogMapper;  
33 -  
34 - @Resource  
35 private SysSmsSendLogMapper smsSendLogMapper; 27 private SysSmsSendLogMapper smsSendLogMapper;
36 28
37 - @Resource  
38 - private SysSmsChannelService smsChannelService;  
39 -  
40 - /**  
41 - * 定时执行 {@link #getSmsSendResultJob()} 的周期  
42 - */  
43 - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L;  
44 -  
45 @Override 29 @Override
46 public Long createSmsSendLog(String mobile, Long userId, Integer userType, 30 public Long createSmsSendLog(String mobile, Long userId, Integer userType,
47 SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams) { 31 SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams) {
@@ -61,71 +45,12 @@ public class SysSmsSendLogServiceImpl implements SysSmsSendLogService { @@ -61,71 +45,12 @@ public class SysSmsSendLogServiceImpl implements SysSmsSendLogService {
61 } 45 }
62 46
63 @Override 47 @Override
64 - public void updateSmsSendLogFailure(Long id, Integer sendFailureType) {  
65 - smsSendLogMapper.updateById(new SysSmsSendLogDO().setId(id).setSendFailureType(sendFailureType));  
66 - }  
67 -  
68 - @Override  
69 - public void getAndSaveSmsSendLog() {  
70 -  
71 - List<SysSmsSendLogDO> noResultQueryLogList = smsQueryLogMapper.selectNoResultQueryLogList();  
72 -  
73 - if (CollectionUtil.isEmpty(noResultQueryLogList)) {  
74 - return;  
75 - }  
76 - //用于添加的发送日志对象  
77 - SysSmsSendLogDOX insertSendLog = new SysSmsSendLogDOX();  
78 - //用于修改状态的请求日志对象  
79 - SysSmsSendLogDO updateQueryLog = new SysSmsSendLogDO();  
80 -  
81 - noResultQueryLogList.forEach(queryLog -> {  
82 - AbstractSmsClient smsClient = smsChannelService.getSmsClient(queryLog.getTemplateCode());  
83 -  
84 - updateQueryLog.setId(queryLog.getId());  
85 -  
86 - // 只处理实现了获取发送结果方法的短信客户端,理论上这里都是满足条件的,以防万一加个判断。  
87 - /*if (smsClient instanceof NeedQuerySendResultSmsClient) {  
88 - //初始化点字段值  
89 - queryLog2SendLong(insertSendLog, queryLog);  
90 -  
91 - NeedQuerySendResultSmsClient querySendResultSmsClient = (NeedQuerySendResultSmsClient) smsClient;  
92 - try {  
93 - List<SmsResultDetail> smsSendResult = querySendResultSmsClient.getSmsSendResult(queryLog.getRemark());  
94 - smsSendResult.forEach(resultDetail -> {  
95 - insertSendLog.setPhone(resultDetail.getPhone());  
96 - insertSendLog.setSendStatus(resultDetail.getSendStatus());  
97 - insertSendLog.setSendTime(resultDetail.getSendTime());  
98 - insertSendLog.setRemark(resultDetail.getMessage());  
99 - smsSendLogMapper.insert(insertSendLog);  
100 - });  
101 - } catch (Exception e) {  
102 - //exception handle  
103 - log.error("query send result fail, exception: " + e.getMessage());  
104 -  
105 - updateQueryLog.setSendStatus(SmsSendStatusEnum.QUERY_SEND_FAIL.getStatus());  
106 - updateQueryLog.setRemark(e.getMessage());  
107 - smsQueryLogMapper.updateById(updateQueryLog);  
108 - return;  
109 - }  
110 - } else {  
111 - //理论上这里都是满足条件的,以防万一加个判断。  
112 - updateQueryLog.setSendStatus(SmsSendStatusEnum.QUERY_SEND_FAIL.getStatus());  
113 - smsQueryLogMapper.updateById(updateQueryLog);  
114 - }*/  
115 - updateQueryLog.setSendStatus(SysSmsSendStatusEnum.SEND_SUCCESS.getStatus());  
116 - updateQueryLog.setRemark(String.format("日志(id = %s)对应的客户端没有继承NeedQuerySendResultSmsClient, 不能获取短信结果。", queryLog.getId()));  
117 - smsQueryLogMapper.updateById(updateQueryLog);  
118 - });  
119 - }  
120 -  
121 - private void queryLog2SendLong(SysSmsSendLogDOX insertSendLog, SysSmsSendLogDO queryLog) {  
122 - insertSendLog.setChannelCode(queryLog.getChannelCode());  
123 - insertSendLog.setChannelId(queryLog.getChannelId());  
124 - insertSendLog.setTemplateCode(queryLog.getTemplateCode()); 48 + public void updateSmsSendLogResult(Long id, Boolean success, Integer sendFailureType, String sendFailureMsg,
  49 + String apiSendFailureType, String apiSendFailureMsg, String apiRequestId, String apiSerialNo) {
  50 + SysSmsSendStatusEnum sendStatus = Objects.equals(success, true) ? SysSmsSendStatusEnum.SUCCESS : SysSmsSendStatusEnum.FAILURE;
  51 + smsSendLogMapper.updateById(new SysSmsSendLogDO().setId(id).setSendStatus(sendStatus.getStatus()).setSendTime(new Date())
  52 + .setSendFailureType(sendFailureType).setSendFailureMsg(sendFailureMsg)
  53 + .setApiSendFailureType(apiSendFailureType).setApiSendFailureMsg(apiSendFailureMsg).setApiRequestId(apiRequestId).setApiSerialNo(apiSerialNo));
125 } 54 }
126 55
127 - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)  
128 - public void getSmsSendResultJob() {  
129 - getAndSaveSmsSendLog();  
130 - }  
131 } 56 }
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java
@@ -4,15 +4,19 @@ import cn.hutool.core.collection.CollUtil; @@ -4,15 +4,19 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.StrUtil; 4 import cn.hutool.core.util.StrUtil;
5 import cn.iocoder.dashboard.common.enums.CommonStatusEnum; 5 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
6 import cn.iocoder.dashboard.common.enums.UserTypeEnum; 6 import cn.iocoder.dashboard.common.enums.UserTypeEnum;
7 -import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory;  
8 -import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; 7 +import cn.iocoder.dashboard.framework.sms.core.SmsResult;
  8 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient;
  9 +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory;
9 import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum; 10 import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum;
10 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; 11 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
11 import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; 12 import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
12 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; 13 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage;
13 import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer; 14 import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer;
14 -import cn.iocoder.dashboard.modules.system.service.sms.*; 15 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsSendLogService;
  16 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;
  17 +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService;
15 import cn.iocoder.dashboard.modules.system.service.user.SysUserService; 18 import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
  19 +import lombok.extern.slf4j.Slf4j;
16 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
17 21
18 import javax.annotation.Resource; 22 import javax.annotation.Resource;
@@ -31,29 +35,21 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; @@ -31,29 +35,21 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
31 * @date 2021/1/25 9:25 35 * @date 2021/1/25 9:25
32 */ 36 */
33 @Service 37 @Service
  38 +@Slf4j
34 public class SysSmsServiceImpl implements SysSmsService { 39 public class SysSmsServiceImpl implements SysSmsService {
35 40
36 @Resource 41 @Resource
37 private SysSmsTemplateService smsTemplateService; 42 private SysSmsTemplateService smsTemplateService;
38 @Resource 43 @Resource
39 private SysSmsSendLogService smsSendLogService; 44 private SysSmsSendLogService smsSendLogService;
40 -  
41 - @Resource  
42 - private SysUserService userService;  
43 -  
44 - @Resource  
45 - private SysSmsChannelService channelService;  
46 -  
47 -  
48 - @Resource  
49 - private SysSmsQueryLogService logService;  
50 -  
51 @Resource 45 @Resource
52 private SysSmsProducer smsProducer; 46 private SysSmsProducer smsProducer;
53 -  
54 @Resource 47 @Resource
55 private SmsClientFactory smsClientFactory; 48 private SmsClientFactory smsClientFactory;
56 49
  50 + @Resource
  51 + private SysUserService userService;
  52 +
57 @Override 53 @Override
58 public void sendSingleSms(String mobile, Long userId, Integer userType, 54 public void sendSingleSms(String mobile, Long userId, Integer userType,
59 String templateCode, Map<String, Object> templateParams) { 55 String templateCode, Map<String, Object> templateParams) {
@@ -68,7 +64,7 @@ public class SysSmsServiceImpl implements SysSmsService { @@ -68,7 +64,7 @@ public class SysSmsServiceImpl implements SysSmsService {
68 64
69 // 如果模板被禁用,则直接标记发送失败。也就说,不发短信,嘿嘿。 65 // 如果模板被禁用,则直接标记发送失败。也就说,不发短信,嘿嘿。
70 if (CommonStatusEnum.DISABLE.getStatus().equals(template.getStatus())) { 66 if (CommonStatusEnum.DISABLE.getStatus().equals(template.getStatus())) {
71 - smsSendLogService.updateSmsSendLogFailure(sendLogId, SmsSendFailureTypeEnum.SMS_TEMPLATE_DISABLE.getType()); 67 + smsSendLogService.updateSmsSendLogFailure(sendLogId, SmsSendFailureTypeEnum.SMS_TEMPLATE_DISABLE);
72 return; 68 return;
73 } 69 }
74 // 如果模板未禁用,发送 MQ 消息。目的是,异步化调用短信平台 70 // 如果模板未禁用,发送 MQ 消息。目的是,异步化调用短信平台
@@ -126,19 +122,31 @@ public class SysSmsServiceImpl implements SysSmsService { @@ -126,19 +122,31 @@ public class SysSmsServiceImpl implements SysSmsService {
126 SysUserDO user = userService.getUser(userId); 122 SysUserDO user = userService.getUser(userId);
127 return user != null ? user.getMobile() : null; 123 return user != null ? user.getMobile() : null;
128 } 124 }
  125 + // TODO 芋艿:支持 C 端用户
129 return null; 126 return null;
130 } 127 }
131 128
132 @Override 129 @Override
133 public void doSendSms(SysSmsSendMessage message) { 130 public void doSendSms(SysSmsSendMessage message) {
  131 + // 获得渠道对应的 SmsClient 客户端
  132 + SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId());
  133 + if (smsClient == null) {
  134 + log.error("[doSendSms][短信 message({}) 找不到对应的客户端]", message);
  135 + smsSendLogService.updateSmsSendLogFailure(message.getSendLogId(), SmsSendFailureTypeEnum.SMS_CHANNEL_CLIENT_NOT_EXISTS);
  136 + return;
  137 + }
134 138
  139 + // 发送短信
  140 + SmsResult sendResult = smsClient.send(message.getSendLogId(), message.getMobile(),
  141 + message.getApiTemplateId(), message.getTemplateParams());
135 } 142 }
136 143
137 @Override 144 @Override
138 public Object smsSendCallbackHandle(ServletRequest request) { 145 public Object smsSendCallbackHandle(ServletRequest request) {
139 - SmsResultDetail smsResultDetail = smsClientFactory.getSmsResultDetailFromCallbackQuery(request);  
140 - logService.updateSendLogByResultDetail(smsResultDetail);  
141 - return smsResultDetail.getCallbackResponseBody(); 146 +// SmsResultDetail smsResultDetail = smsClientFactory.getSmsResultDetailFromCallbackQuery(request);
  147 +// logService.updateSendLogByResultDetail(smsResultDetail);
  148 +// return smsResultDetail.getCallbackResponseBody();
  149 + return null;
142 } 150 }
143 151
144 } 152 }