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 46 <easyexcel.verion>2.2.7</easyexcel.verion>
47 47 <velocity.version>2.2</velocity.version>
48 48 <screw.version>1.0.5</screw.version>
  49 + <!-- 三方云服务相关 -->
49 50 </properties>
50 51  
51 52 <!-- 依赖声明 -->
... ... @@ -271,6 +272,8 @@
271 272 <version>${screw.version}</version>
272 273 </dependency>
273 274  
  275 + <!-- 三方云服务相关 -->
  276 +
274 277 <!-- SMS SDK begin -->
275 278 <dependency>
276 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 15 String COMMA = ",";
16 16  
17 17 String SUCCESS = "SUCCESS";
  18 +
18 19 }
... ...
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java
1 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 5 import lombok.Data;
4   -import lombok.experimental.Accessors;
5 6  
6 7 import java.io.Serializable;
7 8  
... ... @@ -9,33 +10,64 @@ import java.io.Serializable;
9 10 * 消息内容实体类
10 11 */
11 12 @Data
12   -@Accessors(chain = true)
13 13 public class SmsResult implements Serializable {
14 14  
15 15 /**
16   - * 是否成功(发送短信的请求是否成功)
  16 + * 是否成功
  17 + *
  18 + * 注意,是调用 API 短信平台的请求是否成功
17 19 */
18 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 3 import cn.iocoder.dashboard.framework.sms.core.SmsResult;
5 4 import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail;
6 5  
7 6 import javax.servlet.ServletRequest;
  7 +import java.util.Map;
8 8  
9 9 /**
10   - * 短信父接口
  10 + * 短信客户端接口
11 11 *
12 12 * @author zzf
13 13 * @date 2021/1/25 14:14
... ... @@ -15,14 +15,22 @@ import javax.servlet.ServletRequest;
15 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 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 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 3 import cn.hutool.core.collection.CollectionUtil;
4 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 5 import cn.iocoder.dashboard.framework.sms.core.SmsResult;
8 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 9 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
11 10 import cn.iocoder.dashboard.util.json.JsonUtils;
12 11 import com.aliyuncs.DefaultAcsClient;
13 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 13 import com.aliyuncs.profile.DefaultProfile;
18 14 import com.aliyuncs.profile.IClientProfile;
19 15 import com.fasterxml.jackson.core.type.TypeReference;
... ... @@ -28,7 +24,7 @@ import java.util.List;
28 24 import java.util.Map;
29 25  
30 26 /**
31   - * 阿里短信实现类
  27 + * 阿里短信客户端的实现类
32 28 *
33 29 * @author zzf
34 30 * @date 2021/1/25 14:17
... ... @@ -36,54 +32,54 @@ import java.util.Map;
36 32 @Slf4j
37 33 public class AliyunSmsClient extends AbstractSmsClient {
38 34  
39   - private static final String OK = "OK";
40   -
41 35 private static final String PRODUCT = "Dystopi";
42   -
43 36 private static final String DOMAIN = "dysmsapi.aliyuncs.com";
44   -
45 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 53 DefaultProfile.addEndpoint(ENDPOINT, PRODUCT, DOMAIN);
62   -
63 54 acsClient = new DefaultAcsClient(profile);
64 55 }
65 56  
66 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 85 * "send_time" : "2017-08-30 00:00:00",
... ... @@ -131,8 +127,8 @@ public class AliyunSmsClient extends AbstractSmsClient {
131 127  
132 128 public Integer getSendStatus() {
133 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 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 4 import cn.hutool.core.collection.CollectionUtil;
4 5 import cn.hutool.core.date.DateUtil;
5 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 8 import cn.iocoder.dashboard.framework.sms.core.SmsConstants;
9 9 import cn.iocoder.dashboard.framework.sms.core.SmsResult;
10 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 14 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
13 15 import cn.iocoder.dashboard.util.json.JsonUtils;
14 16 import com.fasterxml.jackson.core.type.TypeReference;
15 17 import com.yunpian.sdk.YunpianClient;
16   -import com.yunpian.sdk.constant.Code;
17 18 import com.yunpian.sdk.constant.YunpianConstant;
18 19 import com.yunpian.sdk.model.Result;
19 20 import com.yunpian.sdk.model.SmsSingleSend;
... ... @@ -25,9 +26,10 @@ import java.net.URLEncoder;
25 26 import java.util.HashMap;
26 27 import java.util.List;
27 28 import java.util.Map;
  29 +import java.util.StringJoiner;
28 30  
29 31 /**
30   - * 云片短信实现类
  32 + * 云片短信客户端的实现类
31 33 *
32 34 * @author zzf
33 35 * @date 9:48 2021/3/5
... ... @@ -35,70 +37,58 @@ import java.util.Map;
35 37 @Slf4j
36 38 public class YunpianSmsClient extends AbstractSmsClient {
37 39  
38   - private final YunpianClient client;
  40 + /**
  41 + * 云信短信客户端
  42 + */
  43 + private volatile YunpianClient client;
39 44  
40 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 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 99 return Helper.getSmsResultDetailByParam(map);
110 100 }
111 101  
112   -
113 102 /**
114 103 * 从 request 中获取请求中传入的短信发送结果信息
115 104 *
... ... @@ -155,8 +144,8 @@ public class YunpianSmsClient extends AbstractSmsClient {
155 144 private static int getSendStatus(Map<String, String> map) {
156 145 String reportStatus = map.get(REPORT_STATUS);
157 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 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 13 public enum SmsSendFailureTypeEnum {
14 14  
15 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 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 1 package cn.iocoder.dashboard.framework.sms.core.property;
2 2  
  3 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
3 4 import lombok.Data;
4   -import lombok.EqualsAndHashCode;
  5 +import org.springframework.validation.annotation.Validated;
5 6  
6 7 import javax.validation.constraints.NotEmpty;
7 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 13 * @author zzf
15 14 * @date 2021/1/25 17:01
16 15 */
17 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 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 36 private String code;
32   -
33 37 /**
34   - * 渠道账号id
  38 + * 短信 API 的账号
35 39 */
36   - @NotEmpty(message = "渠道账号id不能为空")
  40 + @NotEmpty(message = "短信 API 的账号不能为空")
