From 767cd90279e2247e704156f7c028b0b97560f663 Mon Sep 17 00:00:00 2001 From: zengzefeng <986510453@qq.com> Date: Mon, 8 Mar 2021 09:15:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=89=B9=E9=87=8F=E5=8F=91=E9=80=81=E5=8D=8A?= =?UTF-8?q?=E6=88=90=E5=93=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 16 ++- sql/sms.sql | 42 ++++-- .../common/enums/DefaultBitFieldEnum.java | 27 ++++ .../sms/client/AbstractSmsClient.java | 2 +- .../framework/sms/client/AliyunSmsClient.java | 37 +++-- .../sms/client/HadCallbackSmsClient.java | 25 ++++ .../client/NeedQuerySendResultSmsClient.java | 24 ++++ .../framework/sms/client/SmsClient.java | 5 + .../sms/client/YunpianSmsClient.java | 132 ++++++++++++++++++ .../dashboard/framework/sms/core/SmsBody.java | 6 +- .../framework/sms/core/SmsConstants.java | 18 +++ .../framework/sms/core/SmsResult.java | 13 +- .../framework/sms/core/SmsResultDetail.java | 4 +- .../sms/core/property/SmsChannelProperty.java | 11 ++ .../sms/SmsDefaultCallbackController.java | 27 ++++ .../mysql/dao/sms/SysSmsQueryLogMapper.java | 27 ++++ ...ogMapper.java => SysSmsSendLogMapper.java} | 4 +- .../mysql/dataobject/sms/SysSmsChannelDO.java | 10 ++ .../dataobject/sms/SysSmsQueryLogDO.java | 94 +++++++++++++ ...{SysSmsLogDO.java => SysSmsSendLogDO.java} | 27 ++-- .../system/enums/sms/SmsSendStatusEnum.java | 24 ++-- .../mq/consumer/sms/SmsSendConsumer.java | 18 ++- ...ervice.java => SysSmsQueryLogService.java} | 7 +- .../service/sms/SysSmsSendLogService.java | 13 ++ .../system/service/sms/SysSmsService.java | 69 ++------- .../sms/impl/SysSmsLogServiceImpl.java | 77 ---------- .../sms/impl/SysSmsQueryLogServiceImpl.java | 59 ++++++++ .../sms/impl/SysSmsSendLogServiceImpl.java | 109 +++++++++++++++ .../service/sms/impl/SysSmsServiceImpl.java | 28 ++-- .../dashboard/util/json/JsonUtils.java | 10 +- 30 files changed, 735 insertions(+), 230 deletions(-) create mode 100644 src/main/java/cn/iocoder/dashboard/common/enums/DefaultBitFieldEnum.java create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/client/HadCallbackSmsClient.java create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/client/NeedQuerySendResultSmsClient.java create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/client/YunpianSmsClient.java create mode 100644 src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsConstants.java create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsDefaultCallbackController.java create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsQueryLogMapper.java rename src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/{SysSmsLogMapper.java => SysSmsSendLogMapper.java} (71%) create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsQueryLogDO.java rename src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/{SysSmsLogDO.java => SysSmsSendLogDO.java} (66%) rename src/main/java/cn/iocoder/dashboard/modules/system/service/sms/{SysSmsLogService.java => SysSmsQueryLogService.java} (84%) create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java delete mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsLogServiceImpl.java create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsQueryLogServiceImpl.java create mode 100644 src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java diff --git a/pom.xml b/pom.xml index 163aa1941..52c3e85dc 100644 --- a/pom.xml +++ b/pom.xml @@ -183,12 +183,6 @@ ${jjwt.version} - - org.projectlombok - lombok - ${lombok.version} - - org.mapstruct mapstruct @@ -222,17 +216,25 @@ ${easyexcel.verion} + + + + com.yunpian.sdk + yunpian-java-sdk + 1.2.7 + + com.aliyun aliyun-java-sdk-core 4.5.18 - com.aliyun aliyun-java-sdk-dysmsapi 2.1.0 + diff --git a/sql/sms.sql b/sql/sms.sql index 82d854155..027052779 100644 --- a/sql/sms.sql +++ b/sql/sms.sql @@ -12,6 +12,8 @@ CREATE TABLE `sms_channel` `code` varchar(50) NOT NULL COMMENT '编码(来自枚举类 阿里、华为、七牛等)', `api_key` varchar(100) NOT NULL COMMENT '账号id', `api_secret` varchar(100) NOT NULL COMMENT '账号秘钥', + `had_callback` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否拥有回调函数', + `callback_url` varchar(100) NOT NULL default '' COMMENT '回调请求路径', `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识', `name` varchar(50) NOT NULL COMMENT '名称', `signature` varchar(50) NOT NULL COMMENT '签名值', @@ -60,23 +62,47 @@ CREATE TABLE `sms_template` AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='短信模板'; +-- ---------------------------- +-- Table structure for sms_query_log +-- ---------------------------- +DROP TABLE IF EXISTS `sms_query_log`; +CREATE TABLE `sms_query_log` +( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', + `api_id` varchar(100) NOT NULL COMMENT '第三方唯一标识', + `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)', + `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id', + `template_code` varchar(50) NOT NULL COMMENT '渠道编码', + `phones` varchar(2000) NOT NULL COMMENT '手机号(数组json字符串)', + `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', + `send_result_param` varchar(200) NOT NULL DEFAULT '' COMMENT '查询短信发送结果的参数', + `send_status` tinyint(1) NOT NULL DEFAULT 2 COMMENT '发送状态(0本地异步中 1发送请求失败 2发送请求成功)', + `got_result` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否获取发送结果', + `had_callback` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否拥有回调函数', + `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id`) USING BTREE +) ENGINE = InnoDB + AUTO_INCREMENT = 1 + DEFAULT CHARSET = utf8mb4 COMMENT ='短信请求日志'; + -- ---------------------------- -- Table structure for sms_log -- ---------------------------- -DROP TABLE IF EXISTS `sms_log`; -CREATE TABLE `sms_log` +DROP TABLE IF EXISTS `sms_send_log`; +CREATE TABLE `sms_send_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)', `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id', `template_code` varchar(50) NOT NULL COMMENT '渠道编码', - `phones` char(11) NOT NULL COMMENT '手机号(数组json字符串)', + `query_log_id` bigint(20) NOT NULL COMMENT '请求日志id', + `phone` char(11) NOT NULL COMMENT '手机号', `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', - `remark` varchar(200) DEFAULT NULL COMMENT '备注', - `send_status` tinyint(4) NOT NULL DEFAULT 2 COMMENT '发送状态(1异步推送中 2发送中 3失败 4成功)', - `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者', - `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `remark` varchar(200) DEFAULT NULL COMMENT '备注', + `success` tinyint(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', + `send_time` datetime DEFAULT NULL COMMENT '创建时间', PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 - DEFAULT CHARSET = utf8mb4 COMMENT ='短信日志'; + DEFAULT CHARSET = utf8mb4 COMMENT ='短信发送日志'; diff --git a/src/main/java/cn/iocoder/dashboard/common/enums/DefaultBitFieldEnum.java b/src/main/java/cn/iocoder/dashboard/common/enums/DefaultBitFieldEnum.java new file mode 100644 index 000000000..7738d40a2 --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/common/enums/DefaultBitFieldEnum.java @@ -0,0 +1,27 @@ +package cn.iocoder.dashboard.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 通用状态枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum DefaultBitFieldEnum { + + NO(0, "否"), + YES(1, "是"); + + /** + * 状态值 + */ + private final Integer val; + /** + * 状态名 + */ + private final String name; + +} 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 index d40d636e4..150b994bb 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java @@ -58,7 +58,7 @@ public abstract class AbstractSmsClient implements SmsClient { * @return 短信发送结果 * @throws Exception 调用发送失败,抛出异常 */ - public abstract SmsResult doSend(String templateApiId, SmsBody smsBody, Collection targets) throws Exception; + protected abstract SmsResult doSend(String templateApiId, SmsBody smsBody, Collection targets) throws Exception; protected void beforeSend(String templateApiId, SmsBody smsBody, Collection targets) throws Exception { } diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java index f88d6f291..fff2d162d 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.framework.sms.client; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.StrUtil; import cn.iocoder.dashboard.framework.sms.core.SmsBody; import cn.iocoder.dashboard.framework.sms.core.SmsResult; import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; @@ -14,11 +13,11 @@ import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest; import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; +import com.aliyuncs.exceptions.ClientException; import com.aliyuncs.http.MethodType; import com.aliyuncs.profile.DefaultProfile; import com.aliyuncs.profile.IClientProfile; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; import java.util.ArrayList; import java.util.Collection; @@ -31,7 +30,7 @@ import java.util.List; * @date 2021/1/25 14:17 */ @Slf4j -public class AliyunSmsClient extends AbstractSmsClient { +public class AliyunSmsClient extends AbstractSmsClient implements NeedQuerySendResultSmsClient { private static final String OK = "OK"; @@ -70,35 +69,43 @@ public class AliyunSmsClient extends AbstractSmsClient { request.setTemplateParam(smsBody.getParamsStr()); SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); - boolean result = OK.equals(sendSmsResponse.getCode()); - if (!result) { + boolean success = OK.equals(sendSmsResponse.getCode()); + if (!success) { log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage()); } - SmsResult resultBody = new SmsResult(); - resultBody.setSuccess(result); + return new SmsResult() + .setSuccess(success) + .setMessage(sendSmsResponse.getMessage()) + .setCode(sendSmsResponse.getCode()) + .setApiId(sendSmsResponse.getBizId()) + .setSendResultParam(sendSmsResponse.getBizId()); + } + + + @Override + public List getSmsSendResult(String param) throws ClientException { QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest(); - querySendDetailsRequest.setBizId(sendSmsResponse.getBizId()); - // TODO FROM 芋艿 to zzf:发送完之后,基于短信平台回调,去更新回执状态。短信发送是否成功,和最终用户收到,是两个维度。这块有困惑,可以微信,我给个截图哈。 + querySendDetailsRequest.setBizId(param); + // TODO FROM 芋艿 to zzf:发送完之后,基于短信平台回调,去更新回执状态。短信发送是否成功,和最终用户收到,是两个维度。这块有困惑,可以微信,我给个截图哈。 DONE QuerySendDetailsResponse acsResponse = acsClient.getAcsResponse(querySendDetailsRequest); List resultDetailList = new ArrayList<>(Integer.parseInt(acsResponse.getTotalCount())); acsResponse.getSmsSendDetailDTOs().forEach(s -> { SmsResultDetail resultDetail = new SmsResultDetail(); - resultDetail.setCreateTime(DateUtil.parseDateTime(s.getSendDate())); + resultDetail.setSendTime(DateUtil.parseDateTime(s.getSendDate())); resultDetail.setMessage(s.getContent()); resultDetail.setPhone(s.getPhoneNum()); - resultDetail.setStatus(statusConvert(s.getSendStatus())); + resultDetail.setSendStatus(statusConvert(s.getSendStatus())); resultDetailList.add(resultDetail); }); - resultBody.setResult(resultDetailList); - return resultBody; + return resultDetailList; } private int statusConvert(Long aliSendStatus) { if (aliSendStatus == 1L) { - return SmsSendStatusEnum.SUCCESS.getStatus(); + return SmsSendStatusEnum.SEND_SUCCESS.getStatus(); } if (aliSendStatus == 2L) { - return SmsSendStatusEnum.FAIL.getStatus(); + return SmsSendStatusEnum.SEND_FAIL.getStatus(); } return SmsSendStatusEnum.WAITING.getStatus(); } diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/HadCallbackSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/client/HadCallbackSmsClient.java new file mode 100644 index 000000000..1df4261d3 --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/client/HadCallbackSmsClient.java @@ -0,0 +1,25 @@ +package cn.iocoder.dashboard.framework.sms.client; + +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; + +import javax.servlet.ServletRequest; +import java.io.UnsupportedEncodingException; +import java.util.List; + +/** + * 需要发送请求获取短信发送结果的短信客户端 + * + * @author zzf + * @date 2021/3/4 17:20 + */ +public interface HadCallbackSmsClient { + + /** + * 获取短信发送结果 + * + * @param request 请求 + * @return 短信发送结果 + */ + List getSmsSendResult(ServletRequest request) throws Exception; + +} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/NeedQuerySendResultSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/client/NeedQuerySendResultSmsClient.java new file mode 100644 index 000000000..37352235f --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/client/NeedQuerySendResultSmsClient.java @@ -0,0 +1,24 @@ +package cn.iocoder.dashboard.framework.sms.client; + +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; +import com.aliyuncs.exceptions.ClientException; + +import java.util.List; + +/** + * 需要发送请求获取短信发送结果的短信客户端 + * + * @author zzf + * @date 2021/3/4 17:20 + */ +public interface NeedQuerySendResultSmsClient { + + /** + * 获取短信发送结果 + * + * @param param 参数 + * @return 短信发送结果 + */ + List getSmsSendResult(String param) throws Exception; + +} diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java index 94a6de933..803e3b16f 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java @@ -2,8 +2,10 @@ 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.SmsResultDetail; import java.util.Collection; +import java.util.List; /** * 短信父接口 @@ -23,4 +25,7 @@ public interface SmsClient { */ SmsResult send(String templateApiId, SmsBody smsBody, Collection targets); + + //List getSmsSendResult(String jsonObjectParam); + } \ No newline at end of file diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/client/YunpianSmsClient.java b/src/main/java/cn/iocoder/dashboard/framework/sms/client/YunpianSmsClient.java new file mode 100644 index 000000000..77cd45f06 --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/client/YunpianSmsClient.java @@ -0,0 +1,132 @@ +package cn.iocoder.dashboard.framework.sms.client; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.iocoder.dashboard.framework.sms.core.SmsBody; +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.modules.system.enums.sms.SmsSendStatusEnum; +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.SmsBatchSend; +import lombok.extern.slf4j.Slf4j; + +import javax.servlet.ServletRequest; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.*; + +/** + * 云片短信实现类 + * + * @author zzf + * @date 9:48 2021/3/5 + */ +@Slf4j +public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSmsClient { + + private final YunpianClient client; + + private final TypeReference>> callbackType = new TypeReference>>() { + }; + + /** + * 构造云片短信发送处理 + * + * @param channelVO 阿里云短信配置 + */ + public YunpianSmsClient(SmsChannelProperty channelVO) { + super(channelVO); + client = new YunpianClient(channelVO.getApiKey()); + } + + @Override + public SmsResult doSend(String templateApiId, SmsBody smsBody, Collection targets) { + Map paramMap = new HashMap<>(); + paramMap.put("apikey", getProperty().getApiKey()); + paramMap.put("mobile", String.join(SmsConstants.COMMA, targets)); + paramMap.put("text", formatContent(smsBody)); + paramMap.put("callback", getProperty().getCallbackUrl()); + + Result sendResult = client.sms().batch_send(paramMap); + boolean success = sendResult.getCode().equals(Code.OK); + + if (!success) { + log.debug("send fail[code={}, message={}]", sendResult.getCode(), sendResult.getDetail()); + } + return new SmsResult() + .setSuccess(success) + .setMessage(sendResult.getDetail()) + .setCode(sendResult.getCode().toString()) + .setApiId(sendResult.getData().getData().get(0).getSid().toString()); + } + + + /** + * 格式化短信内容,将参数注入到模板中 + * + * @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(); + } + + /** + * 将指定参数改成对应的占位字符 + *

+ * 云片的是 #param# 的形式作为占位符 + * + * @param key 参数名 + * @return 对应的占位字符 + */ + private String parseParamToPlaceholder(String key) { + return SmsConstants.JING_HAO + key + SmsConstants.JING_HAO; + } + + + @Override + public List getSmsSendResult(ServletRequest request) throws UnsupportedEncodingException { + List> stringStringMap = getSendResult(request); + List resultDetailList = new ArrayList<>(stringStringMap.size()); + stringStringMap.forEach(map -> { + SmsResultDetail detail = new SmsResultDetail(); + + detail.setPhone(map.get("mobile")); + detail.setMessage(map.get("error_msg")); + detail.setSendTime(DateUtil.parseTime(map.get("user_receive_time"))); + String reportStatus = map.get("report_status"); + detail.setSendStatus(reportStatus.equals(SmsConstants.SUCCESS) + ? SmsSendStatusEnum.SEND_SUCCESS.getStatus() + : SmsSendStatusEnum.SEND_FAIL.getStatus() + ); + resultDetailList.add(detail); + }); + return resultDetailList; + } + + /** + * 从 request 中获取请求中传入的短信发送结果信息 + * + * @param request 回调请求 + * @return 短信发送结果信息 + * @throws UnsupportedEncodingException 解码异常 + */ + private List> getSendResult(ServletRequest request) throws UnsupportedEncodingException { + Map parameterMap = request.getParameterMap(); + String[] smsStatuses = parameterMap.get(YunpianConstant.SMS_STATUS); + String encode = URLEncoder.encode(smsStatuses[0], CharsetUtil.UTF_8); + return JsonUtils.parseByType(encode, callbackType); + } +} 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 index 9b132431e..f82f0e142 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java @@ -4,7 +4,6 @@ import cn.iocoder.dashboard.util.json.JsonUtils; import lombok.Data; import java.util.Map; -import java.util.UUID; /** * 消息内容实体类 @@ -22,6 +21,11 @@ public class SmsBody { */ private String templateCode; + /** + * 模板编码 + */ + private String templateContent; + /** * 参数列表 */ 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 new file mode 100644 index 000000000..e519306f3 --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsConstants.java @@ -0,0 +1,18 @@ +package cn.iocoder.dashboard.framework.sms.core; + +/** + * 短信相关常量类 + * + * @author zzf + * @date 2021/3/5 10:42 + */ +public interface SmsConstants { + + String OK = "OK"; + + String JING_HAO = "#"; + + 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 46306322d..694005482 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,14 +1,15 @@ package cn.iocoder.dashboard.framework.sms.core; import lombok.Data; +import lombok.experimental.Accessors; import java.io.Serializable; -import java.util.List; /** * 消息内容实体类 */ @Data +@Accessors(chain = true) public class SmsResult implements Serializable { /** @@ -16,6 +17,11 @@ public class SmsResult implements Serializable { */ private Boolean success; + /** + * 第三方唯一标识 + */ + private String apiId; + /** * 状态码 */ @@ -27,10 +33,9 @@ public class SmsResult implements Serializable { private String message; /** - * 返回值 + * 用于查询发送结果的参数 */ - private List result; - + private String sendResultParam; public static SmsResult failResult(String message) { SmsResult resultBody = new SmsResult(); diff --git a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResultDetail.java b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResultDetail.java index 67de28938..fcca0a0be 100644 --- a/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResultDetail.java +++ b/src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResultDetail.java @@ -14,7 +14,7 @@ public class SmsResultDetail implements Serializable { /** * 短信发送状态 {@link cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum} */ - private Integer status; + private Integer sendStatus; /** * 接收手机号 @@ -29,5 +29,5 @@ public class SmsResultDetail implements Serializable { /** * 时间 */ - private Date createTime; + private Date sendTime; } 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 index 95f2a8ce7..1c7ff2305 100644 --- 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 @@ -54,4 +54,15 @@ public class SmsChannelProperty implements Serializable { @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/modules/system/controller/sms/SmsDefaultCallbackController.java b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsDefaultCallbackController.java new file mode 100644 index 000000000..94f0fc81d --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsDefaultCallbackController.java @@ -0,0 +1,27 @@ +package cn.iocoder.dashboard.modules.system.controller.sms; + +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.ServletRequest; + +/** + * 短信默认回调接口 + * + * @author zzf + * @date 2021/3/5 8:59 + */ +@RestController("/sms/callback") +public class SmsDefaultCallbackController { + + @Resource + private SysSmsService smsService; + + @RequestMapping("/sms-send") + public Object sendSmsCallback(ServletRequest request){ + return smsService.smsSendCallbackHandle(request); + } + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsQueryLogMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsQueryLogMapper.java new file mode 100644 index 000000000..7472f481e --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsQueryLogMapper.java @@ -0,0 +1,27 @@ +package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms; + +import cn.iocoder.dashboard.common.enums.DefaultBitFieldEnum; +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsQueryLogDO; +import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; +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 { + + /** + * 查询还没有获取发送结果的短信请求信息 + * + * @return + */ + default List selectNoResultQueryLogList() { + return this.selectList(new LambdaQueryWrapper() + .eq(SysSmsQueryLogDO::getSendStatus, SmsSendStatusEnum.QUERY_SUCCESS) + .eq(SysSmsQueryLogDO::getGotResult, DefaultBitFieldEnum.NO) + .eq(SysSmsQueryLogDO::getHadCallback, DefaultBitFieldEnum.NO) + ); + } +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsLogMapper.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsSendLogMapper.java similarity index 71% rename from src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsLogMapper.java rename to src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsSendLogMapper.java index cdb6df40b..eebb48fcc 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsLogMapper.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsSendLogMapper.java @@ -1,10 +1,10 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms; -import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsLogDO; +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsSendLogDO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; @Mapper -public interface SysSmsLogMapper extends BaseMapper { +public interface SysSmsSendLogMapper extends BaseMapper { } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsChannelDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsChannelDO.java index 3e9422e79..3f212dbda 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsChannelDO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsChannelDO.java @@ -27,6 +27,16 @@ public class SysSmsChannelDO extends BaseDO { */ private String code; + /** + * 是否拥有回答(0否 1是) + */ + private Integer had_callback; + + /** + * 短信发送回调url + */ + private String callback_url; + /** * 渠道账号id */ diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsQueryLogDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsQueryLogDO.java new file mode 100644 index 000000000..fce137d2c --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsQueryLogDO.java @@ -0,0 +1,94 @@ +package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 短信日志 + * + * @author zzf + * @since 2021-01-25 + */ +@Data +@EqualsAndHashCode +@Accessors(chain = true) +@TableName(value = "sms_query_log", autoResultMap = true) +public class SysSmsQueryLogDO implements Serializable { + + /** + * 自增编号 + */ + private Long id; + + /** + * 短信渠道编码(来自枚举类) + */ + private String channelCode; + + /** + * 短信渠道id + */ + private Long channelId; + + /** + * 模板id + */ + private String templateCode; + + /** + * 手机号 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List phones; + + /** + * 内容 + */ + private String content; + + /** + * 发送状态 + * + * @see cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum + */ + private Integer sendStatus; + + /** + * 是否获取过结果[0否 1是] + */ + private Integer gotResult; + + /** + * 是否拥有回调函数(0否 1是) + */ + private Integer hadCallback; + + /** + * 结果(对象json字符串) + */ + private String sendResultParam; + + /** + * 备注 + */ + private String remark; + + /** + * 创建人 + */ + private String createBy; + + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsLogDO.java b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsSendLogDO.java similarity index 66% rename from src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsLogDO.java rename to src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsSendLogDO.java index 97ec6c02e..f85416dba 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsLogDO.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsSendLogDO.java @@ -1,5 +1,6 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms; +import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; import com.baomidou.mybatisplus.annotation.TableName; import lombok.Data; import lombok.EqualsAndHashCode; @@ -17,8 +18,8 @@ import java.util.Date; @Data @EqualsAndHashCode @Accessors(chain = true) -@TableName(value = "sms_log", autoResultMap = true) -public class SysSmsLogDO implements Serializable { +@TableName(value = "sms_send_log", autoResultMap = true) +public class SysSmsSendLogDO implements Serializable { /** * 自增编号 @@ -41,14 +42,9 @@ public class SysSmsLogDO implements Serializable { private String templateCode; /** - * 手机号(数组json字符串) + * 手机号 */ - private String phones; - - /** - * 内容 - */ - private String content; + private String phone; /** * 备注 @@ -56,18 +52,15 @@ public class SysSmsLogDO implements Serializable { private String remark; /** - * 发送状态(1异步推送中 2发送中 3失败 4成功) + * 发送状态 + * + * @see SmsSendStatusEnum */ private Integer sendStatus; /** - * 创建者 + * 发送时间 */ - private String createBy; - - /** - * 创建时间 - */ - private Date createTime; + private Date sendTime; } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java b/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java index 846c70967..4e4121083 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java @@ -13,20 +13,26 @@ import lombok.Getter; @AllArgsConstructor public enum SmsSendStatusEnum { + //请求发送结果时失败 + QUERY_SEND_FAIL(-3), + + //短信发送失败 + SEND_FAIL(-2), + + //短信请求失败 + QUERY_FAIL(-1), + //异步转发中 - ASYNC(1), + ASYNC(0), - //发送中 - SENDING(2), + //请求成功 + QUERY_SUCCESS(1), - //失败 - FAIL(3), + //短信成功 + SEND_SUCCESS(2), //等待回执 - WAITING(4), - - //成功 - SUCCESS(5); + WAITING(3); private final int status; 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 f5f9109e2..f962bf372 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,10 +1,12 @@ package cn.iocoder.dashboard.modules.system.mq.consumer.sms; import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; import cn.iocoder.dashboard.framework.sms.core.SmsResult; import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage; import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage; -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @@ -20,12 +22,20 @@ import javax.annotation.Resource; public class SmsSendConsumer extends AbstractChannelMessageListener { @Resource - private SysSmsService sysSmsService; + private SysSmsChannelService smsChannelService; + + @Resource + private SysSmsQueryLogService smsQueryLogService; @Override public void onMessage(SmsSendMessage message) { - log.info("[onMessage][收到 发送短信 消息], content: " + message.toString()); - SmsResult send = sysSmsService.send(message.getSmsBody(), message.getTargetPhones()); + log.info("[onMessage][收到 发送短信 消息], content: " + message.toString()); + AbstractSmsClient smsClient = smsChannelService.getSmsClient(message.getSmsBody().getTemplateCode()); + String templateApiId = smsChannelService.getSmsTemplateApiIdByCode(message.getSmsBody().getTemplateCode()); + + SmsResult result = smsClient.send(templateApiId, message.getSmsBody(), message.getTargetPhones()); + + smsQueryLogService.afterSendLog(message.getSmsBody().getSmsLogId(), result); } } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsQueryLogService.java similarity index 84% rename from src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogService.java rename to src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsQueryLogService.java index 431737912..7312e2355 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogService.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsQueryLogService.java @@ -7,26 +7,25 @@ import cn.iocoder.dashboard.framework.sms.core.SmsResult; import java.util.List; /** - * 短信渠道Service接口 + * 短信请求日志服务接口 * * @author zzf * @date 2021/1/25 9:24 */ -public interface SysSmsLogService { +public interface SysSmsQueryLogService { /** * 发送短信前的日志处理 * * @param smsBody 短信内容 * @param targetPhones 发送对象手机号集合 * @param client 短信客户端 - * @param isAsync 是否异步发送 * @return 生成的日志id */ // TODO FROM 芋艿 to ZZF: async 是针对发送的方式,对于日志不一定需要关心。这样,短信日志,实际就发送前插入,发送后更新结果. // 这里只用于记录状态,毕竟异步可能推送失败,此时日志可记录该状态。 // TODO FROM 芋艿 to ZZF:短信日志,群发的情况,应该是每个手机一条哈。虽然是群发,但是可能部分成功,部分失败;对应到短信平台,实际也是多条。 - Long beforeSendLog(SmsBody smsBody, List targetPhones, AbstractSmsClient client, Boolean isAsync); + void beforeSendLog(SmsBody smsBody, List targetPhones, AbstractSmsClient client); /** * 发送消息后的日志处理 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 new file mode 100644 index 000000000..fe6f5e973 --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java @@ -0,0 +1,13 @@ +package cn.iocoder.dashboard.modules.system.service.sms; + +/** + * 短信发送日志服务接口 + * + * @author zzf + * @date 13:48 2021/3/2 + */ +public interface SysSmsSendLogService { + + void getAndSaveSmsSendLog(); + +} diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java index 5b403cb5e..6d851d4f6 100644 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java @@ -1,15 +1,15 @@ package cn.iocoder.dashboard.modules.system.service.sms; import cn.iocoder.dashboard.framework.sms.core.SmsBody; -import cn.iocoder.dashboard.framework.sms.core.SmsResult; -import org.apache.commons.lang3.StringUtils; +import javax.servlet.ServletRequest; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * 短信Service接口 + * 只支持异步,因此没有返回值 * * @author zzf * @date 2021/1/25 9:24 @@ -21,23 +21,17 @@ public interface SysSmsService { * * @param smsBody 消息内容 * @param targetPhones 发送对象手机号列表 - * @return 是否发送成功 */ - SmsResult send(SmsBody smsBody, List targetPhones); + void send(SmsBody smsBody, List targetPhones); /** * 发送消息 * * @param smsBody 消息内容 * @param targetPhone 发送对象手机号 - * @return 是否发送成功 */ - default SmsResult send(SmsBody smsBody, String targetPhone) { - if (StringUtils.isBlank(targetPhone)) { - return failResult("targetPhone must not null."); - } - - return send(smsBody, Collections.singletonList(targetPhone)); + default void send(SmsBody smsBody, String targetPhone) { + send(smsBody, Collections.singletonList(targetPhone)); } /** @@ -45,57 +39,16 @@ public interface SysSmsService { * * @param smsBody 消息内容 * @param targetPhones 发送对象手机号数组 - * @return 是否发送成功 */ - default SmsResult send(SmsBody smsBody, String... targetPhones) { - if (targetPhones == null) { - return failResult("targetPhones must not null."); - } - - return send(smsBody, Arrays.asList(targetPhones)); - } - - - /** - * 异步发送消息 - * - * @param msgBody 消息内容 - * @param targetPhones 发送对象列表 - */ - void sendAsync(SmsBody msgBody, List targetPhones); - - /** - * 异步发送消息 - * - * @param msgBody 消息内容 - * @param targetPhone 发送对象 - */ - default void sendAsync(SmsBody msgBody, String targetPhone) { - if (StringUtils.isBlank(targetPhone)) { - return; - } - sendAsync(msgBody, Collections.singletonList(targetPhone)); + default void send(SmsBody smsBody, String... targetPhones) { + send(smsBody, Arrays.asList(targetPhones)); } /** - * 异步发送消息 + * 处理短信发送回调函数 * - * @param msgBody 消息内容 - * @param targetPhones 发送对象列表 + * @param request 请求 + * @return 响应数据 */ - default void sendAsync(SmsBody msgBody, String... targetPhones) { - if (targetPhones == null) { - return; - } - sendAsync(msgBody, Arrays.asList(targetPhones)); - } - - - default SmsResult failResult(String message) { - SmsResult resultBody = new SmsResult(); - resultBody.setSuccess(false); - resultBody.setMessage(message); - return resultBody; - } - + Object smsSendCallbackHandle(ServletRequest request); } diff --git a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsLogServiceImpl.java b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsLogServiceImpl.java deleted file mode 100644 index a89f80ff7..000000000 --- a/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsLogServiceImpl.java +++ /dev/null @@ -1,77 +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.property.SmsChannelProperty; -import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsLogMapper; -import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsLogDO; -import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService; -import cn.iocoder.dashboard.util.json.JsonUtils; -import org.springframework.stereotype.Service; - -import javax.annotation.Resource; -import java.util.List; - -/** - * 短信日志Service实现类 - * - * @author zzf - * @date 2021/1/25 9:25 - */ -@Service -public class SysSmsLogServiceImpl implements SysSmsLogService { - - @Resource - private SysSmsLogMapper logMapper; - - @Override - public Long beforeSendLog(SmsBody smsBody, List targetPhones, AbstractSmsClient client, Boolean isAsync) { - SysSmsLogDO smsLog = new SysSmsLogDO(); - if (smsBody.getSmsLogId() != null) { - smsLog.setId(smsBody.getSmsLogId()); - smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus()); - logMapper.updateById(smsLog); - return smsBody.getSmsLogId(); - } else { - SmsChannelProperty property = client.getProperty(); - - smsLog.setChannelCode(property.getCode()) - .setChannelId(property.getId()) - .setTemplateCode(smsBody.getTemplateCode()) - .setPhones(JsonUtils.toJsonString(targetPhones)) - .setContent(smsBody.getParams().toString()); - - if (isAsync) { - smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus()); - } else { - smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus()); - } - logMapper.insert(smsLog); - return smsLog.getId(); - } - } - - @Override - public void afterSendLog(Long logId, SmsResult result) { - SysSmsLogDO smsLog = new SysSmsLogDO(); - smsLog.setId(logId); - if (result.getSuccess()) { - smsLog.setSendStatus(SmsSendStatusEnum.SUCCESS.getStatus()); - SysSmsLogDO smsLogDO = logMapper.selectById(logId); - result.getResult().forEach(s -> { - smsLogDO.setPhones(s.getPhone()); - smsLogDO.setSendStatus(s.getStatus()); - smsLogDO.setRemark(s.getMessage()); - smsLogDO.setCreateTime(s.getCreateTime()); - logMapper.insert(smsLogDO); - }); - } else { - smsLog.setSendStatus(SmsSendStatusEnum.FAIL.getStatus()); - smsLog.setRemark(result.getMessage() + JsonUtils.toJsonString(result.getResult())); - } - logMapper.updateById(smsLog); - } - -} 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 new file mode 100644 index 000000000..7264f550c --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsQueryLogServiceImpl.java @@ -0,0 +1,59 @@ +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.property.SmsChannelProperty; +import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsQueryLogMapper; +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsQueryLogDO; +import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; +import cn.iocoder.dashboard.util.json.JsonUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; + +/** + * 短信请求日志服务实现类 + * + * @author zzf + * @date 13:50 2021/3/2 + */ +@Service +public class SysSmsQueryLogServiceImpl implements SysSmsQueryLogService { + + @Resource + private SysSmsQueryLogMapper logMapper; + + @Override + public void beforeSendLog(SmsBody smsBody, List targetPhones, AbstractSmsClient client) { + SysSmsQueryLogDO smsLog = new SysSmsQueryLogDO(); + SmsChannelProperty property = client.getProperty(); + + smsLog.setChannelCode(property.getCode()) + .setChannelId(property.getId()) + .setTemplateCode(smsBody.getTemplateCode()) + .setPhones(targetPhones) + .setContent(smsBody.getParams().toString()); + + smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus()); + logMapper.insert(smsLog); + smsBody.setSmsLogId(smsLog.getId()); + } + + @Override + public void afterSendLog(Long logId, SmsResult result) { + SysSmsQueryLogDO smsLog = new SysSmsQueryLogDO(); + smsLog.setId(logId); + if (result.getSuccess()) { + smsLog.setSendStatus(SmsSendStatusEnum.QUERY_SUCCESS.getStatus()); + smsLog.setSendResultParam(result.getSendResultParam()); + } else { + smsLog.setSendStatus(SmsSendStatusEnum.QUERY_FAIL.getStatus()); + smsLog.setRemark(result.getMessage()); + } + logMapper.updateById(smsLog); + } + +} 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 new file mode 100644 index 000000000..03ef5575d --- /dev/null +++ b/src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java @@ -0,0 +1,109 @@ +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.framework.sms.client.NeedQuerySendResultSmsClient; +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; +import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsQueryLogMapper; +import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsSendLogMapper; +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsQueryLogDO; +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsSendLogDO; +import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; +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; + +/** + * 短信发送日志服务实现类 + * + * @author zzf + * @date 2021/1/25 9:25 + */ +@Slf4j +@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 void getAndSaveSmsSendLog() { + + List noResultQueryLogList = smsQueryLogMapper.selectNoResultQueryLogList(); + + if (CollectionUtil.isEmpty(noResultQueryLogList)) { + return; + } + //用于添加的发送日志对象 + SysSmsSendLogDO insertSendLog = new SysSmsSendLogDO(); + //用于修改状态的请求日志对象 + SysSmsQueryLogDO updateQueryLog = new SysSmsQueryLogDO(); + + 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(SmsSendStatusEnum.SEND_SUCCESS.getStatus()); + updateQueryLog.setRemark(String.format("日志(id = %s)对应的客户端没有继承NeedQuerySendResultSmsClient, 不能获取短信结果。", queryLog.getId())); + smsQueryLogMapper.updateById(updateQueryLog); + }); + } + + private void queryLog2SendLong(SysSmsSendLogDO insertSendLog, SysSmsQueryLogDO 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 eb4019fee..81371e575 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 @@ -2,11 +2,11 @@ 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.modules.system.mq.producer.sms.SmsProducer; import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService; +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -25,30 +25,18 @@ public class SysSmsServiceImpl implements SysSmsService { private SysSmsChannelService channelService; @Resource - private SysSmsLogService logService; + private SysSmsQueryLogService logService; @Resource private SmsProducer smsProducer; @Override - public SmsResult send(SmsBody smsBody, List targetPhones) { + public void send(SmsBody smsBody, List targetPhones) { AbstractSmsClient client = channelService.getSmsClient(smsBody.getTemplateCode()); - String templateApiId = channelService.getSmsTemplateApiIdByCode(smsBody.getTemplateCode()); - Long logId = logService.beforeSendLog(smsBody, targetPhones, client, false); - - SmsResult result = client.send(templateApiId, smsBody, targetPhones); - - logService.afterSendLog(logId, result); - - return result; - } - - // TODO FROM 芋艿 to ZZF:可能要讨论下,对于短信发送来说,貌似只提供异步发送即可。对于业务来说,一定不能依赖短信的发送结果。 - // 我的想法是1、很多短信,比如验证码,总还是需要知道是否发送成功的。2、别人可以不用,我们不能没有。3、实现挺简单的,个人觉得无需纠结。 - @Override - public void sendAsync(SmsBody smsBody, List targetPhones) { - AbstractSmsClient client = channelService.getSmsClient(smsBody.getTemplateCode()); - logService.beforeSendLog(smsBody, targetPhones, client, true); + logService.beforeSendLog(smsBody, targetPhones, client); smsProducer.sendSmsSendMessage(smsBody, targetPhones); } + + // TODO FROM 芋艿 to ZZF:可能要讨论下,对于短信发送来说,貌似只提供异步发送即可。对于业务来说,一定不能依赖短信的发送结果. + } diff --git a/src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java b/src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java index f6727459c..2a735e214 100644 --- a/src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java +++ b/src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java @@ -20,7 +20,7 @@ public class JsonUtils { /** * 初始化 objectMapper 属性 - * + *

* 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean * * @param objectMapper ObjectMapper 对象 @@ -67,4 +67,12 @@ public class JsonUtils { } } + public static T parseByType(String text, TypeReference typeReference) { + try { + return objectMapper.readValue(text, typeReference); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }