Commit 515fca5c41819e8187167c483e05d21cd4b4b029
1 parent
46ed64ba
短信提交 2021-03-28,增加发送日志
Showing
33 changed files
with
544 additions
and
930 deletions
pom.xml
| ... | ... | @@ -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
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 | } | ... | ... |