37 41 private String apiKey;
38   -
39 42 /**
40   - * 渠道账号秘钥
  43 + * 短信 API 的秘钥
41 44 */
42   - @NotEmpty(message = "渠道账号秘钥不能为空")
  45 + @NotEmpty(message = "短信 API 的秘钥不能为空")
43 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 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 15 @EqualsAndHashCode(callSuper = true)
16 16 public class SmsChannelPageReqVO extends PageParam {
17 17  
18   - @ApiModelProperty(value = "渠道名", example = "阿里", notes = "模糊匹配")
19   - private String name;
20   -
21 18 @ApiModelProperty(value = "签名值", example = "源码", notes = "模糊匹配")
22 19 private String signature;
23 20  
... ...
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java
1 1 package cn.iocoder.dashboard.modules.system.convert.sms;
2 2  
3 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 5 import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO;
7 6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
8 7 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
9 8 import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO;
10 9 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
11   -import com.baomidou.mybatisplus.core.metadata.IPage;
12 10 import org.mapstruct.Mapper;
13   -import org.mapstruct.Mapping;
14 11 import org.mapstruct.factory.Mappers;
15 12  
16 13 import java.util.List;
... ... @@ -20,9 +17,6 @@ public interface SmsChannelConvert {
20 17  
21 18 SmsChannelConvert INSTANCE = Mappers.getMapper(SmsChannelConvert.class);
22 19  
23   - @Mapping(source = "records", target = "list")
24   - PageResult<SysSmsChannelDO> convertPage(IPage<SysSmsChannelDO> page);
25   -
26 20 SysSmsChannelDO convert(SmsChannelCreateReqVO bean);
27 21  
28 22 SysSmsChannelDO convert(SysUserUpdateReqVO bean);
... ... @@ -31,9 +25,8 @@ public interface SmsChannelConvert {
31 25  
32 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 1 package cn.iocoder.dashboard.modules.system.convert.sms;
2 2  
3 3 import cn.iocoder.dashboard.common.pojo.PageResult;
4   -import cn.iocoder.dashboard.framework.sms.core.property.SmsTemplateProperty;
5 4 import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO;
6 5 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
7 6 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
... ... @@ -24,6 +23,4 @@ public interface SmsTemplateConvert {
24 23  
25 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 1 package cn.iocoder.dashboard.modules.system.dal.dataobject.sms;
2 2  
  3 +import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
3 4 import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO;
  5 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum;
4 6 import com.baomidou.mybatisplus.annotation.TableName;
5 7 import lombok.Data;
6 8 import lombok.EqualsAndHashCode;
7 9  
8   -
9 10 /**
10 11 * 短信渠道
11 12 *
12 13 * @author zzf
13 14 * @since 2021-01-25
14 15 */
  16 +@TableName(value = "sms_channel", autoResultMap = true)
15 17 @Data
16 18 @EqualsAndHashCode(callSuper = true)
17   -@TableName(value = "sms_channel", autoResultMap = true)
18 19 public class SysSmsChannelDO extends BaseDO {
19 20  
20 21 /**
21   - * 自增编号
  22 + * 渠道编号
22 23 */
23 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 106 */
107 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 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 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 1 package cn.iocoder.dashboard.modules.system.dal.mysql.sms;
2 2  
3 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 6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
7 7 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO;
8 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 9 import org.apache.ibatis.annotations.Mapper;
12 10  
13 11 import java.util.List;
14 12  
15 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 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 1 package cn.iocoder.dashboard.modules.system.dal.mysql.sms;
2 2  
  3 +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX;
3 4 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO;
4   -import com.baomidou.mybatisplus.core.mapper.BaseMapper;
5 5 import org.apache.ibatis.annotations.Mapper;
6 6  
7 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 1 package cn.iocoder.dashboard.modules.system.mq.consumer.sms;
2 2  
3 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 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 5 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService;
11   -import cn.iocoder.dashboard.util.json.JsonUtils;
12 6 import lombok.extern.slf4j.Slf4j;
13   -import org.springframework.data.redis.connection.stream.ObjectRecord;
14   -import org.springframework.data.redis.stream.StreamListener;
15 7 import org.springframework.stereotype.Component;
16 8  
17 9 import javax.annotation.Resource;
... ... @@ -27,24 +19,9 @@ import javax.annotation.Resource;
27 19 public class SmsSendConsumer extends AbstractStreamMessageListener<SysSmsSendMessage> {
28 20  
29 21 @Resource
30   - private SysSmsChannelService smsChannelService;
31   -
32   - @Resource
33   - private SysSmsQueryLogService smsQueryLogService;
34   -
35   - @Resource
36 22 private SysSmsService smsService;
37 23  
38 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 25 public void onMessage(SysSmsSendMessage message) {
49 26 log.info("[onMessage][消息内容({})]", message);
50 27 smsService.doSendSms(message);
... ...
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java
1 1 package cn.iocoder.dashboard.modules.system.service.sms;
2 2  
3 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 4 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
7 5 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
8 6 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
... ... @@ -46,26 +44,4 @@ public interface SysSmsChannelService {
46 44 */
47 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 1 package cn.iocoder.dashboard.modules.system.service.sms;
2 2  
  3 +import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum;
3 4 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
4 5  
5 6 import java.util.Map;
... ... @@ -12,17 +13,38 @@ import java.util.Map;
12 13 */
13 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 27 Long createSmsSendLog(String mobile, Long userId, Integer userType,
16 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 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 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 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 8 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO;
12 9 import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO;
13 10 import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO;
14 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 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 15 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
21 16 import org.springframework.stereotype.Service;
22 17  
23 18 import javax.annotation.PostConstruct;
24 19 import javax.annotation.Resource;
25   -import java.util.ArrayList;
26 20 import java.util.Arrays;
27 21 import java.util.List;
28   -import java.util.Map;
29   -import java.util.concurrent.ConcurrentHashMap;
30 22  
31 23 /**
32 24 * 短信渠道Service实现类
... ... @@ -37,10 +29,8 @@ import java.util.concurrent.ConcurrentHashMap;
37 29 @Service
38 30 public class SysSmsChannelServiceImpl implements SysSmsChannelService {
39 31  
40   - private final Map<String, Long> templateCode2ChannelIdMap = new ConcurrentHashMap<>(32);
41   -
42 32 @Resource
43   - private SmsClientFactory clientFactory;
  33 + private SmsClientFactory smsClientFactory;
44 34  
45 35 @Resource
46 36 private SysSmsChannelMapper channelMapper;
... ... @@ -48,30 +38,19 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService {
48 38 @Resource
49 39 private SysSmsTemplateMapper templateMapper;
50 40  
51   -
52   - @PostConstruct
53 41 @Override
  42 + @PostConstruct
54 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 51 @Override
73 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 56 @Override
... ... @@ -86,30 +65,20 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService {
86 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 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 3 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO;
6   -import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDOX;
7 4 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
8   -import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsQueryLogMapper;
9 5 import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsSendLogMapper;
10 6 import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum;
11   -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService;
12 7 import cn.iocoder.dashboard.modules.system.service.sms.SysSmsSendLogService;
13 8 import lombok.extern.slf4j.Slf4j;
14   -import org.springframework.scheduling.annotation.Scheduled;
15 9 import org.springframework.stereotype.Service;
16 10  
17 11 import javax.annotation.Resource;
18   -import java.util.List;
  12 +import java.util.Date;
19 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 24 public class SysSmsSendLogServiceImpl implements SysSmsSendLogService {
30 25  
31 26 @Resource
32   - private SysSmsQueryLogMapper smsQueryLogMapper;
33   -
34   - @Resource
35 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 29 @Override
46 30 public Long createSmsSendLog(String mobile, Long userId, Integer userType,
47 31 SysSmsTemplateDO template, String templateContent, Map<String, Object> templateParams) {
... ... @@ -61,71 +45,12 @@ public class SysSmsSendLogServiceImpl implements SysSmsSendLogService {
61 45 }
62 46  
63 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 4 import cn.hutool.core.util.StrUtil;
5 5 import cn.iocoder.dashboard.common.enums.CommonStatusEnum;
6 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 10 import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum;
10 11 import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO;
11 12 import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO;
12 13 import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage;
13 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 18 import cn.iocoder.dashboard.modules.system.service.user.SysUserService;
  19 +import lombok.extern.slf4j.Slf4j;
16 20 import org.springframework.stereotype.Service;
17 21  
18 22 import javax.annotation.Resource;
... ... @@ -31,29 +35,21 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*;
31 35 * @date 2021/1/25 9:25
32 36 */
33 37 @Service
  38 +@Slf4j
34 39 public class SysSmsServiceImpl implements SysSmsService {
35 40  
36 41 @Resource
37 42 private SysSmsTemplateService smsTemplateService;
38 43 @Resource
39 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 45 @Resource
52 46 private SysSmsProducer smsProducer;
53   -
54 47 @Resource
55 48 private SmsClientFactory smsClientFactory;
56 49  
  50 + @Resource
  51 + private SysUserService userService;
  52 +
57 53 @Override
58 54 public void sendSingleSms(String mobile, Long userId, Integer userType,
59 55 String templateCode, Map<String, Object> templateParams) {
... ... @@ -68,7 +64,7 @@ public class SysSmsServiceImpl implements SysSmsService {
68 64  
69 65 // 如果模板被禁用,则直接标记发送失败。也就说,不发短信,嘿嘿。
70 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 68 return;
73 69 }
74 70 // 如果模板未禁用,发送 MQ 消息。目的是,异步化调用短信平台
... ... @@ -126,19 +122,31 @@ public class SysSmsServiceImpl implements SysSmsService {
126 122 SysUserDO user = userService.getUser(userId);
127 123 return user != null ? user.getMobile() : null;
128 124 }
  125 + // TODO 芋艿:支持 C 端用户
129 126 return null;
130 127 }
131 128  
132 129 @Override
133 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 144 @Override
138 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 }
... ...