Commit 767cd90279e2247e704156f7c028b0b97560f663
1 parent
c3372d4b
批量发送半成品
Showing
29 changed files
with
693 additions
and
188 deletions
pom.xml
| @@ -184,12 +184,6 @@ | @@ -184,12 +184,6 @@ | ||
| 184 | </dependency> | 184 | </dependency> |
| 185 | 185 | ||
| 186 | <dependency> | 186 | <dependency> |
| 187 | - <groupId>org.projectlombok</groupId> | ||
| 188 | - <artifactId>lombok</artifactId> | ||
| 189 | - <version>${lombok.version}</version> | ||
| 190 | - </dependency> | ||
| 191 | - | ||
| 192 | - <dependency> | ||
| 193 | <groupId>org.mapstruct</groupId> | 187 | <groupId>org.mapstruct</groupId> |
| 194 | <artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher --> | 188 | <artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher --> |
| 195 | <version>${mapstruct.version}</version> | 189 | <version>${mapstruct.version}</version> |
| @@ -222,17 +216,25 @@ | @@ -222,17 +216,25 @@ | ||
| 222 | <version>${easyexcel.verion}</version> | 216 | <version>${easyexcel.verion}</version> |
| 223 | </dependency> | 217 | </dependency> |
| 224 | 218 | ||
| 219 | + | ||
| 220 | + <!-- SMS SDK begin --> | ||
| 221 | + <dependency> | ||
| 222 | + <groupId>com.yunpian.sdk</groupId> | ||
| 223 | + <artifactId>yunpian-java-sdk</artifactId> | ||
| 224 | + <version>1.2.7</version> | ||
| 225 | + </dependency> | ||
| 226 | + | ||
| 225 | <dependency> | 227 | <dependency> |
| 226 | <groupId>com.aliyun</groupId> | 228 | <groupId>com.aliyun</groupId> |
| 227 | <artifactId>aliyun-java-sdk-core</artifactId> | 229 | <artifactId>aliyun-java-sdk-core</artifactId> |
| 228 | <version>4.5.18</version> | 230 | <version>4.5.18</version> |
| 229 | </dependency> | 231 | </dependency> |
| 230 | - | ||
| 231 | <dependency> | 232 | <dependency> |
| 232 | <groupId>com.aliyun</groupId> | 233 | <groupId>com.aliyun</groupId> |
| 233 | <artifactId>aliyun-java-sdk-dysmsapi</artifactId> | 234 | <artifactId>aliyun-java-sdk-dysmsapi</artifactId> |
| 234 | <version>2.1.0</version> | 235 | <version>2.1.0</version> |
| 235 | </dependency> | 236 | </dependency> |
| 237 | + <!-- SMS SDK end --> | ||
| 236 | 238 | ||
| 237 | </dependencies> | 239 | </dependencies> |
| 238 | 240 |
sql/sms.sql
| @@ -12,6 +12,8 @@ CREATE TABLE `sms_channel` | @@ -12,6 +12,8 @@ CREATE TABLE `sms_channel` | ||
| 12 | `code` varchar(50) NOT NULL COMMENT '编码(来自枚举类 阿里、华为、七牛等)', | 12 | `code` varchar(50) NOT NULL COMMENT '编码(来自枚举类 阿里、华为、七牛等)', |
| 13 | `api_key` varchar(100) NOT NULL COMMENT '账号id', | 13 | `api_key` varchar(100) NOT NULL COMMENT '账号id', |
| 14 | `api_secret` varchar(100) NOT NULL COMMENT '账号秘钥', | 14 | `api_secret` varchar(100) NOT NULL COMMENT '账号秘钥', |
| 15 | + `had_callback` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否拥有回调函数', | ||
| 16 | + `callback_url` varchar(100) NOT NULL default '' COMMENT '回调请求路径', | ||
| 15 | `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识', | 17 | `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识', |
| 16 | `name` varchar(50) NOT NULL COMMENT '名称', | 18 | `name` varchar(50) NOT NULL COMMENT '名称', |
| 17 | `signature` varchar(50) NOT NULL COMMENT '签名值', | 19 | `signature` varchar(50) NOT NULL COMMENT '签名值', |
| @@ -61,22 +63,46 @@ CREATE TABLE `sms_template` | @@ -61,22 +63,46 @@ CREATE TABLE `sms_template` | ||
| 61 | DEFAULT CHARSET = utf8mb4 COMMENT ='短信模板'; | 63 | DEFAULT CHARSET = utf8mb4 COMMENT ='短信模板'; |
| 62 | 64 | ||
| 63 | -- ---------------------------- | 65 | -- ---------------------------- |
| 66 | +-- Table structure for sms_query_log | ||
| 67 | +-- ---------------------------- | ||
| 68 | +DROP TABLE IF EXISTS `sms_query_log`; | ||
| 69 | +CREATE TABLE `sms_query_log` | ||
| 70 | +( | ||
| 71 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', | ||
| 72 | + `api_id` varchar(100) NOT NULL COMMENT '第三方唯一标识', | ||
| 73 | + `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)', | ||
| 74 | + `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id', | ||
| 75 | + `template_code` varchar(50) NOT NULL COMMENT '渠道编码', | ||
| 76 | + `phones` varchar(2000) NOT NULL COMMENT '手机号(数组json字符串)', | ||
| 77 | + `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', | ||
| 78 | + `send_result_param` varchar(200) NOT NULL DEFAULT '' COMMENT '查询短信发送结果的参数', | ||
| 79 | + `send_status` tinyint(1) NOT NULL DEFAULT 2 COMMENT '发送状态(0本地异步中 1发送请求失败 2发送请求成功)', | ||
| 80 | + `got_result` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否获取发送结果', | ||
| 81 | + `had_callback` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否拥有回调函数', | ||
| 82 | + `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者', | ||
| 83 | + `create_time` datetime DEFAULT NULL COMMENT '创建时间', | ||
| 84 | + PRIMARY KEY (`id`) USING BTREE | ||
| 85 | +) ENGINE = InnoDB | ||
| 86 | + AUTO_INCREMENT = 1 | ||
| 87 | + DEFAULT CHARSET = utf8mb4 COMMENT ='短信请求日志'; | ||
| 88 | + | ||
| 89 | +-- ---------------------------- | ||
| 64 | -- Table structure for sms_log | 90 | -- Table structure for sms_log |
| 65 | -- ---------------------------- | 91 | -- ---------------------------- |
| 66 | -DROP TABLE IF EXISTS `sms_log`; | ||
| 67 | -CREATE TABLE `sms_log` | 92 | +DROP TABLE IF EXISTS `sms_send_log`; |
| 93 | +CREATE TABLE `sms_send_log` | ||
| 68 | ( | 94 | ( |
| 69 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', | 95 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', |
| 70 | `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)', | 96 | `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)', |
| 71 | `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id', | 97 | `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id', |
| 72 | `template_code` varchar(50) NOT NULL COMMENT '渠道编码', | 98 | `template_code` varchar(50) NOT NULL COMMENT '渠道编码', |
| 73 | - `phones` char(11) NOT NULL COMMENT '手机号(数组json字符串)', | 99 | + `query_log_id` bigint(20) NOT NULL COMMENT '请求日志id', |
| 100 | + `phone` char(11) NOT NULL COMMENT '手机号', | ||
| 74 | `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', | 101 | `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', |
| 75 | - `remark` varchar(200) DEFAULT NULL COMMENT '备注', | ||
| 76 | - `send_status` tinyint(4) NOT NULL DEFAULT 2 COMMENT '发送状态(1异步推送中 2发送中 3失败 4成功)', | ||
| 77 | - `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者', | ||
| 78 | - `create_time` datetime DEFAULT NULL COMMENT '创建时间', | 102 | + `remark` varchar(200) DEFAULT NULL COMMENT '备注', |
| 103 | + `success` tinyint(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | ||
| 104 | + `send_time` datetime DEFAULT NULL COMMENT '创建时间', | ||
| 79 | PRIMARY KEY (`id`) USING BTREE | 105 | PRIMARY KEY (`id`) USING BTREE |
| 80 | ) ENGINE = InnoDB | 106 | ) ENGINE = InnoDB |
| 81 | AUTO_INCREMENT = 1 | 107 | AUTO_INCREMENT = 1 |
| 82 | - DEFAULT CHARSET = utf8mb4 COMMENT ='短信日志'; | 108 | + DEFAULT CHARSET = utf8mb4 COMMENT ='短信发送日志'; |
src/main/java/cn/iocoder/dashboard/common/enums/DefaultBitFieldEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.common.enums; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 通用状态枚举 | ||
| 8 | + * | ||
| 9 | + * @author 芋道源码 | ||
| 10 | + */ | ||
| 11 | +@Getter | ||
| 12 | +@AllArgsConstructor | ||
| 13 | +public enum DefaultBitFieldEnum { | ||
| 14 | + | ||
| 15 | + NO(0, "否"), | ||
| 16 | + YES(1, "是"); | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 状态值 | ||
| 20 | + */ | ||
| 21 | + private final Integer val; | ||
| 22 | + /** | ||
| 23 | + * 状态名 | ||
| 24 | + */ | ||
| 25 | + private final String name; | ||
| 26 | + | ||
| 27 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java
| @@ -58,7 +58,7 @@ public abstract class AbstractSmsClient implements SmsClient { | @@ -58,7 +58,7 @@ public abstract class AbstractSmsClient implements SmsClient { | ||
| 58 | * @return 短信发送结果 | 58 | * @return 短信发送结果 |
| 59 | * @throws Exception 调用发送失败,抛出异常 | 59 | * @throws Exception 调用发送失败,抛出异常 |
| 60 | */ | 60 | */ |
| 61 | - public abstract SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception; | 61 | + protected abstract SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception; |
| 62 | 62 | ||
| 63 | protected void beforeSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception { | 63 | protected void beforeSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception { |
| 64 | } | 64 | } |
src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java
| @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.framework.sms.client; | @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.framework.sms.client; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.date.DateUtil; | 3 | import cn.hutool.core.date.DateUtil; |
| 4 | import cn.hutool.core.util.ArrayUtil; | 4 | import cn.hutool.core.util.ArrayUtil; |
| 5 | -import cn.hutool.core.util.StrUtil; | ||
| 6 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; | 5 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 7 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; | 6 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 8 | import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | 7 | import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; |
| @@ -14,11 +13,11 @@ import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest; | @@ -14,11 +13,11 @@ import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest; | ||
| 14 | import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse; | 13 | import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse; |
| 15 | import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; | 14 | import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; |
| 16 | import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; | 15 | import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; |
| 16 | +import com.aliyuncs.exceptions.ClientException; | ||
| 17 | import com.aliyuncs.http.MethodType; | 17 | import com.aliyuncs.http.MethodType; |
| 18 | import com.aliyuncs.profile.DefaultProfile; | 18 | import com.aliyuncs.profile.DefaultProfile; |
| 19 | import com.aliyuncs.profile.IClientProfile; | 19 | import com.aliyuncs.profile.IClientProfile; |
| 20 | import lombok.extern.slf4j.Slf4j; | 20 | import lombok.extern.slf4j.Slf4j; |
| 21 | -import org.apache.commons.lang3.StringUtils; | ||
| 22 | 21 | ||
| 23 | import java.util.ArrayList; | 22 | import java.util.ArrayList; |
| 24 | import java.util.Collection; | 23 | import java.util.Collection; |
| @@ -31,7 +30,7 @@ import java.util.List; | @@ -31,7 +30,7 @@ import java.util.List; | ||
| 31 | * @date 2021/1/25 14:17 | 30 | * @date 2021/1/25 14:17 |
| 32 | */ | 31 | */ |
| 33 | @Slf4j | 32 | @Slf4j |
| 34 | -public class AliyunSmsClient extends AbstractSmsClient { | 33 | +public class AliyunSmsClient extends AbstractSmsClient implements NeedQuerySendResultSmsClient { |
| 35 | 34 | ||
| 36 | private static final String OK = "OK"; | 35 | private static final String OK = "OK"; |
| 37 | 36 | ||
| @@ -70,35 +69,43 @@ public class AliyunSmsClient extends AbstractSmsClient { | @@ -70,35 +69,43 @@ public class AliyunSmsClient extends AbstractSmsClient { | ||
| 70 | request.setTemplateParam(smsBody.getParamsStr()); | 69 | request.setTemplateParam(smsBody.getParamsStr()); |
| 71 | SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); | 70 | SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); |
| 72 | 71 | ||
| 73 | - boolean result = OK.equals(sendSmsResponse.getCode()); | ||
| 74 | - if (!result) { | 72 | + boolean success = OK.equals(sendSmsResponse.getCode()); |
| 73 | + if (!success) { | ||
| 75 | log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage()); | 74 | log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage()); |
| 76 | } | 75 | } |
| 77 | - SmsResult resultBody = new SmsResult(); | ||
| 78 | - resultBody.setSuccess(result); | 76 | + return new SmsResult() |
| 77 | + .setSuccess(success) | ||
| 78 | + .setMessage(sendSmsResponse.getMessage()) | ||
| 79 | + .setCode(sendSmsResponse.getCode()) | ||
| 80 | + .setApiId(sendSmsResponse.getBizId()) | ||
| 81 | + .setSendResultParam(sendSmsResponse.getBizId()); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + | ||
| 85 | + @Override | ||
| 86 | + public List<SmsResultDetail> getSmsSendResult(String param) throws ClientException { | ||
| 79 | QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest(); | 87 | QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest(); |
| 80 | - querySendDetailsRequest.setBizId(sendSmsResponse.getBizId()); | ||
| 81 | - // TODO FROM 芋艿 to zzf:发送完之后,基于短信平台回调,去更新回执状态。短信发送是否成功,和最终用户收到,是两个维度。这块有困惑,可以微信,我给个截图哈。 | 88 | + querySendDetailsRequest.setBizId(param); |
| 89 | + // TODO FROM 芋艿 to zzf:发送完之后,基于短信平台回调,去更新回执状态。短信发送是否成功,和最终用户收到,是两个维度。这块有困惑,可以微信,我给个截图哈。 DONE | ||
| 82 | QuerySendDetailsResponse acsResponse = acsClient.getAcsResponse(querySendDetailsRequest); | 90 | QuerySendDetailsResponse acsResponse = acsClient.getAcsResponse(querySendDetailsRequest); |
| 83 | List<SmsResultDetail> resultDetailList = new ArrayList<>(Integer.parseInt(acsResponse.getTotalCount())); | 91 | List<SmsResultDetail> resultDetailList = new ArrayList<>(Integer.parseInt(acsResponse.getTotalCount())); |
| 84 | acsResponse.getSmsSendDetailDTOs().forEach(s -> { | 92 | acsResponse.getSmsSendDetailDTOs().forEach(s -> { |
| 85 | SmsResultDetail resultDetail = new SmsResultDetail(); | 93 | SmsResultDetail resultDetail = new SmsResultDetail(); |
| 86 | - resultDetail.setCreateTime(DateUtil.parseDateTime(s.getSendDate())); | 94 | + resultDetail.setSendTime(DateUtil.parseDateTime(s.getSendDate())); |
| 87 | resultDetail.setMessage(s.getContent()); | 95 | resultDetail.setMessage(s.getContent()); |
| 88 | resultDetail.setPhone(s.getPhoneNum()); | 96 | resultDetail.setPhone(s.getPhoneNum()); |
| 89 | - resultDetail.setStatus(statusConvert(s.getSendStatus())); | 97 | + resultDetail.setSendStatus(statusConvert(s.getSendStatus())); |
| 90 | resultDetailList.add(resultDetail); | 98 | resultDetailList.add(resultDetail); |
| 91 | }); | 99 | }); |
| 92 | - resultBody.setResult(resultDetailList); | ||
| 93 | - return resultBody; | 100 | + return resultDetailList; |
| 94 | } | 101 | } |
| 95 | 102 | ||
| 96 | private int statusConvert(Long aliSendStatus) { | 103 | private int statusConvert(Long aliSendStatus) { |
| 97 | if (aliSendStatus == 1L) { | 104 | if (aliSendStatus == 1L) { |
| 98 | - return SmsSendStatusEnum.SUCCESS.getStatus(); | 105 | + return SmsSendStatusEnum.SEND_SUCCESS.getStatus(); |
| 99 | } | 106 | } |
| 100 | if (aliSendStatus == 2L) { | 107 | if (aliSendStatus == 2L) { |
| 101 | - return SmsSendStatusEnum.FAIL.getStatus(); | 108 | + return SmsSendStatusEnum.SEND_FAIL.getStatus(); |
| 102 | } | 109 | } |
| 103 | return SmsSendStatusEnum.WAITING.getStatus(); | 110 | return SmsSendStatusEnum.WAITING.getStatus(); |
| 104 | } | 111 | } |
src/main/java/cn/iocoder/dashboard/framework/sms/client/HadCallbackSmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.client; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | ||
| 4 | + | ||
| 5 | +import javax.servlet.ServletRequest; | ||
| 6 | +import java.io.UnsupportedEncodingException; | ||
| 7 | +import java.util.List; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 需要发送请求获取短信发送结果的短信客户端 | ||
| 11 | + * | ||
| 12 | + * @author zzf | ||
| 13 | + * @date 2021/3/4 17:20 | ||
| 14 | + */ | ||
| 15 | +public interface HadCallbackSmsClient { | ||
| 16 | + | ||
| 17 | + /** | ||
| 18 | + * 获取短信发送结果 | ||
| 19 | + * | ||
| 20 | + * @param request 请求 | ||
| 21 | + * @return 短信发送结果 | ||
| 22 | + */ | ||
| 23 | + List<SmsResultDetail> getSmsSendResult(ServletRequest request) throws Exception; | ||
| 24 | + | ||
| 25 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/client/NeedQuerySendResultSmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.client; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | ||
| 4 | +import com.aliyuncs.exceptions.ClientException; | ||
| 5 | + | ||
| 6 | +import java.util.List; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 需要发送请求获取短信发送结果的短信客户端 | ||
| 10 | + * | ||
| 11 | + * @author zzf | ||
| 12 | + * @date 2021/3/4 17:20 | ||
| 13 | + */ | ||
| 14 | +public interface NeedQuerySendResultSmsClient { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 获取短信发送结果 | ||
| 18 | + * | ||
| 19 | + * @param param 参数 | ||
| 20 | + * @return 短信发送结果 | ||
| 21 | + */ | ||
| 22 | + List<SmsResultDetail> getSmsSendResult(String param) throws Exception; | ||
| 23 | + | ||
| 24 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java
| @@ -2,8 +2,10 @@ package cn.iocoder.dashboard.framework.sms.client; | @@ -2,8 +2,10 @@ package cn.iocoder.dashboard.framework.sms.client; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; | 3 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 4 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; | 4 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | ||
| 5 | 6 | ||
| 6 | import java.util.Collection; | 7 | import java.util.Collection; |
| 8 | +import java.util.List; | ||
| 7 | 9 | ||
| 8 | /** | 10 | /** |
| 9 | * 短信父接口 | 11 | * 短信父接口 |
| @@ -23,4 +25,7 @@ public interface SmsClient { | @@ -23,4 +25,7 @@ public interface SmsClient { | ||
| 23 | */ | 25 | */ |
| 24 | SmsResult send(String templateApiId, SmsBody smsBody, Collection<String> targets); | 26 | SmsResult send(String templateApiId, SmsBody smsBody, Collection<String> targets); |
| 25 | 27 | ||
| 28 | + | ||
| 29 | + //List<SmsResultDetail> getSmsSendResult(String jsonObjectParam); | ||
| 30 | + | ||
| 26 | } | 31 | } |
| 27 | \ No newline at end of file | 32 | \ No newline at end of file |
src/main/java/cn/iocoder/dashboard/framework/sms/client/YunpianSmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.client; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.date.DateUtil; | ||
| 4 | +import cn.hutool.core.util.CharsetUtil; | ||
| 5 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | ||
| 6 | +import cn.iocoder.dashboard.framework.sms.core.SmsConstants; | ||
| 7 | +import cn.iocoder.dashboard.framework.sms.core.SmsResult; | ||
| 8 | +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | ||
| 9 | +import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; | ||
| 10 | +import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; | ||
| 11 | +import cn.iocoder.dashboard.util.json.JsonUtils; | ||
| 12 | +import com.fasterxml.jackson.core.type.TypeReference; | ||
| 13 | +import com.yunpian.sdk.YunpianClient; | ||
| 14 | +import com.yunpian.sdk.constant.Code; | ||
| 15 | +import com.yunpian.sdk.constant.YunpianConstant; | ||
| 16 | +import com.yunpian.sdk.model.Result; | ||
| 17 | +import com.yunpian.sdk.model.SmsBatchSend; | ||
| 18 | +import lombok.extern.slf4j.Slf4j; | ||
| 19 | + | ||
| 20 | +import javax.servlet.ServletRequest; | ||
| 21 | +import java.io.UnsupportedEncodingException; | ||
| 22 | +import java.net.URLEncoder; | ||
| 23 | +import java.util.*; | ||
| 24 | + | ||
| 25 | +/** | ||
| 26 | + * 云片短信实现类 | ||
| 27 | + * | ||
| 28 | + * @author zzf | ||
| 29 | + * @date 9:48 2021/3/5 | ||
| 30 | + */ | ||
| 31 | +@Slf4j | ||
| 32 | +public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSmsClient { | ||
| 33 | + | ||
| 34 | + private final YunpianClient client; | ||
| 35 | + | ||
| 36 | + private final TypeReference<List<Map<String, String>>> callbackType = new TypeReference<List<Map<String, String>>>() { | ||
| 37 | + }; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 构造云片短信发送处理 | ||
| 41 | + * | ||
| 42 | + * @param channelVO 阿里云短信配置 | ||
| 43 | + */ | ||
| 44 | + public YunpianSmsClient(SmsChannelProperty channelVO) { | ||
| 45 | + super(channelVO); | ||
| 46 | + client = new YunpianClient(channelVO.getApiKey()); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + @Override | ||
| 50 | + public SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) { | ||
| 51 | + Map<String, String> paramMap = new HashMap<>(); | ||
| 52 | + paramMap.put("apikey", getProperty().getApiKey()); | ||
| 53 | + paramMap.put("mobile", String.join(SmsConstants.COMMA, targets)); | ||
| 54 | + paramMap.put("text", formatContent(smsBody)); | ||
| 55 | + paramMap.put("callback", getProperty().getCallbackUrl()); | ||
| 56 | + | ||
| 57 | + Result<SmsBatchSend> sendResult = client.sms().batch_send(paramMap); | ||
| 58 | + boolean success = sendResult.getCode().equals(Code.OK); | ||
| 59 | + | ||
| 60 | + if (!success) { | ||
| 61 | + log.debug("send fail[code={}, message={}]", sendResult.getCode(), sendResult.getDetail()); | ||
| 62 | + } | ||
| 63 | + return new SmsResult() | ||
| 64 | + .setSuccess(success) | ||
| 65 | + .setMessage(sendResult.getDetail()) | ||
| 66 | + .setCode(sendResult.getCode().toString()) | ||
| 67 | + .setApiId(sendResult.getData().getData().get(0).getSid().toString()); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + | ||
| 71 | + /** | ||
| 72 | + * 格式化短信内容,将参数注入到模板中 | ||
| 73 | + * | ||
| 74 | + * @param smsBody 短信信息 | ||
| 75 | + * @return 格式化后的短信内容 | ||
| 76 | + */ | ||
| 77 | + private String formatContent(SmsBody smsBody) { | ||
| 78 | + StringBuilder result = new StringBuilder(smsBody.getTemplateContent()); | ||
| 79 | + smsBody.getParams().forEach((key, val) -> { | ||
| 80 | + String param = parseParamToPlaceholder(key); | ||
| 81 | + result.replace(result.indexOf(param), result.indexOf(param + param.length()), val); | ||
| 82 | + }); | ||
| 83 | + return result.toString(); | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | + /** | ||
| 87 | + * 将指定参数改成对应的占位字符 | ||
| 88 | + * <p> | ||
| 89 | + * 云片的是 #param# 的形式作为占位符 | ||
| 90 | + * | ||
| 91 | + * @param key 参数名 | ||
| 92 | + * @return 对应的占位字符 | ||
| 93 | + */ | ||
| 94 | + private String parseParamToPlaceholder(String key) { | ||
| 95 | + return SmsConstants.JING_HAO + key + SmsConstants.JING_HAO; | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + | ||
| 99 | + @Override | ||
| 100 | + public List<SmsResultDetail> getSmsSendResult(ServletRequest request) throws UnsupportedEncodingException { | ||
| 101 | + List<Map<String, String>> stringStringMap = getSendResult(request); | ||
| 102 | + List<SmsResultDetail> resultDetailList = new ArrayList<>(stringStringMap.size()); | ||
| 103 | + stringStringMap.forEach(map -> { | ||
| 104 | + SmsResultDetail detail = new SmsResultDetail(); | ||
| 105 | + | ||
| 106 | + detail.setPhone(map.get("mobile")); | ||
| 107 | + detail.setMessage(map.get("error_msg")); | ||
| 108 | + detail.setSendTime(DateUtil.parseTime(map.get("user_receive_time"))); | ||
| 109 | + String reportStatus = map.get("report_status"); | ||
| 110 | + detail.setSendStatus(reportStatus.equals(SmsConstants.SUCCESS) | ||
| 111 | + ? SmsSendStatusEnum.SEND_SUCCESS.getStatus() | ||
| 112 | + : SmsSendStatusEnum.SEND_FAIL.getStatus() | ||
| 113 | + ); | ||
| 114 | + resultDetailList.add(detail); | ||
| 115 | + }); | ||
| 116 | + return resultDetailList; | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + /** | ||
| 120 | + * 从 request 中获取请求中传入的短信发送结果信息 | ||
| 121 | + * | ||
| 122 | + * @param request 回调请求 | ||
| 123 | + * @return 短信发送结果信息 | ||
| 124 | + * @throws UnsupportedEncodingException 解码异常 | ||
| 125 | + */ | ||
| 126 | + private List<Map<String, String>> getSendResult(ServletRequest request) throws UnsupportedEncodingException { | ||
| 127 | + Map<String, String[]> parameterMap = request.getParameterMap(); | ||
| 128 | + String[] smsStatuses = parameterMap.get(YunpianConstant.SMS_STATUS); | ||
| 129 | + String encode = URLEncoder.encode(smsStatuses[0], CharsetUtil.UTF_8); | ||
| 130 | + return JsonUtils.parseByType(encode, callbackType); | ||
| 131 | + } | ||
| 132 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java
| @@ -4,7 +4,6 @@ import cn.iocoder.dashboard.util.json.JsonUtils; | @@ -4,7 +4,6 @@ import cn.iocoder.dashboard.util.json.JsonUtils; | ||
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | import java.util.Map; | 6 | import java.util.Map; |
| 7 | -import java.util.UUID; | ||
| 8 | 7 | ||
| 9 | /** | 8 | /** |
| 10 | * 消息内容实体类 | 9 | * 消息内容实体类 |
| @@ -23,6 +22,11 @@ public class SmsBody { | @@ -23,6 +22,11 @@ public class SmsBody { | ||
| 23 | private String templateCode; | 22 | private String templateCode; |
| 24 | 23 | ||
| 25 | /** | 24 | /** |
| 25 | + * 模板编码 | ||
| 26 | + */ | ||
| 27 | + private String templateContent; | ||
| 28 | + | ||
| 29 | + /** | ||
| 26 | * 参数列表 | 30 | * 参数列表 |
| 27 | */ | 31 | */ |
| 28 | private Map<String, String> params; | 32 | private Map<String, String> params; |
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsConstants.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * 短信相关常量类 | ||
| 5 | + * | ||
| 6 | + * @author zzf | ||
| 7 | + * @date 2021/3/5 10:42 | ||
| 8 | + */ | ||
| 9 | +public interface SmsConstants { | ||
| 10 | + | ||
| 11 | + String OK = "OK"; | ||
| 12 | + | ||
| 13 | + String JING_HAO = "#"; | ||
| 14 | + | ||
| 15 | + String COMMA = ","; | ||
| 16 | + | ||
| 17 | + String SUCCESS = "SUCCESS"; | ||
| 18 | +} |
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java
| 1 | package cn.iocoder.dashboard.framework.sms.core; | 1 | package cn.iocoder.dashboard.framework.sms.core; |
| 2 | 2 | ||
| 3 | import lombok.Data; | 3 | import lombok.Data; |
| 4 | +import lombok.experimental.Accessors; | ||
| 4 | 5 | ||
| 5 | import java.io.Serializable; | 6 | import java.io.Serializable; |
| 6 | -import java.util.List; | ||
| 7 | 7 | ||
| 8 | /** | 8 | /** |
| 9 | * 消息内容实体类 | 9 | * 消息内容实体类 |
| 10 | */ | 10 | */ |
| 11 | @Data | 11 | @Data |
| 12 | +@Accessors(chain = true) | ||
| 12 | public class SmsResult implements Serializable { | 13 | public class SmsResult implements Serializable { |
| 13 | 14 | ||
| 14 | /** | 15 | /** |
| @@ -17,6 +18,11 @@ public class SmsResult implements Serializable { | @@ -17,6 +18,11 @@ public class SmsResult implements Serializable { | ||
| 17 | private Boolean success; | 18 | private Boolean success; |
| 18 | 19 | ||
| 19 | /** | 20 | /** |
| 21 | + * 第三方唯一标识 | ||
| 22 | + */ | ||
| 23 | + private String apiId; | ||
| 24 | + | ||
| 25 | + /** | ||
| 20 | * 状态码 | 26 | * 状态码 |
| 21 | */ | 27 | */ |
| 22 | private String code; | 28 | private String code; |
| @@ -27,10 +33,9 @@ public class SmsResult implements Serializable { | @@ -27,10 +33,9 @@ public class SmsResult implements Serializable { | ||
| 27 | private String message; | 33 | private String message; |
| 28 | 34 | ||
| 29 | /** | 35 | /** |
| 30 | - * 返回值 | 36 | + * 用于查询发送结果的参数 |
| 31 | */ | 37 | */ |
| 32 | - private List<SmsResultDetail> result; | ||
| 33 | - | 38 | + private String sendResultParam; |
| 34 | 39 | ||
| 35 | public static SmsResult failResult(String message) { | 40 | public static SmsResult failResult(String message) { |
| 36 | SmsResult resultBody = new SmsResult(); | 41 | SmsResult resultBody = new SmsResult(); |
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResultDetail.java
| @@ -14,7 +14,7 @@ public class SmsResultDetail implements Serializable { | @@ -14,7 +14,7 @@ public class SmsResultDetail implements Serializable { | ||
| 14 | /** | 14 | /** |
| 15 | * 短信发送状态 {@link cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum} | 15 | * 短信发送状态 {@link cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum} |
| 16 | */ | 16 | */ |
| 17 | - private Integer status; | 17 | + private Integer sendStatus; |
| 18 | 18 | ||
| 19 | /** | 19 | /** |
| 20 | * 接收手机号 | 20 | * 接收手机号 |
| @@ -29,5 +29,5 @@ public class SmsResultDetail implements Serializable { | @@ -29,5 +29,5 @@ public class SmsResultDetail implements Serializable { | ||
| 29 | /** | 29 | /** |
| 30 | * 时间 | 30 | * 时间 |
| 31 | */ | 31 | */ |
| 32 | - private Date createTime; | 32 | + private Date sendTime; |
| 33 | } | 33 | } |
src/main/java/cn/iocoder/dashboard/framework/sms/core/property/SmsChannelProperty.java
| @@ -54,4 +54,15 @@ public class SmsChannelProperty implements Serializable { | @@ -54,4 +54,15 @@ public class SmsChannelProperty implements Serializable { | ||
| 54 | @NotEmpty(message = "签名值不能为空") | 54 | @NotEmpty(message = "签名值不能为空") |
| 55 | private String signature; | 55 | private String signature; |
| 56 | 56 | ||
| 57 | + /** | ||
| 58 | + * 是否拥有回调函数(0否 1是) | ||
| 59 | + */ | ||
| 60 | + @NotNull(message = "是否拥有回调函数不能为空") | ||
| 61 | + private Integer hadCallback; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 短信发送回调url | ||
| 65 | + */ | ||
| 66 | + private String callbackUrl; | ||
| 67 | + | ||
| 57 | } | 68 | } |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsDefaultCallbackController.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.controller.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; | ||
| 4 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
| 5 | +import org.springframework.web.bind.annotation.RestController; | ||
| 6 | + | ||
| 7 | +import javax.annotation.Resource; | ||
| 8 | +import javax.servlet.ServletRequest; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 短信默认回调接口 | ||
| 12 | + * | ||
| 13 | + * @author zzf | ||
| 14 | + * @date 2021/3/5 8:59 | ||
| 15 | + */ | ||
| 16 | +@RestController("/sms/callback") | ||
| 17 | +public class SmsDefaultCallbackController { | ||
| 18 | + | ||
| 19 | + @Resource | ||
| 20 | + private SysSmsService smsService; | ||
| 21 | + | ||
| 22 | + @RequestMapping("/sms-send") | ||
| 23 | + public Object sendSmsCallback(ServletRequest request){ | ||
| 24 | + return smsService.smsSendCallbackHandle(request); | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsQueryLogMapper.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.common.enums.DefaultBitFieldEnum; | ||
| 4 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsQueryLogDO; | ||
| 5 | +import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; | ||
| 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<SysSmsQueryLogDO> { | ||
| 14 | + | ||
| 15 | + /** | ||
| 16 | + * 查询还没有获取发送结果的短信请求信息 | ||
| 17 | + * | ||
| 18 | + * @return | ||
| 19 | + */ | ||
| 20 | + default List<SysSmsQueryLogDO> selectNoResultQueryLogList() { | ||
| 21 | + return this.selectList(new LambdaQueryWrapper<SysSmsQueryLogDO>() | ||
| 22 | + .eq(SysSmsQueryLogDO::getSendStatus, SmsSendStatusEnum.QUERY_SUCCESS) | ||
| 23 | + .eq(SysSmsQueryLogDO::getGotResult, DefaultBitFieldEnum.NO) | ||
| 24 | + .eq(SysSmsQueryLogDO::getHadCallback, DefaultBitFieldEnum.NO) | ||
| 25 | + ); | ||
| 26 | + } | ||
| 27 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsLogMapper.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsSendLogMapper.java
| 1 | package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms; | 1 | package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsLogDO; | 3 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsSendLogDO; |
| 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| 5 | import org.apache.ibatis.annotations.Mapper; | 5 | import org.apache.ibatis.annotations.Mapper; |
| 6 | 6 | ||
| 7 | @Mapper | 7 | @Mapper |
| 8 | -public interface SysSmsLogMapper extends BaseMapper<SysSmsLogDO> { | 8 | +public interface SysSmsSendLogMapper extends BaseMapper<SysSmsSendLogDO> { |
| 9 | 9 | ||
| 10 | } | 10 | } |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsChannelDO.java
| @@ -28,6 +28,16 @@ public class SysSmsChannelDO extends BaseDO { | @@ -28,6 +28,16 @@ public class SysSmsChannelDO extends BaseDO { | ||
| 28 | private String code; | 28 | private String code; |
| 29 | 29 | ||
| 30 | /** | 30 | /** |
| 31 | + * 是否拥有回答(0否 1是) | ||
| 32 | + */ | ||
| 33 | + private Integer had_callback; | ||
| 34 | + | ||
| 35 | + /** | ||
| 36 | + * 短信发送回调url | ||
| 37 | + */ | ||
| 38 | + private String callback_url; | ||
| 39 | + | ||
| 40 | + /** | ||
| 31 | * 渠道账号id | 41 | * 渠道账号id |
| 32 | */ | 42 | */ |
| 33 | private String apiKey; | 43 | private String apiKey; |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsLogDO.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsQueryLogDO.java
| 1 | package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms; | 1 | package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms; |
| 2 | 2 | ||
| 3 | +import com.baomidou.mybatisplus.annotation.TableField; | ||
| 3 | import com.baomidou.mybatisplus.annotation.TableName; | 4 | import com.baomidou.mybatisplus.annotation.TableName; |
| 5 | +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | ||
| 4 | import lombok.Data; | 6 | import lombok.Data; |
| 5 | import lombok.EqualsAndHashCode; | 7 | import lombok.EqualsAndHashCode; |
| 6 | import lombok.experimental.Accessors; | 8 | import lombok.experimental.Accessors; |
| 7 | 9 | ||
| 8 | import java.io.Serializable; | 10 | import java.io.Serializable; |
| 9 | import java.util.Date; | 11 | import java.util.Date; |
| 12 | +import java.util.List; | ||
| 10 | 13 | ||
| 11 | /** | 14 | /** |
| 12 | * 短信日志 | 15 | * 短信日志 |
| @@ -17,8 +20,8 @@ import java.util.Date; | @@ -17,8 +20,8 @@ import java.util.Date; | ||
| 17 | @Data | 20 | @Data |
| 18 | @EqualsAndHashCode | 21 | @EqualsAndHashCode |
| 19 | @Accessors(chain = true) | 22 | @Accessors(chain = true) |
| 20 | -@TableName(value = "sms_log", autoResultMap = true) | ||
| 21 | -public class SysSmsLogDO implements Serializable { | 23 | +@TableName(value = "sms_query_log", autoResultMap = true) |
| 24 | +public class SysSmsQueryLogDO implements Serializable { | ||
| 22 | 25 | ||
| 23 | /** | 26 | /** |
| 24 | * 自增编号 | 27 | * 自增编号 |
| @@ -41,9 +44,10 @@ public class SysSmsLogDO implements Serializable { | @@ -41,9 +44,10 @@ public class SysSmsLogDO implements Serializable { | ||
| 41 | private String templateCode; | 44 | private String templateCode; |
| 42 | 45 | ||
| 43 | /** | 46 | /** |
| 44 | - * 手机号(数组json字符串) | 47 | + * 手机号 |
| 45 | */ | 48 | */ |
| 46 | - private String phones; | 49 | + @TableField(typeHandler = JacksonTypeHandler.class) |
| 50 | + private List<String> phones; | ||
| 47 | 51 | ||
| 48 | /** | 52 | /** |
| 49 | * 内容 | 53 | * 内容 |
| @@ -51,17 +55,34 @@ public class SysSmsLogDO implements Serializable { | @@ -51,17 +55,34 @@ public class SysSmsLogDO implements Serializable { | ||
| 51 | private String content; | 55 | private String content; |
| 52 | 56 | ||
| 53 | /** | 57 | /** |
| 54 | - * 备注 | 58 | + * 发送状态 |
| 59 | + * | ||
| 60 | + * @see cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum | ||
| 55 | */ | 61 | */ |
| 56 | - private String remark; | 62 | + private Integer sendStatus; |
| 57 | 63 | ||
| 58 | /** | 64 | /** |
| 59 | - * 发送状态(1异步推送中 2发送中 3失败 4成功) | 65 | + * 是否获取过结果[0否 1是] |
| 60 | */ | 66 | */ |
| 61 | - private Integer sendStatus; | 67 | + private Integer gotResult; |
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * 是否拥有回调函数(0否 1是) | ||
| 71 | + */ | ||
| 72 | + private Integer hadCallback; | ||
| 73 | + | ||
| 74 | + /** | ||
| 75 | + * 结果(对象json字符串) | ||
| 76 | + */ | ||
| 77 | + private String sendResultParam; | ||
| 78 | + | ||
| 79 | + /** | ||
| 80 | + * 备注 | ||
| 81 | + */ | ||
| 82 | + private String remark; | ||
| 62 | 83 | ||
| 63 | /** | 84 | /** |
| 64 | - * 创建者 | 85 | + * 创建人 |
| 65 | */ | 86 | */ |
| 66 | private String createBy; | 87 | private String createBy; |
| 67 | 88 |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsSendLogDO.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; | ||
| 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 SysSmsSendLogDO 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 SmsSendStatusEnum | ||
| 58 | + */ | ||
| 59 | + private Integer sendStatus; | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 发送时间 | ||
| 63 | + */ | ||
| 64 | + private Date sendTime; | ||
| 65 | + | ||
| 66 | +} |
src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java
| @@ -13,20 +13,26 @@ import lombok.Getter; | @@ -13,20 +13,26 @@ import lombok.Getter; | ||
| 13 | @AllArgsConstructor | 13 | @AllArgsConstructor |
| 14 | public enum SmsSendStatusEnum { | 14 | public enum SmsSendStatusEnum { |
| 15 | 15 | ||
| 16 | + //请求发送结果时失败 | ||
| 17 | + QUERY_SEND_FAIL(-3), | ||
| 18 | + | ||
| 19 | + //短信发送失败 | ||
| 20 | + SEND_FAIL(-2), | ||
| 21 | + | ||
| 22 | + //短信请求失败 | ||
| 23 | + QUERY_FAIL(-1), | ||
| 24 | + | ||
| 16 | //异步转发中 | 25 | //异步转发中 |
| 17 | - ASYNC(1), | 26 | + ASYNC(0), |
| 18 | 27 | ||
| 19 | - //发送中 | ||
| 20 | - SENDING(2), | 28 | + //请求成功 |
| 29 | + QUERY_SUCCESS(1), | ||
| 21 | 30 | ||
| 22 | - //失败 | ||
| 23 | - FAIL(3), | 31 | + //短信成功 |
| 32 | + SEND_SUCCESS(2), | ||
| 24 | 33 | ||
| 25 | //等待回执 | 34 | //等待回执 |
| 26 | - WAITING(4), | ||
| 27 | - | ||
| 28 | - //成功 | ||
| 29 | - SUCCESS(5); | 35 | + WAITING(3); |
| 30 | 36 | ||
| 31 | private final int status; | 37 | private final int status; |
| 32 | 38 |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java
| 1 | package cn.iocoder.dashboard.modules.system.mq.consumer.sms; | 1 | package cn.iocoder.dashboard.modules.system.mq.consumer.sms; |
| 2 | 2 | ||
| 3 | import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; | 3 | import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; |
| 4 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | ||
| 4 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; | 5 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 5 | import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage; | 6 | import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage; |
| 6 | import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage; | 7 | import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage; |
| 7 | -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; | 8 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; |
| 9 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; | ||
| 8 | import lombok.extern.slf4j.Slf4j; | 10 | import lombok.extern.slf4j.Slf4j; |
| 9 | import org.springframework.stereotype.Component; | 11 | import org.springframework.stereotype.Component; |
| 10 | 12 | ||
| @@ -20,12 +22,20 @@ import javax.annotation.Resource; | @@ -20,12 +22,20 @@ import javax.annotation.Resource; | ||
| 20 | public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> { | 22 | public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> { |
| 21 | 23 | ||
| 22 | @Resource | 24 | @Resource |
| 23 | - private SysSmsService sysSmsService; | 25 | + private SysSmsChannelService smsChannelService; |
| 26 | + | ||
| 27 | + @Resource | ||
| 28 | + private SysSmsQueryLogService smsQueryLogService; | ||
| 24 | 29 | ||
| 25 | @Override | 30 | @Override |
| 26 | public void onMessage(SmsSendMessage message) { | 31 | public void onMessage(SmsSendMessage message) { |
| 27 | - log.info("[onMessage][收到 发送短信 消息], content: " + message.toString()); | ||
| 28 | - SmsResult send = sysSmsService.send(message.getSmsBody(), message.getTargetPhones()); | 32 | + log.info("[onMessage][收到 发送短信 消息], content: " + message.toString()); |
| 33 | + AbstractSmsClient smsClient = smsChannelService.getSmsClient(message.getSmsBody().getTemplateCode()); | ||
| 34 | + String templateApiId = smsChannelService.getSmsTemplateApiIdByCode(message.getSmsBody().getTemplateCode()); | ||
| 35 | + | ||
| 36 | + SmsResult result = smsClient.send(templateApiId, message.getSmsBody(), message.getTargetPhones()); | ||
| 37 | + | ||
| 38 | + smsQueryLogService.afterSendLog(message.getSmsBody().getSmsLogId(), result); | ||
| 29 | } | 39 | } |
| 30 | 40 | ||
| 31 | } | 41 | } |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsLogService.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsQueryLogService.java
| @@ -7,26 +7,25 @@ import cn.iocoder.dashboard.framework.sms.core.SmsResult; | @@ -7,26 +7,25 @@ import cn.iocoder.dashboard.framework.sms.core.SmsResult; | ||
| 7 | import java.util.List; | 7 | import java.util.List; |
| 8 | 8 | ||
| 9 | /** | 9 | /** |
| 10 | - * 短信渠道Service接口 | 10 | + * 短信请求日志服务接口 |
| 11 | * | 11 | * |
| 12 | * @author zzf | 12 | * @author zzf |
| 13 | * @date 2021/1/25 9:24 | 13 | * @date 2021/1/25 9:24 |
| 14 | */ | 14 | */ |
| 15 | -public interface SysSmsLogService { | 15 | +public interface SysSmsQueryLogService { |
| 16 | /** | 16 | /** |
| 17 | * 发送短信前的日志处理 | 17 | * 发送短信前的日志处理 |
| 18 | * | 18 | * |
| 19 | * @param smsBody 短信内容 | 19 | * @param smsBody 短信内容 |
| 20 | * @param targetPhones 发送对象手机号集合 | 20 | * @param targetPhones 发送对象手机号集合 |
| 21 | * @param client 短信客户端 | 21 | * @param client 短信客户端 |
| 22 | - * @param isAsync 是否异步发送 | ||
| 23 | * @return 生成的日志id | 22 | * @return 生成的日志id |
| 24 | */ | 23 | */ |
| 25 | // TODO FROM 芋艿 to ZZF: async 是针对发送的方式,对于日志不一定需要关心。这样,短信日志,实际就发送前插入,发送后更新结果. | 24 | // TODO FROM 芋艿 to ZZF: async 是针对发送的方式,对于日志不一定需要关心。这样,短信日志,实际就发送前插入,发送后更新结果. |
| 26 | // 这里只用于记录状态,毕竟异步可能推送失败,此时日志可记录该状态。 | 25 | // 这里只用于记录状态,毕竟异步可能推送失败,此时日志可记录该状态。 |
| 27 | 26 | ||
| 28 | // TODO FROM 芋艿 to ZZF:短信日志,群发的情况,应该是每个手机一条哈。虽然是群发,但是可能部分成功,部分失败;对应到短信平台,实际也是多条。 | 27 | // TODO FROM 芋艿 to ZZF:短信日志,群发的情况,应该是每个手机一条哈。虽然是群发,但是可能部分成功,部分失败;对应到短信平台,实际也是多条。 |
| 29 | - Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient client, Boolean isAsync); | 28 | + void beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient client); |
| 30 | 29 | ||
| 31 | /** | 30 | /** |
| 32 | * 发送消息后的日志处理 | 31 | * 发送消息后的日志处理 |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsSendLogService.java
0 → 100644
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsService.java
| 1 | package cn.iocoder.dashboard.modules.system.service.sms; | 1 | package cn.iocoder.dashboard.modules.system.service.sms; |
| 2 | 2 | ||
| 3 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; | 3 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 4 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | ||
| 5 | -import org.apache.commons.lang3.StringUtils; | ||
| 6 | 4 | ||
| 5 | +import javax.servlet.ServletRequest; | ||
| 7 | import java.util.Arrays; | 6 | import java.util.Arrays; |
| 8 | import java.util.Collections; | 7 | import java.util.Collections; |
| 9 | import java.util.List; | 8 | import java.util.List; |
| 10 | 9 | ||
| 11 | /** | 10 | /** |
| 12 | * 短信Service接口 | 11 | * 短信Service接口 |
| 12 | + * 只支持异步,因此没有返回值 | ||
| 13 | * | 13 | * |
| 14 | * @author zzf | 14 | * @author zzf |
| 15 | * @date 2021/1/25 9:24 | 15 | * @date 2021/1/25 9:24 |
| @@ -21,23 +21,17 @@ public interface SysSmsService { | @@ -21,23 +21,17 @@ public interface SysSmsService { | ||
| 21 | * | 21 | * |
| 22 | * @param smsBody 消息内容 | 22 | * @param smsBody 消息内容 |
| 23 | * @param targetPhones 发送对象手机号列表 | 23 | * @param targetPhones 发送对象手机号列表 |
| 24 | - * @return 是否发送成功 | ||
| 25 | */ | 24 | */ |
| 26 | - SmsResult send(SmsBody smsBody, List<String> targetPhones); | 25 | + void send(SmsBody smsBody, List<String> targetPhones); |
| 27 | 26 | ||
| 28 | /** | 27 | /** |
| 29 | * 发送消息 | 28 | * 发送消息 |
| 30 | * | 29 | * |
| 31 | * @param smsBody 消息内容 | 30 | * @param smsBody 消息内容 |
| 32 | * @param targetPhone 发送对象手机号 | 31 | * @param targetPhone 发送对象手机号 |
| 33 | - * @return 是否发送成功 | ||
| 34 | */ | 32 | */ |
| 35 | - default SmsResult send(SmsBody smsBody, String targetPhone) { | ||
| 36 | - if (StringUtils.isBlank(targetPhone)) { | ||
| 37 | - return failResult("targetPhone must not null."); | ||
| 38 | - } | ||
| 39 | - | ||
| 40 | - return send(smsBody, Collections.singletonList(targetPhone)); | 33 | + default void send(SmsBody smsBody, String targetPhone) { |
| 34 | + send(smsBody, Collections.singletonList(targetPhone)); | ||
| 41 | } | 35 | } |
| 42 | 36 | ||
| 43 | /** | 37 | /** |
| @@ -45,57 +39,16 @@ public interface SysSmsService { | @@ -45,57 +39,16 @@ public interface SysSmsService { | ||
| 45 | * | 39 | * |
| 46 | * @param smsBody 消息内容 | 40 | * @param smsBody 消息内容 |
| 47 | * @param targetPhones 发送对象手机号数组 | 41 | * @param targetPhones 发送对象手机号数组 |
| 48 | - * @return 是否发送成功 | ||
| 49 | */ | 42 | */ |
| 50 | - default SmsResult send(SmsBody smsBody, String... targetPhones) { | ||
| 51 | - if (targetPhones == null) { | ||
| 52 | - return failResult("targetPhones must not null."); | ||
| 53 | - } | ||
| 54 | - | ||
| 55 | - return send(smsBody, Arrays.asList(targetPhones)); | 43 | + default void send(SmsBody smsBody, String... targetPhones) { |
| 44 | + send(smsBody, Arrays.asList(targetPhones)); | ||
| 56 | } | 45 | } |
| 57 | 46 | ||
| 58 | - | ||
| 59 | /** | 47 | /** |
| 60 | - * 异步发送消息 | 48 | + * 处理短信发送回调函数 |
| 61 | * | 49 | * |
| 62 | - * @param msgBody 消息内容 | ||
| 63 | - * @param targetPhones 发送对象列表 | 50 | + * @param request 请求 |
| 51 | + * @return 响应数据 | ||
| 64 | */ | 52 | */ |
| 65 | - void sendAsync(SmsBody msgBody, List<String> targetPhones); | ||
| 66 | - | ||
| 67 | - /** | ||
| 68 | - * 异步发送消息 | ||
| 69 | - * | ||
| 70 | - * @param msgBody 消息内容 | ||
| 71 | - * @param targetPhone 发送对象 | ||
| 72 | - */ | ||
| 73 | - default void sendAsync(SmsBody msgBody, String targetPhone) { | ||
| 74 | - if (StringUtils.isBlank(targetPhone)) { | ||
| 75 | - return; | ||
| 76 | - } | ||
| 77 | - sendAsync(msgBody, Collections.singletonList(targetPhone)); | ||
| 78 | - } | ||
| 79 | - | ||
| 80 | - /** | ||
| 81 | - * 异步发送消息 | ||
| 82 | - * | ||
| 83 | - * @param msgBody 消息内容 | ||
| 84 | - * @param targetPhones 发送对象列表 | ||
| 85 | - */ | ||
| 86 | - default void sendAsync(SmsBody msgBody, String... targetPhones) { | ||
| 87 | - if (targetPhones == null) { | ||
| 88 | - return; | ||
| 89 | - } | ||
| 90 | - sendAsync(msgBody, Arrays.asList(targetPhones)); | ||
| 91 | - } | ||
| 92 | - | ||
| 93 | - | ||
| 94 | - default SmsResult failResult(String message) { | ||
| 95 | - SmsResult resultBody = new SmsResult(); | ||
| 96 | - resultBody.setSuccess(false); | ||
| 97 | - resultBody.setMessage(message); | ||
| 98 | - return resultBody; | ||
| 99 | - } | ||
| 100 | - | 53 | + Object smsSendCallbackHandle(ServletRequest request); |
| 101 | } | 54 | } |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsLogServiceImpl.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsQueryLogServiceImpl.java
| @@ -4,10 +4,10 @@ import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | @@ -4,10 +4,10 @@ import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | ||
| 4 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; | 4 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 5 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; | 5 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 6 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; | 6 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; |
| 7 | -import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsLogMapper; | ||
| 8 | -import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsLogDO; | 7 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsQueryLogMapper; |
| 8 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsQueryLogDO; | ||
| 9 | import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; | 9 | import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; |
| 10 | -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService; | 10 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; |
| 11 | import cn.iocoder.dashboard.util.json.JsonUtils; | 11 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 12 | import org.springframework.stereotype.Service; | 12 | import org.springframework.stereotype.Service; |
| 13 | 13 | ||
| @@ -15,61 +15,43 @@ import javax.annotation.Resource; | @@ -15,61 +15,43 @@ import javax.annotation.Resource; | ||
| 15 | import java.util.List; | 15 | import java.util.List; |
| 16 | 16 | ||
| 17 | /** | 17 | /** |
| 18 | - * 短信日志Service实现类 | 18 | + * 短信请求日志服务实现类 |
| 19 | * | 19 | * |
| 20 | * @author zzf | 20 | * @author zzf |
| 21 | - * @date 2021/1/25 9:25 | 21 | + * @date 13:50 2021/3/2 |
| 22 | */ | 22 | */ |
| 23 | @Service | 23 | @Service |
| 24 | -public class SysSmsLogServiceImpl implements SysSmsLogService { | 24 | +public class SysSmsQueryLogServiceImpl implements SysSmsQueryLogService { |
| 25 | 25 | ||
| 26 | @Resource | 26 | @Resource |
| 27 | - private SysSmsLogMapper logMapper; | 27 | + private SysSmsQueryLogMapper logMapper; |
| 28 | 28 | ||
| 29 | @Override | 29 | @Override |
| 30 | - public Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient client, Boolean isAsync) { | ||
| 31 | - SysSmsLogDO smsLog = new SysSmsLogDO(); | ||
| 32 | - if (smsBody.getSmsLogId() != null) { | ||
| 33 | - smsLog.setId(smsBody.getSmsLogId()); | ||
| 34 | - smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus()); | ||
| 35 | - logMapper.updateById(smsLog); | ||
| 36 | - return smsBody.getSmsLogId(); | ||
| 37 | - } else { | ||
| 38 | - SmsChannelProperty property = client.getProperty(); | ||
| 39 | - | ||
| 40 | - smsLog.setChannelCode(property.getCode()) | ||
| 41 | - .setChannelId(property.getId()) | ||
| 42 | - .setTemplateCode(smsBody.getTemplateCode()) | ||
| 43 | - .setPhones(JsonUtils.toJsonString(targetPhones)) | ||
| 44 | - .setContent(smsBody.getParams().toString()); | ||
| 45 | - | ||
| 46 | - if (isAsync) { | ||
| 47 | - smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus()); | ||
| 48 | - } else { | ||
| 49 | - smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus()); | ||
| 50 | - } | ||
| 51 | - logMapper.insert(smsLog); | ||
| 52 | - return smsLog.getId(); | ||
| 53 | - } | 30 | + public void beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient client) { |
| 31 | + SysSmsQueryLogDO smsLog = new SysSmsQueryLogDO(); | ||
| 32 | + SmsChannelProperty property = client.getProperty(); | ||
| 33 | + | ||
| 34 | + smsLog.setChannelCode(property.getCode()) | ||
| 35 | + .setChannelId(property.getId()) | ||
| 36 | + .setTemplateCode(smsBody.getTemplateCode()) | ||
| 37 | + .setPhones(targetPhones) | ||
| 38 | + .setContent(smsBody.getParams().toString()); | ||
| 39 | + | ||
| 40 | + smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus()); | ||
| 41 | + logMapper.insert(smsLog); | ||
| 42 | + smsBody.setSmsLogId(smsLog.getId()); | ||
| 54 | } | 43 | } |
| 55 | 44 | ||
| 56 | @Override | 45 | @Override |
| 57 | public void afterSendLog(Long logId, SmsResult result) { | 46 | public void afterSendLog(Long logId, SmsResult result) { |
| 58 | - SysSmsLogDO smsLog = new SysSmsLogDO(); | 47 | + SysSmsQueryLogDO smsLog = new SysSmsQueryLogDO(); |
| 59 | smsLog.setId(logId); | 48 | smsLog.setId(logId); |
| 60 | if (result.getSuccess()) { | 49 | if (result.getSuccess()) { |
| 61 | - smsLog.setSendStatus(SmsSendStatusEnum.SUCCESS.getStatus()); | ||
| 62 | - SysSmsLogDO smsLogDO = logMapper.selectById(logId); | ||
| 63 | - result.getResult().forEach(s -> { | ||
| 64 | - smsLogDO.setPhones(s.getPhone()); | ||
| 65 | - smsLogDO.setSendStatus(s.getStatus()); | ||
| 66 | - smsLogDO.setRemark(s.getMessage()); | ||
| 67 | - smsLogDO.setCreateTime(s.getCreateTime()); | ||
| 68 | - logMapper.insert(smsLogDO); | ||
| 69 | - }); | 50 | + smsLog.setSendStatus(SmsSendStatusEnum.QUERY_SUCCESS.getStatus()); |
| 51 | + smsLog.setSendResultParam(result.getSendResultParam()); | ||
| 70 | } else { | 52 | } else { |
| 71 | - smsLog.setSendStatus(SmsSendStatusEnum.FAIL.getStatus()); | ||
| 72 | - smsLog.setRemark(result.getMessage() + JsonUtils.toJsonString(result.getResult())); | 53 | + smsLog.setSendStatus(SmsSendStatusEnum.QUERY_FAIL.getStatus()); |
| 54 | + smsLog.setRemark(result.getMessage()); | ||
| 73 | } | 55 | } |
| 74 | logMapper.updateById(smsLog); | 56 | logMapper.updateById(smsLog); |
| 75 | } | 57 | } |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms.impl; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollectionUtil; | ||
| 4 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | ||
| 5 | +import cn.iocoder.dashboard.framework.sms.client.NeedQuerySendResultSmsClient; | ||
| 6 | +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | ||
| 7 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsQueryLogMapper; | ||
| 8 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsSendLogMapper; | ||
| 9 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsQueryLogDO; | ||
| 10 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsSendLogDO; | ||
| 11 | +import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; | ||
| 12 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; | ||
| 13 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsSendLogService; | ||
| 14 | +import lombok.extern.slf4j.Slf4j; | ||
| 15 | +import org.springframework.scheduling.annotation.Scheduled; | ||
| 16 | +import org.springframework.stereotype.Service; | ||
| 17 | + | ||
| 18 | +import javax.annotation.Resource; | ||
| 19 | +import java.util.List; | ||
| 20 | + | ||
| 21 | +/** | ||
| 22 | + * 短信发送日志服务实现类 | ||
| 23 | + * | ||
| 24 | + * @author zzf | ||
| 25 | + * @date 2021/1/25 9:25 | ||
| 26 | + */ | ||
| 27 | +@Slf4j | ||
| 28 | +@Service | ||
| 29 | +public class SysSmsSendLogServiceImpl implements SysSmsSendLogService { | ||
| 30 | + | ||
| 31 | + @Resource | ||
| 32 | + private SysSmsQueryLogMapper smsQueryLogMapper; | ||
| 33 | + | ||
| 34 | + @Resource | ||
| 35 | + private SysSmsSendLogMapper smsSendLogMapper; | ||
| 36 | + | ||
| 37 | + @Resource | ||
| 38 | + private SysSmsChannelService smsChannelService; | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 定时执行 {@link #getSmsSendResultJob()} 的周期 | ||
| 42 | + */ | ||
| 43 | + private static final long SCHEDULER_PERIOD = 5 * 60 * 1000L; | ||
| 44 | + | ||
| 45 | + | ||
| 46 | + @Override | ||
| 47 | + public void getAndSaveSmsSendLog() { | ||
| 48 | + | ||
| 49 | + List<SysSmsQueryLogDO> noResultQueryLogList = smsQueryLogMapper.selectNoResultQueryLogList(); | ||
| 50 | + | ||
| 51 | + if (CollectionUtil.isEmpty(noResultQueryLogList)) { | ||
| 52 | + return; | ||
| 53 | + } | ||
| 54 | + //用于添加的发送日志对象 | ||
| 55 | + SysSmsSendLogDO insertSendLog = new SysSmsSendLogDO(); | ||
| 56 | + //用于修改状态的请求日志对象 | ||
| 57 | + SysSmsQueryLogDO updateQueryLog = new SysSmsQueryLogDO(); | ||
| 58 | + | ||
| 59 | + noResultQueryLogList.forEach(queryLog -> { | ||
| 60 | + AbstractSmsClient smsClient = smsChannelService.getSmsClient(queryLog.getTemplateCode()); | ||
| 61 | + | ||
| 62 | + updateQueryLog.setId(queryLog.getId()); | ||
| 63 | + | ||
| 64 | + // 只处理实现了获取发送结果方法的短信客户端,理论上这里都是满足条件的,以防万一加个判断。 | ||
| 65 | + if (smsClient instanceof NeedQuerySendResultSmsClient) { | ||
| 66 | + //初始化点字段值 | ||
| 67 | + queryLog2SendLong(insertSendLog, queryLog); | ||
| 68 | + | ||
| 69 | + NeedQuerySendResultSmsClient querySendResultSmsClient = (NeedQuerySendResultSmsClient) smsClient; | ||
| 70 | + try { | ||
| 71 | + List<SmsResultDetail> smsSendResult = querySendResultSmsClient.getSmsSendResult(queryLog.getRemark()); | ||
| 72 | + smsSendResult.forEach(resultDetail -> { | ||
| 73 | + insertSendLog.setPhone(resultDetail.getPhone()); | ||
| 74 | + insertSendLog.setSendStatus(resultDetail.getSendStatus()); | ||
| 75 | + insertSendLog.setSendTime(resultDetail.getSendTime()); | ||
| 76 | + insertSendLog.setRemark(resultDetail.getMessage()); | ||
| 77 | + smsSendLogMapper.insert(insertSendLog); | ||
| 78 | + }); | ||
| 79 | + } catch (Exception e) { | ||
| 80 | + //exception handle | ||
| 81 | + log.error("query send result fail, exception: " + e.getMessage()); | ||
| 82 | + | ||
| 83 | + updateQueryLog.setSendStatus(SmsSendStatusEnum.QUERY_SEND_FAIL.getStatus()); | ||
| 84 | + updateQueryLog.setRemark(e.getMessage()); | ||
| 85 | + smsQueryLogMapper.updateById(updateQueryLog); | ||
| 86 | + return; | ||
| 87 | + } | ||
| 88 | + } else { | ||
| 89 | + //理论上这里都是满足条件的,以防万一加个判断。 | ||
| 90 | + updateQueryLog.setSendStatus(SmsSendStatusEnum.QUERY_SEND_FAIL.getStatus()); | ||
| 91 | + smsQueryLogMapper.updateById(updateQueryLog); | ||
| 92 | + } | ||
| 93 | + updateQueryLog.setSendStatus(SmsSendStatusEnum.SEND_SUCCESS.getStatus()); | ||
| 94 | + updateQueryLog.setRemark(String.format("日志(id = %s)对应的客户端没有继承NeedQuerySendResultSmsClient, 不能获取短信结果。", queryLog.getId())); | ||
| 95 | + smsQueryLogMapper.updateById(updateQueryLog); | ||
| 96 | + }); | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + private void queryLog2SendLong(SysSmsSendLogDO insertSendLog, SysSmsQueryLogDO queryLog) { | ||
| 100 | + insertSendLog.setChannelCode(queryLog.getChannelCode()); | ||
| 101 | + insertSendLog.setChannelId(queryLog.getChannelId()); | ||
| 102 | + insertSendLog.setTemplateCode(queryLog.getTemplateCode()); | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) | ||
| 106 | + public void getSmsSendResultJob() { | ||
| 107 | + getAndSaveSmsSendLog(); | ||
| 108 | + } | ||
| 109 | +} |
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; | @@ -2,11 +2,11 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | 3 | import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; |
| 4 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; | 4 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 5 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | ||
| 6 | import cn.iocoder.dashboard.modules.system.mq.producer.sms.SmsProducer; | 5 | import cn.iocoder.dashboard.modules.system.mq.producer.sms.SmsProducer; |
| 7 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; | 6 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; |
| 8 | -import cn.iocoder.dashboard.modules.system.service.sms.SysSmsLogService; | 7 | +import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; |
| 9 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; | 8 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; |
| 9 | +import org.springframework.scheduling.annotation.Scheduled; | ||
| 10 | import org.springframework.stereotype.Service; | 10 | import org.springframework.stereotype.Service; |
| 11 | 11 | ||
| 12 | import javax.annotation.Resource; | 12 | import javax.annotation.Resource; |
| @@ -25,30 +25,18 @@ public class SysSmsServiceImpl implements SysSmsService { | @@ -25,30 +25,18 @@ public class SysSmsServiceImpl implements SysSmsService { | ||
| 25 | private SysSmsChannelService channelService; | 25 | private SysSmsChannelService channelService; |
| 26 | 26 | ||
| 27 | @Resource | 27 | @Resource |
| 28 | - private SysSmsLogService logService; | 28 | + private SysSmsQueryLogService logService; |
| 29 | 29 | ||
| 30 | @Resource | 30 | @Resource |
| 31 | private SmsProducer smsProducer; | 31 | private SmsProducer smsProducer; |
| 32 | 32 | ||
| 33 | @Override | 33 | @Override |
| 34 | - public SmsResult send(SmsBody smsBody, List<String> targetPhones) { | 34 | + public void send(SmsBody smsBody, List<String> targetPhones) { |
| 35 | AbstractSmsClient client = channelService.getSmsClient(smsBody.getTemplateCode()); | 35 | AbstractSmsClient client = channelService.getSmsClient(smsBody.getTemplateCode()); |
| 36 | - String templateApiId = channelService.getSmsTemplateApiIdByCode(smsBody.getTemplateCode()); | ||
| 37 | - Long logId = logService.beforeSendLog(smsBody, targetPhones, client, false); | ||
| 38 | - | ||
| 39 | - SmsResult result = client.send(templateApiId, smsBody, targetPhones); | ||
| 40 | - | ||
| 41 | - logService.afterSendLog(logId, result); | ||
| 42 | - | ||
| 43 | - return result; | ||
| 44 | - } | ||
| 45 | - | ||
| 46 | - // TODO FROM 芋艿 to ZZF:可能要讨论下,对于短信发送来说,貌似只提供异步发送即可。对于业务来说,一定不能依赖短信的发送结果。 | ||
| 47 | - // 我的想法是1、很多短信,比如验证码,总还是需要知道是否发送成功的。2、别人可以不用,我们不能没有。3、实现挺简单的,个人觉得无需纠结。 | ||
| 48 | - @Override | ||
| 49 | - public void sendAsync(SmsBody smsBody, List<String> targetPhones) { | ||
| 50 | - AbstractSmsClient client = channelService.getSmsClient(smsBody.getTemplateCode()); | ||
| 51 | - logService.beforeSendLog(smsBody, targetPhones, client, true); | 36 | + logService.beforeSendLog(smsBody, targetPhones, client); |
| 52 | smsProducer.sendSmsSendMessage(smsBody, targetPhones); | 37 | smsProducer.sendSmsSendMessage(smsBody, targetPhones); |
| 53 | } | 38 | } |
| 39 | + | ||
| 40 | + // TODO FROM 芋艿 to ZZF:可能要讨论下,对于短信发送来说,貌似只提供异步发送即可。对于业务来说,一定不能依赖短信的发送结果. | ||
| 41 | + | ||
| 54 | } | 42 | } |
src/main/java/cn/iocoder/dashboard/util/json/JsonUtils.java
| @@ -20,7 +20,7 @@ public class JsonUtils { | @@ -20,7 +20,7 @@ public class JsonUtils { | ||
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * 初始化 objectMapper 属性 | 22 | * 初始化 objectMapper 属性 |
| 23 | - * | 23 | + * <p> |
| 24 | * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean | 24 | * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean |
| 25 | * | 25 | * |
| 26 | * @param objectMapper ObjectMapper 对象 | 26 | * @param objectMapper ObjectMapper 对象 |
| @@ -67,4 +67,12 @@ public class JsonUtils { | @@ -67,4 +67,12 @@ public class JsonUtils { | ||
| 67 | } | 67 | } |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | + public static <T> T parseByType(String text, TypeReference<T> typeReference) { | ||
| 71 | + try { | ||
| 72 | + return objectMapper.readValue(text, typeReference); | ||
| 73 | + } catch (IOException e) { | ||
| 74 | + throw new RuntimeException(e); | ||
| 75 | + } | ||
| 76 | + } | ||
| 77 | + | ||
| 70 | } | 78 | } |