From 515fca5c41819e8187167c483e05d21cd4b4b029 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 28 Mar 2021 22:49:14 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9F=AD=E4=BF=A1=E6=8F=90=E4=BA=A4=202021-03-?= =?UTF-8?q?28=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=8F=91=E9=80=81=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 3 + .../sms/client/AbstractSmsClient.java | 67 --------- .../sms/config/SmsConfiguration.java | 21 +++ .../dashboard/framework/sms/core/SmsBody.java | 38 ----- .../framework/sms/core/SmsClientFactory.java | 133 ------------------ .../framework/sms/core/SmsConstants.java | 1 + .../framework/sms/core/SmsResult.java | 64 ++++++--- .../sms/{ => core}/client/SmsClient.java | 22 ++- .../sms/core/client/SmsClientFactory.java | 28 ++++ .../core/client/impl/AbstractSmsClient.java | 89 ++++++++++++ .../client/impl/SmsClientFactoryImpl.java | 83 +++++++++++ .../client/impl/aliyun}/AliyunSmsClient.java | 80 +++++------ .../client/impl/yunpian/YunpianSmsClient.java | 105 +++++++------- .../core/enums/SmsSendFailureTypeEnum.java | 13 +- .../core/property/SmsChannelProperties.java | 52 +++++++ .../sms/core/property/SmsChannelProperty.java | 68 --------- .../core/property/SmsTemplateProperty.java | 47 ------- .../sms/vo/req/SmsChannelPageReqVO.java | 3 - .../system/convert/sms/SmsChannelConvert.java | 13 +- .../convert/sms/SmsTemplateConvert.java | 3 - .../dal/dataobject/sms/SysSmsChannelDO.java | 66 ++++----- .../dal/dataobject/sms/SysSmsSendLogDO.java | 13 +- .../dal/dataobject/sms/SysSmsSendLogDOX.java | 66 --------- .../dal/mysql/sms/SysSmsChannelMapper.java | 24 ++-- .../dal/mysql/sms/SysSmsQueryLogMapper.java | 34 ----- .../dal/mysql/sms/SysSmsSendLogMapper.java | 5 +- .../mq/consumer/sms/SmsSendConsumer.java | 23 --- .../service/sms/SysSmsChannelService.java | 24 ---- .../service/sms/SysSmsQueryLogService.java | 35 ----- .../service/sms/SysSmsSendLogService.java | 32 ++++- .../sms/impl/SysSmsChannelServiceImpl.java | 87 ++++-------- .../sms/impl/SysSmsQueryLogServiceImpl.java | 63 --------- .../sms/impl/SysSmsSendLogServiceImpl.java | 91 ++---------- .../service/sms/impl/SysSmsServiceImpl.java | 48 ++++--- 34 files changed, 579 insertions(+), 965 deletions(-) delete mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/config/SmsConfiguration.java delete mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java delete mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java rename src/main/java/cn/iocoder/dashboard/framework/sms/{ => core}/client/SmsClient.java (57%) create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java rename src/main/java/cn/iocoder/dashboard/framework/sms/{client/impl/ali => core/client/impl/aliyun}/AliyunSmsClient.java (73%) rename src/main/java/cn/iocoder/dashboard/framework/sms/{ => core}/client/impl/yunpian/YunpianSmsClient.java (60%) create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperties.java delete mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperty.java delete mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsTemplateProperty.java delete mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDOX.java delete mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsQueryLogMapper.java delete mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsQueryLogService.java delete mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsQueryLogServiceImpl.java diff --git a/pom.xml b/pom.xml index 83d372add..949978260 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ 2.2.7 2.2 1.0.5 + @@ -271,6 +272,8 @@ ${screw.version} + + com.yunpian.sdk diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java deleted file mode 100644 index fbeceea10..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.dashboard.framework.sms.client; - -import cn.iocoder.dashboard.framework.sms.core.SmsBody; -import cn.iocoder.dashboard.framework.sms.core.SmsResult; -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; -import lombok.extern.slf4j.Slf4j; - -/** - * 抽象短息客户端 - * - * @author zzf - * @date 2021/2/1 9:28 - */ -@Slf4j -public abstract class AbstractSmsClient implements SmsClient { - - /** - * 短信渠道参数 - */ - protected final SmsChannelProperty channelVO; - - /** - * 短信客户端有参构造函数 - * - * @param property 短信配置 - */ - public AbstractSmsClient(SmsChannelProperty property) { - this.channelVO = property; - } - - public SmsChannelProperty getProperty() { - return channelVO; - } - - @Override - public final SmsResult send(String templateApiId, SmsBody smsBody, String target) { - SmsResult result; - try { - beforeSend(templateApiId, smsBody, target); - result = doSend(templateApiId, smsBody, target); - afterSend(templateApiId, smsBody, target, result); - } catch (Exception e) { - // exception handle - log.debug(e.getMessage(), e); - return SmsResult.failResult("发送异常: " + e.getMessage()); - } - return result; - } - - /** - * 发送消息 - * - * @param templateApiId 短信模板唯一标识 - * @param smsBody 消息内容 - * @param targetPhone 发送对象手机号 - * @return 短信发送结果 - * @throws Exception 调用发送失败,抛出异常 - */ - protected abstract SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception; - - protected void beforeSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception { - } - - protected void afterSend(String templateApiId, SmsBody smsBody, String targetPhone, SmsResult result) throws Exception { - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/config/SmsConfiguration.java b/src/main/java/cn/iocoder/dashboard/framework/sms/config/SmsConfiguration.java new file mode 100644 index 000000000..e5441c91f --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/config/SmsConfiguration.java @@ -0,0 +1,21 @@ +package cn.iocoder.dashboard.framework.sms.config; + +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; +import cn.iocoder.dashboard.framework.sms.core.client.impl.SmsClientFactoryImpl; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 短信配置类 + * + * @author 芋道源码 + */ +@Configuration +public class SmsConfiguration { + + @Bean + public SmsClientFactory smsClientFactory() { + return new SmsClientFactoryImpl(); + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java deleted file mode 100644 index f82f0e142..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java +++ /dev/null @@ -1,38 +0,0 @@ -package cn.iocoder.dashboard.framework.sms.core; - -import cn.iocoder.dashboard.util.json.JsonUtils; -import lombok.Data; - -import java.util.Map; - -/** - * 消息内容实体类 - */ -@Data -public class SmsBody { - - /** - * 消息日志id - */ - private Long smsLogId; - - /** - * 模板编码 - */ - private String templateCode; - - /** - * 模板编码 - */ - private String templateContent; - - /** - * 参数列表 - */ - private Map params; - - public String getParamsStr() { - return JsonUtils.toJsonString(params); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java deleted file mode 100644 index 2350bda88..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java +++ /dev/null @@ -1,133 +0,0 @@ -package cn.iocoder.dashboard.framework.sms.core; - -import cn.iocoder.dashboard.common.exception.ServiceException; -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; -import cn.iocoder.dashboard.framework.sms.client.impl.ali.AliyunSmsClient; -import cn.iocoder.dashboard.framework.sms.client.impl.yunpian.YunpianSmsClient; -import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; -import cn.iocoder.dashboard.framework.sms.core.property.SmsTemplateProperty; -import cn.iocoder.dashboard.util.json.JsonUtils; -import org.springframework.stereotype.Component; - -import javax.servlet.ServletRequest; -import java.util.Collection; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; - -/** - * 短信客户端工厂 - * - * @author zzf - * @date 2021/1/28 14:01 - */ -@Component -public class SmsClientFactory { - - /** - * channelId: client map - * 保存 渠道id: 对应短信客户端 的map - */ - private final Map smsSenderMap = new ConcurrentHashMap<>(8); - - /** - * templateCode: TemplateProperty map - * 保存 模板编码:模板信息 的map - */ - private final Map templatePropertyMap = new ConcurrentHashMap<>(16); - - /** - * 创建短信客户端 - * - * @param propertyVO 参数对象 - * @return 客户端id(默认channelId) - */ - public Long createClient(SmsChannelProperty propertyVO) { - AbstractSmsClient sender = createClient(SmsChannelEnum.getByCode(propertyVO.getCode()), propertyVO); - smsSenderMap.put(propertyVO.getId(), sender); - return propertyVO.getId(); - } - - private AbstractSmsClient createClient(SmsChannelEnum channelEnum, SmsChannelProperty channelVO) { - if (channelEnum == null) { - throw new ServiceException(INVALID_CHANNEL_CODE); - } - switch (channelEnum) { - case ALIYUN: - return new AliyunSmsClient(channelVO); - case YUN_PIAN: - return new YunpianSmsClient(channelVO); - // TODO fill more channel - default: - break; - } - throw new ServiceException(SMS_SENDER_NOT_FOUND); - } - - /** - * 获取短信客户端 - * - * @param channelId 渠道id - * @return 短信id - */ - public AbstractSmsClient getClient(Long channelId) { - return smsSenderMap.get(channelId); - } - - - /** - * 添加或修改短信模板信息缓存 - */ - public void addOrUpdateTemplateCache(Collection templateProperties) { - templateProperties.forEach(this::addOrUpdateTemplateCache); - } - - - /** - * 添加或修改短信模板信息缓存 - */ - public void addOrUpdateTemplateCache(SmsTemplateProperty templateProperty) { - templatePropertyMap.put(templateProperty.getCode(), templateProperty); - } - - - /** - * 根据短信模板编码获取模板唯一标识 - * - * @param templateCode 短信模板编码 - * @return 短信id - */ - public String getTemplateApiIdByCode(String templateCode) { - SmsTemplateProperty smsTemplateProperty = templatePropertyMap.get(templateCode); - if (smsTemplateProperty == null) { - throw new ServiceException(SMS_TEMPLATE_NOT_EXISTS); - } - return smsTemplateProperty.getApiTemplateId(); - } - - - /** - * 从短信发送回调函数请求中获取用于唯一确定一条send_lod的apiId - * - * @param callbackRequest 短信发送回调函数请求 - * @return 第三方平台短信唯一标识 - */ - public SmsResultDetail getSmsResultDetailFromCallbackQuery(ServletRequest callbackRequest) { - for (Long channelId : smsSenderMap.keySet()) { - AbstractSmsClient smsClient = smsSenderMap.get(channelId); - try { - SmsResultDetail smsSendResult = smsClient.smsSendCallbackHandle(callbackRequest); - if (smsSendResult != null) { - return smsSendResult; - } - } catch (Exception ignored) { - } - } - throw new IllegalArgumentException("getSmsResultDetailFromCallbackQuery fail! don't match SmsClient by RequestParam: " - + JsonUtils.toJsonString(callbackRequest.getParameterMap())); - } - - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsConstants.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsConstants.java index e519306f3..139cc6736 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsConstants.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsConstants.java @@ -15,4 +15,5 @@ public interface SmsConstants { String COMMA = ","; String SUCCESS = "SUCCESS"; + } diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java index 228630348..1754b897d 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java @@ -1,7 +1,8 @@ package cn.iocoder.dashboard.framework.sms.core; +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum; import lombok.Data; -import lombok.experimental.Accessors; import java.io.Serializable; @@ -9,33 +10,64 @@ import java.io.Serializable; * 消息内容实体类 */ @Data -@Accessors(chain = true) public class SmsResult implements Serializable { /** - * 是否成功(发送短信的请求是否成功) + * 是否成功 + * + * 注意,是调用 API 短信平台的请求是否成功 */ private Boolean success; + /** + * 发送失败的类型 + * + * 枚举 {@link SmsSendFailureTypeEnum#getType()} + */ + private Integer sendFailureType; + /** + * 发送失败的提示 + * + * 一般情况下,使用 {@link SmsSendFailureTypeEnum#getMsg()} + * 异常情况下,通过格式化 Exception 的提示存储 + */ + private String sendFailureMsg; /** - * 第三方唯一标识 + * 短信 API 发送的错误码 + * + * 由于第三方的错误码可能是字符串,所以使用 String 类型 */ - private String apiId; - + private String apiSendCode; /** - * 状态码 + * 短信 API 发送的提示 */ - private String code; - + private String apiSendMsg; /** - * 提示 + * 短信 API 发送返回的唯一请求 ID + * + * 用于和短信 API 进行定位于排错 */ - private String message; + private String apiRequestId; + /** + * 短信 API 发送返回的序号 + * + * 用于和短信 API 平台的发送记录关联 + */ + private String apiSerialNo; - public static SmsResult failResult(String message) { - SmsResult resultBody = new SmsResult(); - resultBody.setSuccess(false); - resultBody.setMessage(message); - return resultBody; + private SmsResult() { } + + public static SmsResult success(SmsSendFailureTypeEnum sendFailureType, + String apiSendCode, String apiSendMsg, String apiRequestId, String apiSerialNo) { + return new SmsResult().setSuccess(true).setSendFailureType(sendFailureType.getType()).setSendFailureMsg(sendFailureType.getMsg()) + .setApiSendCode(apiSendCode).setApiSendMsg(apiSendMsg).setApiRequestId(apiRequestId).setApiSerialNo(apiSerialNo); + } + + public static SmsResult error(Throwable ex) { + return new SmsResult().setSuccess(false) + .setSendFailureType(SmsSendFailureTypeEnum.SMS_SEND_EXCEPTION.getType()) + .setSendFailureMsg(ExceptionUtil.getRootCauseMessage(ex)); + } + } diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java similarity index 57% rename from src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java rename to src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java index ff8e6e72f..2933a11a0 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClient.java @@ -1,28 +1,36 @@ -package cn.iocoder.dashboard.framework.sms.client; +package cn.iocoder.dashboard.framework.sms.core.client; -import cn.iocoder.dashboard.framework.sms.core.SmsBody; import cn.iocoder.dashboard.framework.sms.core.SmsResult; import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; import javax.servlet.ServletRequest; +import java.util.Map; /** - * 短信父接口 + * 短信客户端接口 * * @author zzf * @date 2021/1/25 14:14 */ public interface SmsClient { + /** + * 获得渠道编号 + * + * @return 渠道编号 + */ + Long getId(); + /** * 发送消息 * - * @param templateApiId 短信模板唯一标识 - * @param smsBody 消息内容 - * @param targets 发送对象列表 + * @param sendLogId 发送日志编号 + * @param mobile 手机号 + * @param apiTemplateId 短信 API 的模板编号 + * @param templateParams 短信模板参数 * @return 短信发送结果 */ - SmsResult send(String templateApiId, SmsBody smsBody, String targets); + SmsResult send(Long sendLogId, String mobile, String apiTemplateId, Map templateParams); // TODO FROM 芋艿 to ZZF:是不是可以改成意图更明确的解析返回结果,例如说 parseXXXX /** diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java new file mode 100644 index 000000000..f4bc87dd4 --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/SmsClientFactory.java @@ -0,0 +1,28 @@ +package cn.iocoder.dashboard.framework.sms.core.client; + +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; + +/** + * 短信客户端工厂接口 + * + * @author zzf + * @date 2021/1/28 14:01 + */ +public interface SmsClientFactory { + + /** + * 获得短信 Client + * + * @param channelId 渠道编号 + * @return 短信 Client + */ + SmsClient getSmsClient(Long channelId); + + /** + * 创建短信 Client + * + * @param properties 配置对象 + */ + void createOrUpdateSmsClient(SmsChannelProperties properties); + +} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java new file mode 100644 index 000000000..c8bd94e7a --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/AbstractSmsClient.java @@ -0,0 +1,89 @@ +package cn.iocoder.dashboard.framework.sms.core.client.impl; + +import cn.iocoder.dashboard.framework.sms.core.SmsResult; +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; +import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + +/** + * 短信客户端抽象类 + * + * @author zzf + * @date 2021/2/1 9:28 + */ +@Slf4j +public abstract class AbstractSmsClient implements SmsClient { + + /** + * 短信渠道配置 + */ + protected volatile SmsChannelProperties properties; + + /** + * 短信客户端有参构造函数 + * + * @param properties 短信配置 + */ + public AbstractSmsClient(SmsChannelProperties properties) { + this.properties = properties; + } + + /** + * 初始化 + */ + public final void init() { + doInit(); + log.info("[init][配置({}) 初始化完成]", properties); + } + + public final void refresh(SmsChannelProperties properties) { + // 判断是否更新 + if (!properties.equals(this.properties)) { + return; + } + log.info("[refresh][配置({})发生变化,重新初始化]", properties); + this.properties = properties; + // 初始化 + this.init(); + } + + /** + * 自定义初始化 + */ + protected abstract void doInit(); + + @Override + public Long getId() { + return properties.getId(); + } + + @Override + public final SmsResult send(Long sendLogId, String mobile, String apiTemplateId, Map templateParams) { + SmsResult result; + try { + result = doSend(sendLogId, mobile, apiTemplateId, templateParams); + } catch (Throwable ex) { + // 打印异常日志 + log.error("[send][发送短信异常,sendLogId({}) mobile({}) apiTemplateId({}) templateParams({})]", + sendLogId, mobile, apiTemplateId, templateParams, ex); + // 封装返回 + return SmsResult.error(ex); + } + return result; + } + + /** + * 发送消息 + * + * @param sendLogId 发送日志编号 + * @param mobile 手机号 + * @param apiTemplateId 短信 API 的模板编号 + * @param templateParams 短信模板参数 + * @return 短信发送结果 + */ + protected abstract SmsResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map templateParams) + throws Throwable; + +} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java new file mode 100644 index 000000000..ddfe2302f --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/SmsClientFactoryImpl.java @@ -0,0 +1,83 @@ +package cn.iocoder.dashboard.framework.sms.core.client.impl; + +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; +import cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun.AliyunSmsClient; +import cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian.YunpianSmsClient; +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; +import lombok.extern.slf4j.Slf4j; +import org.springframework.util.Assert; +import org.springframework.validation.annotation.Validated; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 短信客户端工厂接口 + * + * @author zzf + */ +@Validated +@Slf4j +public class SmsClientFactoryImpl implements SmsClientFactory { + + /** + * 短信客户端 Map + * key:渠道编号,使用 {@link SmsChannelProperties#getId()} + */ + private final Map clients = new ConcurrentHashMap<>(); + + @Override + public SmsClient getSmsClient(Long channelId) { + return clients.get(channelId); + } + + @Override + public void createOrUpdateSmsClient(SmsChannelProperties properties) { + AbstractSmsClient client = clients.get(properties.getId()); + if (client == null) { + client = this.createSmsClient(properties); + clients.put(client.getId(), client); + } else { + client.refresh(properties); + } + } + + private AbstractSmsClient createSmsClient(SmsChannelProperties properties) { + SmsChannelEnum channelEnum = SmsChannelEnum.getByCode(properties.getCode()); + Assert.notNull(channelEnum, String.format("渠道类型(%s) 为空", channelEnum)); + // 创建客户端 + switch (channelEnum) { + case ALIYUN: + return new AliyunSmsClient(properties); + case YUN_PIAN: + return new YunpianSmsClient(properties); + } + // 创建失败,错误日志 + 抛出异常 + log.error("[createSmsClient][配置({}) 找不到合适的客户端实现]", properties); + throw new IllegalArgumentException(String.format("配置(%s) 找不到合适的客户端实现", properties)); + } + +// /** +// * 从短信发送回调函数请求中获取用于唯一确定一条send_lod的apiId +// * +// * @param callbackRequest 短信发送回调函数请求 +// * @return 第三方平台短信唯一标识 +// */ +// public SmsResultDetail getSmsResultDetailFromCallbackQuery(ServletRequest callbackRequest) { +// for (Long channelId : clients.keySet()) { +// AbstractSmsClient smsClient = clients.get(channelId); +// try { +// SmsResultDetail smsSendResult = smsClient.smsSendCallbackHandle(callbackRequest); +// if (smsSendResult != null) { +// return smsSendResult; +// } +// } catch (Exception ignored) { +// } +// } +// throw new IllegalArgumentException("getSmsResultDetailFromCallbackQuery fail! don't match SmsClient by RequestParam: " +// + JsonUtils.toJsonString(callbackRequest.getParameterMap())); +// } + +} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/impl/ali/AliyunSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java similarity index 73% rename from src/main/java/cn/iocoder/dashboard/framework/sms/client/impl/ali/AliyunSmsClient.java rename to src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java index 11301ebf6..a2af47ffb 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/client/impl/ali/AliyunSmsClient.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/aliyun/AliyunSmsClient.java @@ -1,19 +1,15 @@ -package cn.iocoder.dashboard.framework.sms.client.impl.ali; +package cn.iocoder.dashboard.framework.sms.core.client.impl.aliyun; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; -import cn.iocoder.dashboard.framework.sms.core.SmsBody; import cn.iocoder.dashboard.framework.sms.core.SmsResult; import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; +import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; import cn.iocoder.dashboard.util.json.JsonUtils; import com.aliyuncs.DefaultAcsClient; import com.aliyuncs.IAcsClient; -import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; -import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; -import com.aliyuncs.http.MethodType; import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.IClientProfile; import com.fasterxml.jackson.core.type.TypeReference; @@ -28,7 +24,7 @@ import java.util.List; import java.util.Map; /** - * 阿里短信实现类 + * 阿里短信客户端的实现类 * * @author zzf * @date 2021/1/25 14:17 @@ -36,54 +32,54 @@ import java.util.Map; @Slf4j public class AliyunSmsClient extends AbstractSmsClient { - private static final String OK = "OK"; - private static final String PRODUCT = "Dystopi"; - private static final String DOMAIN = "dysmsapi.aliyuncs.com"; - private static final String ENDPOINT = "cn-hangzhou"; - private final IAcsClient acsClient; + private static final String OK = "OK"; /** - * 构造阿里云短信发送处理 - * - * @param channelVO 阿里云短信配置 + * 阿里云客户端 */ - public AliyunSmsClient(SmsChannelProperty channelVO) { - super(channelVO); + private volatile IAcsClient acsClient; - String accessKeyId = channelVO.getApiKey(); - String accessKeySecret = channelVO.getApiSecret(); + public AliyunSmsClient(SmsChannelProperties properties) { + super(properties); + } - IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, accessKeyId, accessKeySecret); + @Override + protected void doInit() { + IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, properties.getApiKey(), properties.getApiSecret()); DefaultProfile.addEndpoint(ENDPOINT, PRODUCT, DOMAIN); - acsClient = new DefaultAcsClient(profile); } @Override - public SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception { - SendSmsRequest request = new SendSmsRequest(); - request.setSysMethod(MethodType.POST); - request.setPhoneNumbers(targetPhone); - request.setSignName(channelVO.getApiSignatureId()); - request.setTemplateCode(templateApiId); - request.setTemplateParam(smsBody.getParamsStr()); - SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); - - boolean success = OK.equals(sendSmsResponse.getCode()); - if (!success) { - log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage()); - } - return new SmsResult() - .setSuccess(success) - .setMessage(sendSmsResponse.getMessage()) - .setCode(sendSmsResponse.getCode()) - .setApiId(sendSmsResponse.getBizId()); + protected SmsResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map templateParams) throws Exception { + return null; } +// @Override +// public SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception { +// SendSmsRequest request = new SendSmsRequest(); +// request.setSysMethod(MethodType.POST); +// request.setPhoneNumbers(targetPhone); +// request.setSignName(properties.getSignature()); +// request.setTemplateCode(templateApiId); +// request.setTemplateParam(smsBody.getParamsStr()); +// SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); +// +// boolean success = OK.equals(sendSmsResponse.getCode()); +// if (!success) { +// log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage()); +// } +// return new SmsResult() +// .setSuccess(success) +// .setMessage(sendSmsResponse.getMessage()) +// .setCode(sendSmsResponse.getCode()) +// .setApiId(sendSmsResponse.getBizId()); +// } + /** * [{ * "send_time" : "2017-08-30 00:00:00", @@ -131,8 +127,8 @@ public class AliyunSmsClient extends AbstractSmsClient { public Integer getSendStatus() { return ((Boolean) sendResultParamMap.get(CallbackField.SUCCESS)) - ? SysSmsSendStatusEnum.SEND_SUCCESS.getStatus() - : SysSmsSendStatusEnum.SEND_FAIL.getStatus(); + ? SysSmsSendStatusEnum.SUCCESS.getStatus() + : SysSmsSendStatusEnum.FAILURE.getStatus(); } public String getBizId() { diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/impl/yunpian/YunpianSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java similarity index 60% rename from src/main/java/cn/iocoder/dashboard/framework/sms/client/impl/yunpian/YunpianSmsClient.java rename to src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java index 1164439de..dfadd573a 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/client/impl/yunpian/YunpianSmsClient.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/client/impl/yunpian/YunpianSmsClient.java @@ -1,19 +1,20 @@ -package cn.iocoder.dashboard.framework.sms.client.impl.yunpian; +package cn.iocoder.dashboard.framework.sms.core.client.impl.yunpian; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.CharsetUtil; -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; -import cn.iocoder.dashboard.framework.sms.core.SmsBody; +import cn.hutool.core.util.URLUtil; import cn.iocoder.dashboard.framework.sms.core.SmsConstants; import cn.iocoder.dashboard.framework.sms.core.SmsResult; import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; +import cn.iocoder.dashboard.framework.sms.core.client.impl.AbstractSmsClient; +import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum; +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; import cn.iocoder.dashboard.util.json.JsonUtils; import com.fasterxml.jackson.core.type.TypeReference; import com.yunpian.sdk.YunpianClient; -import com.yunpian.sdk.constant.Code; import com.yunpian.sdk.constant.YunpianConstant; import com.yunpian.sdk.model.Result; import com.yunpian.sdk.model.SmsSingleSend; @@ -25,9 +26,10 @@ import java.net.URLEncoder; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.StringJoiner; /** - * 云片短信实现类 + * 云片短信客户端的实现类 * * @author zzf * @date 9:48 2021/3/5 @@ -35,71 +37,59 @@ import java.util.Map; @Slf4j public class YunpianSmsClient extends AbstractSmsClient { - private final YunpianClient client; + /** + * 云信短信客户端 + */ + private volatile YunpianClient client; private final TypeReference>> callbackType = new TypeReference>>() { }; - /** - * 构造云片短信发送处理 - * - * @param channelVO 阿里云短信配置 - */ - public YunpianSmsClient(SmsChannelProperty channelVO) { - super(channelVO); - client = new YunpianClient(channelVO.getApiKey()); + public YunpianSmsClient(SmsChannelProperties properties) { + super(properties); } @Override - public SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) { - Map paramMap = new HashMap<>(); - paramMap.put(YunpianConstant.APIKEY, getProperty().getApiKey()); - paramMap.put(YunpianConstant.MOBILE, String.join(SmsConstants.COMMA, targetPhone)); - paramMap.put(YunpianConstant.TEXT, formatContent(smsBody)); - paramMap.put(Helper.CALLBACK, getProperty().getCallbackUrl()); + public void doInit() { + client = new YunpianClient(properties.getApiKey()); + } - Result sendResult = client.sms().single_send(paramMap); - boolean success = sendResult.getCode().equals(Code.OK); + @Override + protected SmsResult doSend(Long sendLogId, String mobile, String apiTemplateId, Map templateParams) throws Throwable { + // 构建参数 + Map request = new HashMap<>(); + request.put(YunpianConstant.APIKEY, properties.getApiKey()); + request.put(YunpianConstant.MOBILE, mobile); + request.put(YunpianConstant.TPL_ID, apiTemplateId); + request.put(YunpianConstant.TPL_VALUE, formatTplValue(templateParams)); + request.put(YunpianConstant.UID, String.valueOf(sendLogId)); + request.put(Helper.CALLBACK, properties.getCallbackUrl()); - if (!success) { - log.debug("send fail[code={}, message={}]", sendResult.getCode(), sendResult.getDetail()); + // 执行发送 + Result sendResult = client.sms().tpl_single_send(request); + if (sendResult.getThrowable() != null) { + throw sendResult.getThrowable(); } - return new SmsResult() - .setSuccess(success) - .setMessage(sendResult.getDetail()) - .setCode(sendResult.getCode().toString()) - .setApiId(sendResult.getData().getSid().toString()); + // 解析结果 + SmsSingleSend data = sendResult.getData(); + return SmsResult.success(parseSendFailureType(sendResult), // 将 API 短信平台,解析成统一的错误码 + String.valueOf(data.getCode()), data.getMsg(), null, String.valueOf(data.getSid())); } - - /** - * 格式化短信内容,将参数注入到模板中 - * - * @param smsBody 短信信息 - * @return 格式化后的短信内容 - */ - private String formatContent(SmsBody smsBody) { - StringBuilder result = new StringBuilder(smsBody.getTemplateContent()); - smsBody.getParams().forEach((key, val) -> { - String param = parseParamToPlaceholder(key); - result.replace(result.indexOf(param), result.indexOf(param + param.length()), val); - }); - return result.toString(); + private static String formatTplValue(Map templateParams) { + if (CollUtil.isEmpty(templateParams)) { + return ""; + } + // 参考 https://www.yunpian.com/official/document/sms/zh_cn/introduction_demos_encode_sample 格式化 + StringJoiner joiner = new StringJoiner("&"); + templateParams.forEach((key, value) -> joiner.add(String.format("#%s#=%s", key, URLUtil.encode(String.valueOf(value))))); + return joiner.toString(); } - /** - * 将指定参数改成对应的占位字符 - *

- * 云片的是 #param# 的形式作为占位符 - * - * @param key 参数名 - * @return 对应的占位字符 - */ - private String parseParamToPlaceholder(String key) { - return SmsConstants.JING_HAO + key + SmsConstants.JING_HAO; + private static SmsSendFailureTypeEnum parseSendFailureType(Result sendResult) { + return SmsSendFailureTypeEnum.SMS_UNKNOWN; } - /** * 云片的比较复杂,又是加密又是套娃的 */ @@ -109,7 +99,6 @@ public class YunpianSmsClient extends AbstractSmsClient { return Helper.getSmsResultDetailByParam(map); } - /** * 从 request 中获取请求中传入的短信发送结果信息 * @@ -155,8 +144,8 @@ public class YunpianSmsClient extends AbstractSmsClient { private static int getSendStatus(Map map) { String reportStatus = map.get(REPORT_STATUS); return SmsConstants.SUCCESS.equals(reportStatus) - ? SysSmsSendStatusEnum.SEND_SUCCESS.getStatus() - : SysSmsSendStatusEnum.SEND_FAIL.getStatus(); + ? SysSmsSendStatusEnum.SUCCESS.getStatus() + : SysSmsSendStatusEnum.FAILURE.getStatus(); } public static SmsResultDetail getSmsResultDetailByParam(Map map) { diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsSendFailureTypeEnum.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsSendFailureTypeEnum.java index 572ee3908..82ce1e836 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsSendFailureTypeEnum.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsSendFailureTypeEnum.java @@ -13,14 +13,23 @@ import lombok.Getter; public enum SmsSendFailureTypeEnum { // ========== 模板相关(100 开头) ========== - SMS_TEMPLATE_DISABLE(100), // 短信模板被禁用 + SMS_CHANNEL_CLIENT_NOT_EXISTS(100, "短信渠道的客户端不存在"), - // ========== 其它相关 ========== + // ========== 模板相关(200 开头) ========== + SMS_TEMPLATE_DISABLE(200, "短信模板被禁用"), + + // ========== 其它相关(900 开头) ========== + SMS_SEND_EXCEPTION(900, "发送异常"), + SMS_UNKNOWN(999, "未知错误,需要解析") ; /** * 失败类型 */ private final int type; + /** + * 失败提示 + */ + private final String msg; } diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperties.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperties.java new file mode 100644 index 000000000..750f2e7b4 --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperties.java @@ -0,0 +1,52 @@ +package cn.iocoder.dashboard.framework.sms.core.property; + +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; +import lombok.Data; +import org.springframework.validation.annotation.Validated; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * 短信渠道配置类 + * + * @author zzf + * @date 2021/1/25 17:01 + */ +@Data +@Validated +public class SmsChannelProperties { + + /** + * 渠道编号 + */ + @NotNull(message = "短信渠道 ID 不能为空") + private Long id; + /** + * 短信签名 + */ + @NotEmpty(message = "短信签名不能为空") + private String signature; + /** + * 渠道编码 + * + * 枚举 {@link SmsChannelEnum} + */ + @NotEmpty(message = "渠道编码不能为空") + private String code; + /** + * 短信 API 的账号 + */ + @NotEmpty(message = "短信 API 的账号不能为空") + private String apiKey; + /** + * 短信 API 的秘钥 + */ + @NotEmpty(message = "短信 API 的秘钥不能为空") + private String apiSecret; + /** + * 短信发送回调 URL + */ + private String callbackUrl; + +} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperty.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperty.java deleted file mode 100644 index 1c7ff2305..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperty.java +++ /dev/null @@ -1,68 +0,0 @@ -package cn.iocoder.dashboard.framework.sms.core.property; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.NotNull; -import java.io.Serializable; -import java.util.List; - -/** - * 渠道(包含模板)信息VO类 - * - * @author zzf - * @date 2021/1/25 17:01 - */ -@Data -@EqualsAndHashCode -public class SmsChannelProperty implements Serializable { - - /** - * id - */ - @NotNull(message = "短信渠道ID不能为空") - private Long id; - - /** - * 编码(来自枚举类 阿里、华为、七牛等) - */ - @NotEmpty(message = "短信渠道编码不能为空") - private String code; - - /** - * 渠道账号id - */ - @NotEmpty(message = "渠道账号id不能为空") - private String apiKey; - - /** - * 渠道账号秘钥 - */ - @NotEmpty(message = "渠道账号秘钥不能为空") - private String apiSecret; - - /** - * 实际渠道签名唯一标识 - */ - @NotEmpty(message = "实际渠道签名唯一标识不能为空") - private String apiSignatureId; - - /** - * 签名值 - */ - @NotEmpty(message = "签名值不能为空") - private String signature; - - /** - * 是否拥有回调函数(0否 1是) - */ - @NotNull(message = "是否拥有回调函数不能为空") - private Integer hadCallback; - - /** - * 短信发送回调url - */ - private String callbackUrl; - -} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsTemplateProperty.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsTemplateProperty.java deleted file mode 100644 index ebd3a7a95..000000000 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsTemplateProperty.java +++ /dev/null @@ -1,47 +0,0 @@ -package cn.iocoder.dashboard.framework.sms.core.property; - -import lombok.Data; -import lombok.EqualsAndHashCode; - -import javax.validation.constraints.NotEmpty; - -/** - * 渠道模板VO类 - * - * @author zzf - * @date 2021/1/25 17:03 - */ -@Data -@EqualsAndHashCode -public class SmsTemplateProperty { - - /** - * 渠道id - */ - @NotEmpty(message = "短信渠道编码不能为空") - private Long channelId; - - /** - * 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板) - */ - private String bizCode; - - /** - * 编码 - */ - @NotEmpty(message = "短信模板编码不能为空") - private String code; - - /** - * 实际渠道模板唯一标识 - */ - @NotEmpty(message = "短信模板唯一标识不能为空") - private String apiTemplateId; - - /** - * 内容 - */ - @NotEmpty(message = "短信模板内容不能为空") - private String content; - -} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/req/SmsChannelPageReqVO.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/req/SmsChannelPageReqVO.java index 19e2987fe..9b941edf3 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/req/SmsChannelPageReqVO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/req/SmsChannelPageReqVO.java @@ -15,9 +15,6 @@ import lombok.NoArgsConstructor; @EqualsAndHashCode(callSuper = true) public class SmsChannelPageReqVO extends PageParam { - @ApiModelProperty(value = "渠道名", example = "阿里", notes = "模糊匹配") - private String name; - @ApiModelProperty(value = "签名值", example = "源码", notes = "模糊匹配") private String signature; diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java b/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java index 833c1919c..43d72b471 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java @@ -1,16 +1,13 @@ package cn.iocoder.dashboard.modules.system.convert.sms; import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; -import cn.iocoder.dashboard.common.pojo.PageResult; -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; import cn.iocoder.dashboard.modules.system.controller.user.vo.user.SysUserUpdateReqVO; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; -import com.baomidou.mybatisplus.core.metadata.IPage; import org.mapstruct.Mapper; -import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; import java.util.List; @@ -20,9 +17,6 @@ public interface SmsChannelConvert { SmsChannelConvert INSTANCE = Mappers.getMapper(SmsChannelConvert.class); - @Mapping(source = "records", target = "list") - PageResult convertPage(IPage page); - SysSmsChannelDO convert(SmsChannelCreateReqVO bean); SysSmsChannelDO convert(SysUserUpdateReqVO bean); @@ -31,9 +25,8 @@ public interface SmsChannelConvert { List convert(List bean); - List convertProperty(List list); - - List convertProperties(List list); + List convertProperty(List list); + List convertList(List list); } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsTemplateConvert.java b/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsTemplateConvert.java index 2ff680028..788fc8e19 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsTemplateConvert.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsTemplateConvert.java @@ -1,7 +1,6 @@ package cn.iocoder.dashboard.modules.system.convert.sms; import cn.iocoder.dashboard.common.pojo.PageResult; -import cn.iocoder.dashboard.framework.sms.core.property.SmsTemplateProperty; import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; @@ -24,6 +23,4 @@ public interface SmsTemplateConvert { SmsTemplateVO convert(SysSmsTemplateDO bean); - List convertProperty(List bean); - } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java index 48acf346f..4bca7e517 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsChannelDO.java @@ -1,70 +1,58 @@ package cn.iocoder.dashboard.modules.system.dal.dataobject.sms; +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; import cn.iocoder.dashboard.framework.mybatis.core.dataobject.BaseDO; +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; - /** * 短信渠道 * * @author zzf * @since 2021-01-25 */ +@TableName(value = "sms_channel", autoResultMap = true) @Data @EqualsAndHashCode(callSuper = true) -@TableName(value = "sms_channel", autoResultMap = true) public class SysSmsChannelDO extends BaseDO { /** - * 自增编号 + * 渠道编号 */ private Long id; - /** - * 编码(来自枚举类 阿里、华为、七牛等) - */ - private String code; - - /** - * 短信发送回调url - */ - private String callback_url; - - /** - * 渠道账号id - */ - private String apiKey; - - /** - * 渠道账号秘钥 - */ - private String apiSecret; - - /** - * 实际渠道签名唯一标识 - */ - private String apiSignatureId; - - /** - * 名称 - */ - private String name; - - /** - * 签名值 + * 短信签名 */ private String signature; - + /** + * 渠道编码 + * + * 枚举 {@link SmsChannelEnum} + */ + private String code; + /** + * 启用状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; /** * 备注 */ private String remark; - /** - * 启用状态(0正常 1停用) + * 短信 API 的账号 */ - private Integer status; + private String apiKey; + /** + * 短信 API 的秘钥 + */ + private String apiSecret; + /** + * 短信发送回调 URL + */ + private String callbackUrl; } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDO.java index 7147b8d7a..53e725634 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDO.java @@ -105,16 +105,23 @@ public class SysSmsSendLogDO extends BaseDO { * 枚举 {@link SysSmsSendStatusEnum} */ private Integer sendStatus; + /** + * 时间发送时间 + */ + private Date sendTime; /** * 发送失败的类型 * - * 枚举 {@link SmsSendFailureTypeEnum} + * 枚举 {@link SmsSendFailureTypeEnum#getType()} */ private Integer sendFailureType; /** - * 发送成功时间 + * 发送失败的提示 + * + * 一般情况下,使用 {@link SmsSendFailureTypeEnum#getMsg()} + * 异常情况下,通过格式化 Exception 的提示存储 */ - private Date sendTime; + private String sendFailureMsg; /** * 短信 API 发送失败的类型 * diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDOX.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDOX.java deleted file mode 100644 index 31d491d91..000000000 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/dataobject/sms/SysSmsSendLogDOX.java +++ /dev/null @@ -1,66 +0,0 @@ -package cn.iocoder.dashboard.modules.system.dal.dataobject.sms; - -import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.experimental.Accessors; - -import java.io.Serializable; -import java.util.Date; - -/** - * 短信日志 - * - * @author zzf - * @since 2021-01-25 - */ -@Data -@EqualsAndHashCode -@Accessors(chain = true) -@TableName(value = "sms_send_log", autoResultMap = true) -public class SysSmsSendLogDOX implements Serializable { - - /** - * 自增编号 - */ - private Long id; - - /** - * 短信渠道编码(来自枚举类) - */ - private String channelCode; - - /** - * 短信渠道id - */ - private Long channelId; - - /** - * 模板id - */ - private String templateCode; - - /** - * 手机号 - */ - private String phone; - - /** - * 备注 - */ - private String remark; - - /** - * 发送状态 - * - * @see SysSmsSendStatusEnum - */ - private Integer sendStatus; - - /** - * 发送时间 - */ - private Date sendTime; - -} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsChannelMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsChannelMapper.java index e601ebc5c..e6371e9c9 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsChannelMapper.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsChannelMapper.java @@ -1,31 +1,27 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.sms; import cn.hutool.core.util.StrUtil; -import cn.iocoder.dashboard.common.enums.CommonStatusEnum; -import cn.iocoder.dashboard.framework.mybatis.core.util.MyBatisUtils; +import cn.iocoder.dashboard.common.pojo.PageResult; +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.ibatis.annotations.Mapper; import java.util.List; @Mapper -public interface SysSmsChannelMapper extends BaseMapper { +public interface SysSmsChannelMapper extends BaseMapperX { - default IPage selectChannelPage(SmsChannelPageReqVO reqVO) { - return selectPage(MyBatisUtils.buildPage(reqVO), new LambdaQueryWrapper() - .like(StrUtil.isNotBlank(reqVO.getName()), SysSmsChannelDO::getName, reqVO.getName()) - .like(StrUtil.isNotBlank(reqVO.getSignature()), SysSmsChannelDO::getName, reqVO.getSignature()) - ); + default PageResult selectChannelPage(SmsChannelPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapper() + .like(StrUtil.isNotBlank(reqVO.getSignature()), SysSmsChannelDO::getSignature, reqVO.getSignature())); } - default List selectEnabledList() { + default List selectListByStatus(Integer status) { return selectList(new LambdaQueryWrapper() - .eq(SysSmsChannelDO::getStatus, CommonStatusEnum.ENABLE.getStatus()) - .orderByAsc(SysSmsChannelDO::getId) - ); + .eq(SysSmsChannelDO::getStatus, status) + .orderByAsc(SysSmsChannelDO::getId)); } + } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsQueryLogMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsQueryLogMapper.java deleted file mode 100644 index d1cf63efa..000000000 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsQueryLogMapper.java +++ /dev/null @@ -1,34 +0,0 @@ -package cn.iocoder.dashboard.modules.system.dal.mysql.sms; - -import cn.iocoder.dashboard.common.enums.DefaultBitFieldEnum; -import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO; -import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface SysSmsQueryLogMapper extends BaseMapper { - - /** - * 查询还没有获取发送结果的短信请求信息 - */ - default List selectNoResultQueryLogList() { - return this.selectList(new LambdaQueryWrapper() - .eq(SysSmsSendLogDO::getSendStatus, SysSmsSendStatusEnum.QUERY_SUCCESS) - .eq(SysSmsSendLogDO::getGotResult, DefaultBitFieldEnum.NO) - ); - } - - - /** - * 根据APIId修改对象 - */ - default boolean updateByApiId(SysSmsSendLogDO queryLogDO, String apiId) { - return update(queryLogDO, new LambdaQueryWrapper() - .eq(SysSmsSendLogDO::getApiId, apiId) - ) > 0; - } -} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsSendLogMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsSendLogMapper.java index 3a5c7fbb8..74d713d48 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsSendLogMapper.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/sms/SysSmsSendLogMapper.java @@ -1,10 +1,9 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.sms; +import cn.iocoder.dashboard.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; @Mapper -public interface SysSmsSendLogMapper extends BaseMapper { - +public interface SysSmsSendLogMapper extends BaseMapperX { } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java b/src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java index 63c4bc59e..1e63ca662 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java @@ -1,17 +1,9 @@ package cn.iocoder.dashboard.modules.system.mq.consumer.sms; import cn.iocoder.dashboard.framework.redis.core.stream.AbstractStreamMessageListener; -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; -import cn.iocoder.dashboard.framework.sms.core.SmsBody; -import cn.iocoder.dashboard.framework.sms.core.SmsResult; import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; -import cn.iocoder.dashboard.util.json.JsonUtils; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.redis.connection.stream.ObjectRecord; -import org.springframework.data.redis.stream.StreamListener; import org.springframework.stereotype.Component; import javax.annotation.Resource; @@ -26,24 +18,9 @@ import javax.annotation.Resource; @Slf4j public class SmsSendConsumer extends AbstractStreamMessageListener { - @Resource - private SysSmsChannelService smsChannelService; - - @Resource - private SysSmsQueryLogService smsQueryLogService; - @Resource private SysSmsService smsService; - @Override - public void onMessage(ObjectRecord record) { - AbstractSmsClient smsClient = smsChannelService.getSmsClient(body.getTemplateCode()); - String templateApiId = smsChannelService.getSmsTemplateApiIdByCode(body.getTemplateCode()); - - SmsResult result = smsClient.send(templateApiId, body, message.getTargetPhone()); - smsQueryLogService.afterSendLog(body.getSmsLogId(), result); - } - @Override public void onMessage(SysSmsSendMessage message) { log.info("[onMessage][消息内容({})]", message); diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java index 5224adb9a..e720e363f 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java @@ -1,8 +1,6 @@ package cn.iocoder.dashboard.modules.system.service.sms; import cn.iocoder.dashboard.common.pojo.PageResult; -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; @@ -46,26 +44,4 @@ public interface SysSmsChannelService { */ List getSmsChannelEnums(); - /** - * 根据短信模板编码获取短信客户端 - * - * @param templateCode 短信模板编码 - * @return 短信客户端 - */ - AbstractSmsClient getSmsClient(String templateCode); - - /** - * 根据短信模板编码获取模板唯一标识 - * - * @param templateCode 短信模板编码 - * @return 短信客户端 - */ - String getSmsTemplateApiIdByCode(String templateCode); - - /** - * 查询渠道(包含名下模块)信息集合 - * - * @return 渠道(包含名下模块)信息集合 - */ - List listSmsChannelAllEnabledInfo(); } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsQueryLogService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsQueryLogService.java deleted file mode 100644 index 858bf977a..000000000 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsQueryLogService.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.dashboard.modules.system.service.sms; - -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; -import cn.iocoder.dashboard.framework.sms.core.SmsBody; -import cn.iocoder.dashboard.framework.sms.core.SmsResult; -import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; - -/** - * 短信请求日志服务接口 - * - * @author zzf - * @date 2021/1/25 9:24 - */ -public interface SysSmsQueryLogService { - - /** - * 发送短信前的日志处理 - * - * @param smsBody 短信内容 - * @param targetPhone 发送对象手机号 - * @param client 短信客户端 - * @return 生成的日志id - */ - void beforeSendLog(SmsBody smsBody, String targetPhone, AbstractSmsClient client); - - /** - * 发送消息后的日志处理 - * - * @param logId 日志id - * @param result 消息结果 - */ - void afterSendLog(Long logId, SmsResult result); - - void updateSendLogByResultDetail(SmsResultDetail smsResultDetail); -} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java index baf76b7a0..29118cb8c 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java @@ -1,5 +1,6 @@ package cn.iocoder.dashboard.modules.system.service.sms; +import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; import java.util.Map; @@ -12,17 +13,38 @@ import java.util.Map; */ public interface SysSmsSendLogService { + /** + * 创建发送日志 + * + * @param mobile 手机号 + * @param userId 用户编号 + * @param userType 用户类型 + * @param template 短信模板 + * @param templateContent 短信内容 + * @param templateParams 短信参数 + * @return + */ Long createSmsSendLog(String mobile, Long userId, Integer userType, SysSmsTemplateDO template, String templateContent, Map templateParams); /** - * 更新发送日志为失败 + * 更新发送日志的结果 * - * @param id 发送日志编号 - * @param sendFailureType 失败类型 + * @param id 日志编号 + * @param success 是否成功 + * @param sendFailureType 发送失败的类型 + * @param sendFailureMsg 发送失败的提示 + * @param apiSendFailureType 短信 API 发送失败的类型 + * @param apiSendFailureMsg 短信 API 发送失败的提示 + * @param apiRequestId 短信 API 发送返回的唯一请求 ID + * @param apiSerialNo 短信 API 发送返回的序号 */ - void updateSmsSendLogFailure(Long id, Integer sendFailureType); + void updateSmsSendLogResult(Long id, Boolean success, Integer sendFailureType, String sendFailureMsg, + String apiSendFailureType, String apiSendFailureMsg, String apiRequestId, String apiSerialNo); - void getAndSaveSmsSendLog(); + default void updateSmsSendLogFailure(Long id, SmsSendFailureTypeEnum sendFailureType) { + updateSmsSendLogResult(id, false, sendFailureType.getType(), sendFailureType.getMsg(), + null, null, null, null); + } } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsChannelServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsChannelServiceImpl.java index 6026320f9..930a7db9c 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsChannelServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsChannelServiceImpl.java @@ -1,32 +1,24 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl; -import cn.hutool.core.util.ObjectUtil; +import cn.iocoder.dashboard.common.enums.CommonStatusEnum; import cn.iocoder.dashboard.common.pojo.PageResult; -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; -import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory; +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; -import cn.iocoder.dashboard.framework.sms.core.property.SmsTemplateProperty; -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperties; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; import cn.iocoder.dashboard.modules.system.convert.sms.SmsChannelConvert; -import cn.iocoder.dashboard.modules.system.convert.sms.SmsTemplateConvert; -import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsChannelMapper; -import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsTemplateMapper; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsChannelDO; -import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; +import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsChannelMapper; +import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsTemplateMapper; import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.Resource; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; /** * 短信渠道Service实现类 @@ -37,10 +29,8 @@ import java.util.concurrent.ConcurrentHashMap; @Service public class SysSmsChannelServiceImpl implements SysSmsChannelService { - private final Map templateCode2ChannelIdMap = new ConcurrentHashMap<>(32); - @Resource - private SmsClientFactory clientFactory; + private SmsClientFactory smsClientFactory; @Resource private SysSmsChannelMapper channelMapper; @@ -48,30 +38,19 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService { @Resource private SysSmsTemplateMapper templateMapper; - - @PostConstruct @Override + @PostConstruct public void initSmsClientAndCacheSmsTemplate() { // 查询有效渠道信息 - List channelDOList = channelMapper.selectEnabledList(); - List propertyList = SmsChannelConvert.INSTANCE.convertProperties(channelDOList); - - // 遍历渠道生成client、获取模板并缓存 - propertyList.forEach(channelProperty -> { - List templateDOList = templateMapper.selectListByChannelId(channelProperty.getId()); - if (ObjectUtil.isNotEmpty(templateDOList)) { - Long clientId = clientFactory.createClient(channelProperty); - templateDOList.forEach(template -> templateCode2ChannelIdMap.put(template.getCode(), clientId)); - - List templatePropertyList = SmsTemplateConvert.INSTANCE.convertProperty(templateDOList); - clientFactory.addOrUpdateTemplateCache(templatePropertyList); - } - }); + List channelDOList = channelMapper.selectListByStatus(CommonStatusEnum.ENABLE.getStatus()); + // 创建渠道 Client + List propertiesList = SmsChannelConvert.INSTANCE.convertList(channelDOList); + propertiesList.forEach(properties -> smsClientFactory.createOrUpdateSmsClient(properties)); } @Override public PageResult pageSmsChannels(SmsChannelPageReqVO reqVO) { - return SmsChannelConvert.INSTANCE.convertPage(channelMapper.selectChannelPage(reqVO)); + return channelMapper.selectChannelPage(reqVO); } @Override @@ -86,30 +65,20 @@ public class SysSmsChannelServiceImpl implements SysSmsChannelService { return SmsChannelConvert.INSTANCE.convertEnum(Arrays.asList(SmsChannelEnum.values())); } - @Override - public AbstractSmsClient getSmsClient(String templateCode) { - return clientFactory.getClient(templateCode2ChannelIdMap.get(templateCode)); - } - - @Override - public String getSmsTemplateApiIdByCode(String templateCode) { - return clientFactory.getTemplateApiIdByCode(templateCode); - } - - @Override - public List listSmsChannelAllEnabledInfo() { - List channelDOList = channelMapper.selectEnabledList(); - if (ObjectUtil.isNull(channelDOList)) { - return null; - } - List channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList); - channelAllVOList.forEach(smsChannelDO -> { - List templateDOList = templateMapper.selectListByChannelId(smsChannelDO.getId()); - if (ObjectUtil.isNull(templateDOList)) { - templateDOList = new ArrayList<>(); - } - smsChannelDO.setTemplateList(SmsTemplateConvert.INSTANCE.convert(templateDOList)); - }); - return channelAllVOList; - } +// @Override +// public List listSmsChannelAllEnabledInfo() { +// List channelDOList = channelMapper.selectListByStatus(); +// if (ObjectUtil.isNull(channelDOList)) { +// return null; +// } +// List channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList); +// channelAllVOList.forEach(smsChannelDO -> { +// List templateDOList = templateMapper.selectListByChannelId(smsChannelDO.getId()); +// if (ObjectUtil.isNull(templateDOList)) { +// templateDOList = new ArrayList<>(); +// } +// smsChannelDO.setTemplateList(SmsTemplateConvert.INSTANCE.convert(templateDOList)); +// }); +// return channelAllVOList; +// } } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsQueryLogServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsQueryLogServiceImpl.java deleted file mode 100644 index 6aefcbb65..000000000 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsQueryLogServiceImpl.java +++ /dev/null @@ -1,63 +0,0 @@ -package cn.iocoder.dashboard.modules.system.service.sms.impl; - -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; -import cn.iocoder.dashboard.framework.sms.core.SmsBody; -import cn.iocoder.dashboard.framework.sms.core.SmsResult; -import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; -import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsQueryLogMapper; -import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO; -import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; - -/** - * 短信请求日志服务实现类 - * - * @author zzf - * @date 13:50 2021/3/2 - */ -@Service -public class SysSmsQueryLogServiceImpl implements SysSmsQueryLogService { - - @Resource - private SysSmsQueryLogMapper logMapper; - - @Override - public void beforeSendLog(SmsBody smsBody, String targetPhone, AbstractSmsClient client) { - SysSmsSendLogDO smsLog = new SysSmsSendLogDO(); - SmsChannelProperty property = client.getProperty(); - - smsLog.setChannelCode(property.getCode()) - .setChannelId(property.getId()) - .setTemplateCode(smsBody.getTemplateCode()) - .setPhone(targetPhone) - .setContent(smsBody.getParams().toString()); - - smsLog.setSendStatus(SysSmsSendStatusEnum.ASYNC.getStatus()); - logMapper.insert(smsLog); - smsBody.setSmsLogId(smsLog.getId()); - } - - @Override - public void afterSendLog(Long logId, SmsResult result) { - SysSmsSendLogDO smsLog = new SysSmsSendLogDO(); - smsLog.setId(logId); - smsLog.setApiId(result.getApiId()); - smsLog.setSendStatus(SysSmsSendStatusEnum.QUERY_FAIL.getStatus()); - smsLog.setRemark(result.getCode() + ": " + result.getMessage()); - logMapper.updateById(smsLog); - } - - @Override - public void updateSendLogByResultDetail(SmsResultDetail smsResultDetail) { - SysSmsSendLogDO queryLogDO = new SysSmsSendLogDO(); - queryLogDO.setSendStatus(smsResultDetail.getSendStatus()); - queryLogDO.setSendTime(smsResultDetail.getSendTime()); - queryLogDO.setRemark(smsResultDetail.getMessage()); - logMapper.updateByApiId(queryLogDO, smsResultDetail.getApiId()); - } - -} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java index cfceda947..b57912bd4 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java @@ -1,22 +1,17 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl; -import cn.hutool.core.collection.CollectionUtil; -import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDO; -import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsSendLogDOX; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; -import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsQueryLogMapper; import cn.iocoder.dashboard.modules.system.dal.mysql.sms.SysSmsSendLogMapper; import cn.iocoder.dashboard.modules.system.enums.sms.SysSmsSendStatusEnum; -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; import cn.iocoder.dashboard.modules.system.service.sms.SysSmsSendLogService; import lombok.extern.slf4j.Slf4j; -import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import javax.annotation.Resource; -import java.util.List; +import java.util.Date; import java.util.Map; +import java.util.Objects; /** * 短信发送日志服务实现类 @@ -28,20 +23,9 @@ import java.util.Map; @Service public class SysSmsSendLogServiceImpl implements SysSmsSendLogService { - @Resource - private SysSmsQueryLogMapper smsQueryLogMapper; - @Resource private SysSmsSendLogMapper smsSendLogMapper; - @Resource - private SysSmsChannelService smsChannelService; - - /** - * 定时执行 {@link #getSmsSendResultJob()} 的周期 - */ - private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L; - @Override public Long createSmsSendLog(String mobile, Long userId, Integer userType, SysSmsTemplateDO template, String templateContent, Map templateParams) { @@ -61,71 +45,12 @@ public class SysSmsSendLogServiceImpl implements SysSmsSendLogService { } @Override - public void updateSmsSendLogFailure(Long id, Integer sendFailureType) { - smsSendLogMapper.updateById(new SysSmsSendLogDO().setId(id).setSendFailureType(sendFailureType)); + public void updateSmsSendLogResult(Long id, Boolean success, Integer sendFailureType, String sendFailureMsg, + String apiSendFailureType, String apiSendFailureMsg, String apiRequestId, String apiSerialNo) { + SysSmsSendStatusEnum sendStatus = Objects.equals(success, true) ? SysSmsSendStatusEnum.SUCCESS : SysSmsSendStatusEnum.FAILURE; + smsSendLogMapper.updateById(new SysSmsSendLogDO().setId(id).setSendStatus(sendStatus.getStatus()).setSendTime(new Date()) + .setSendFailureType(sendFailureType).setSendFailureMsg(sendFailureMsg) + .setApiSendFailureType(apiSendFailureType).setApiSendFailureMsg(apiSendFailureMsg).setApiRequestId(apiRequestId).setApiSerialNo(apiSerialNo)); } - @Override - public void getAndSaveSmsSendLog() { - - List noResultQueryLogList = smsQueryLogMapper.selectNoResultQueryLogList(); - - if (CollectionUtil.isEmpty(noResultQueryLogList)) { - return; - } - //用于添加的发送日志对象 - SysSmsSendLogDOX insertSendLog = new SysSmsSendLogDOX(); - //用于修改状态的请求日志对象 - SysSmsSendLogDO updateQueryLog = new SysSmsSendLogDO(); - - noResultQueryLogList.forEach(queryLog -> { - AbstractSmsClient smsClient = smsChannelService.getSmsClient(queryLog.getTemplateCode()); - - updateQueryLog.setId(queryLog.getId()); - - // 只处理实现了获取发送结果方法的短信客户端,理论上这里都是满足条件的,以防万一加个判断。 - /*if (smsClient instanceof NeedQuerySendResultSmsClient) { - //初始化点字段值 - queryLog2SendLong(insertSendLog, queryLog); - - NeedQuerySendResultSmsClient querySendResultSmsClient = (NeedQuerySendResultSmsClient) smsClient; - try { - List smsSendResult = querySendResultSmsClient.getSmsSendResult(queryLog.getRemark()); - smsSendResult.forEach(resultDetail -> { - insertSendLog.setPhone(resultDetail.getPhone()); - insertSendLog.setSendStatus(resultDetail.getSendStatus()); - insertSendLog.setSendTime(resultDetail.getSendTime()); - insertSendLog.setRemark(resultDetail.getMessage()); - smsSendLogMapper.insert(insertSendLog); - }); - } catch (Exception e) { - //exception handle - log.error("query send result fail, exception: " + e.getMessage()); - - updateQueryLog.setSendStatus(SmsSendStatusEnum.QUERY_SEND_FAIL.getStatus()); - updateQueryLog.setRemark(e.getMessage()); - smsQueryLogMapper.updateById(updateQueryLog); - return; - } - } else { - //理论上这里都是满足条件的,以防万一加个判断。 - updateQueryLog.setSendStatus(SmsSendStatusEnum.QUERY_SEND_FAIL.getStatus()); - smsQueryLogMapper.updateById(updateQueryLog); - }*/ - updateQueryLog.setSendStatus(SysSmsSendStatusEnum.SEND_SUCCESS.getStatus()); - updateQueryLog.setRemark(String.format("日志(id = %s)对应的客户端没有继承NeedQuerySendResultSmsClient, 不能获取短信结果。", queryLog.getId())); - smsQueryLogMapper.updateById(updateQueryLog); - }); - } - - private void queryLog2SendLong(SysSmsSendLogDOX insertSendLog, SysSmsSendLogDO queryLog) { - insertSendLog.setChannelCode(queryLog.getChannelCode()); - insertSendLog.setChannelId(queryLog.getChannelId()); - insertSendLog.setTemplateCode(queryLog.getTemplateCode()); - } - - @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) - public void getSmsSendResultJob() { - getAndSaveSmsSendLog(); - } } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java index b06e99cec..61a73ea71 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java @@ -4,15 +4,19 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.dashboard.common.enums.CommonStatusEnum; import cn.iocoder.dashboard.common.enums.UserTypeEnum; -import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory; -import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; +import cn.iocoder.dashboard.framework.sms.core.SmsResult; +import cn.iocoder.dashboard.framework.sms.core.client.SmsClient; +import cn.iocoder.dashboard.framework.sms.core.client.SmsClientFactory; import cn.iocoder.dashboard.framework.sms.core.enums.SmsSendFailureTypeEnum; import cn.iocoder.dashboard.modules.system.dal.dataobject.sms.SysSmsTemplateDO; import cn.iocoder.dashboard.modules.system.dal.dataobject.user.SysUserDO; import cn.iocoder.dashboard.modules.system.mq.message.sms.SysSmsSendMessage; import cn.iocoder.dashboard.modules.system.mq.producer.sms.SysSmsProducer; -import cn.iocoder.dashboard.modules.system.service.sms.*; +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsSendLogService; +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsTemplateService; import cn.iocoder.dashboard.modules.system.service.user.SysUserService; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -31,29 +35,21 @@ import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; * @date 2021/1/25 9:25 */ @Service +@Slf4j public class SysSmsServiceImpl implements SysSmsService { @Resource private SysSmsTemplateService smsTemplateService; @Resource private SysSmsSendLogService smsSendLogService; + @Resource + private SysSmsProducer smsProducer; + @Resource + private SmsClientFactory smsClientFactory; @Resource private SysUserService userService; - @Resource - private SysSmsChannelService channelService; - - - @Resource - private SysSmsQueryLogService logService; - - @Resource - private SysSmsProducer smsProducer; - - @Resource - private SmsClientFactory smsClientFactory; - @Override public void sendSingleSms(String mobile, Long userId, Integer userType, String templateCode, Map templateParams) { @@ -68,7 +64,7 @@ public class SysSmsServiceImpl implements SysSmsService { // 如果模板被禁用,则直接标记发送失败。也就说,不发短信,嘿嘿。 if (CommonStatusEnum.DISABLE.getStatus().equals(template.getStatus())) { - smsSendLogService.updateSmsSendLogFailure(sendLogId, SmsSendFailureTypeEnum.SMS_TEMPLATE_DISABLE.getType()); + smsSendLogService.updateSmsSendLogFailure(sendLogId, SmsSendFailureTypeEnum.SMS_TEMPLATE_DISABLE); return; } // 如果模板未禁用,发送 MQ 消息。目的是,异步化调用短信平台 @@ -126,19 +122,31 @@ public class SysSmsServiceImpl implements SysSmsService { SysUserDO user = userService.getUser(userId); return user != null ? user.getMobile() : null; } + // TODO 芋艿:支持 C 端用户 return null; } @Override public void doSendSms(SysSmsSendMessage message) { + // 获得渠道对应的 SmsClient 客户端 + SmsClient smsClient = smsClientFactory.getSmsClient(message.getChannelId()); + if (smsClient == null) { + log.error("[doSendSms][短信 message({}) 找不到对应的客户端]", message); + smsSendLogService.updateSmsSendLogFailure(message.getSendLogId(), SmsSendFailureTypeEnum.SMS_CHANNEL_CLIENT_NOT_EXISTS); + return; + } + // 发送短信 + SmsResult sendResult = smsClient.send(message.getSendLogId(), message.getMobile(), + message.getApiTemplateId(), message.getTemplateParams()); } @Override public Object smsSendCallbackHandle(ServletRequest request) { - SmsResultDetail smsResultDetail = smsClientFactory.getSmsResultDetailFromCallbackQuery(request); - logService.updateSendLogByResultDetail(smsResultDetail); - return smsResultDetail.getCallbackResponseBody(); +// SmsResultDetail smsResultDetail = smsClientFactory.getSmsResultDetailFromCallbackQuery(request); +// logService.updateSendLogByResultDetail(smsResultDetail); +// return smsResultDetail.getCallbackResponseBody(); + return null; } }