Commit 767cd90279e2247e704156f7c028b0b97560f663
1 parent
c3372d4b
批量发送半成品
Showing
29 changed files
with
693 additions
and
188 deletions
pom.xml
| ... | ... | @@ -184,12 +184,6 @@ |
| 184 | 184 | </dependency> |
| 185 | 185 | |
| 186 | 186 | <dependency> |
| 187 | - <groupId>org.projectlombok</groupId> | |
| 188 | - <artifactId>lombok</artifactId> | |
| 189 | - <version>${lombok.version}</version> | |
| 190 | - </dependency> | |
| 191 | - | |
| 192 | - <dependency> | |
| 193 | 187 | <groupId>org.mapstruct</groupId> |
| 194 | 188 | <artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher --> |
| 195 | 189 | <version>${mapstruct.version}</version> |
| ... | ... | @@ -222,17 +216,25 @@ |
| 222 | 216 | <version>${easyexcel.verion}</version> |
| 223 | 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 | 227 | <dependency> |
| 226 | 228 | <groupId>com.aliyun</groupId> |
| 227 | 229 | <artifactId>aliyun-java-sdk-core</artifactId> |
| 228 | 230 | <version>4.5.18</version> |
| 229 | 231 | </dependency> |
| 230 | - | |
| 231 | 232 | <dependency> |
| 232 | 233 | <groupId>com.aliyun</groupId> |
| 233 | 234 | <artifactId>aliyun-java-sdk-dysmsapi</artifactId> |
| 234 | 235 | <version>2.1.0</version> |
| 235 | 236 | </dependency> |
| 237 | + <!-- SMS SDK end --> | |
| 236 | 238 | |
| 237 | 239 | </dependencies> |
| 238 | 240 | ... | ... |
sql/sms.sql
| ... | ... | @@ -12,6 +12,8 @@ CREATE TABLE `sms_channel` |
| 12 | 12 | `code` varchar(50) NOT NULL COMMENT '编码(来自枚举类 阿里、华为、七牛等)', |
| 13 | 13 | `api_key` varchar(100) NOT NULL COMMENT '账号id', |
| 14 | 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 | 17 | `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识', |
| 16 | 18 | `name` varchar(50) NOT NULL COMMENT '名称', |
| 17 | 19 | `signature` varchar(50) NOT NULL COMMENT '签名值', |
| ... | ... | @@ -61,22 +63,46 @@ CREATE TABLE `sms_template` |
| 61 | 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 | 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 | 95 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', |
| 70 | 96 | `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)', |
| 71 | 97 | `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id', |
| 72 | 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 | 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 | 105 | PRIMARY KEY (`id`) USING BTREE |
| 80 | 106 | ) ENGINE = InnoDB |
| 81 | 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 | 58 | * @return 短信发送结果 |
| 59 | 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 | 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 | 2 | |
| 3 | 3 | import cn.hutool.core.date.DateUtil; |
| 4 | 4 | import cn.hutool.core.util.ArrayUtil; |
| 5 | -import cn.hutool.core.util.StrUtil; | |
| 6 | 5 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 7 | 6 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 8 | 7 | import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; |
| ... | ... | @@ -14,11 +13,11 @@ import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest; |
| 14 | 13 | import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse; |
| 15 | 14 | import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; |
| 16 | 15 | import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; |
| 16 | +import com.aliyuncs.exceptions.ClientException; | |
| 17 | 17 | import com.aliyuncs.http.MethodType; |
| 18 | 18 | import com.aliyuncs.profile.DefaultProfile; |
| 19 | 19 | import com.aliyuncs.profile.IClientProfile; |
| 20 | 20 | import lombok.extern.slf4j.Slf4j; |
| 21 | -import org.apache.commons.lang3.StringUtils; | |
| 22 | 21 | |
| 23 | 22 | import java.util.ArrayList; |
| 24 | 23 | import java.util.Collection; |
| ... | ... | @@ -31,7 +30,7 @@ import java.util.List; |
| 31 | 30 | * @date 2021/1/25 14:17 |
| 32 | 31 | */ |
| 33 | 32 | @Slf4j |
| 34 | -public class AliyunSmsClient extends AbstractSmsClient { | |
| 33 | +public class AliyunSmsClient extends AbstractSmsClient implements NeedQuerySendResultSmsClient { | |
| 35 | 34 | |
| 36 | 35 | private static final String OK = "OK"; |
| 37 | 36 | |
| ... | ... | @@ -70,35 +69,43 @@ public class AliyunSmsClient extends AbstractSmsClient { |
| 70 | 69 | request.setTemplateParam(smsBody.getParamsStr()); |
| 71 | 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 | 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 | 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 | 90 | QuerySendDetailsResponse acsResponse = acsClient.getAcsResponse(querySendDetailsRequest); |
| 83 | 91 | List<SmsResultDetail> resultDetailList = new ArrayList<>(Integer.parseInt(acsResponse.getTotalCount())); |
| 84 | 92 | acsResponse.getSmsSendDetailDTOs().forEach(s -> { |
| 85 | 93 | SmsResultDetail resultDetail = new SmsResultDetail(); |
| 86 | - resultDetail.setCreateTime(DateUtil.parseDateTime(s.getSendDate())); | |
| 94 | + resultDetail.setSendTime(DateUtil.parseDateTime(s.getSendDate())); | |
| 87 | 95 | resultDetail.setMessage(s.getContent()); |
| 88 | 96 | resultDetail.setPhone(s.getPhoneNum()); |
| 89 | - resultDetail.setStatus(statusConvert(s.getSendStatus())); | |
| 97 | + resultDetail.setSendStatus(statusConvert(s.getSendStatus())); | |
| 90 | 98 | resultDetailList.add(resultDetail); |
| 91 | 99 | }); |
| 92 | - resultBody.setResult(resultDetailList); | |
| 93 | - return resultBody; | |
| 100 | + return resultDetailList; | |
| 94 | 101 | } |
| 95 | 102 | |
| 96 | 103 | private int statusConvert(Long aliSendStatus) { |
| 97 | 104 | if (aliSendStatus == 1L) { |
| 98 | - return SmsSendStatusEnum.SUCCESS.getStatus(); | |
| 105 | + return SmsSendStatusEnum.SEND_SUCCESS.getStatus(); | |
| 99 | 106 | } |
| 100 | 107 | if (aliSendStatus == 2L) { |
| 101 | - return SmsSendStatusEnum.FAIL.getStatus(); | |
| 108 | + return SmsSendStatusEnum.SEND_FAIL.getStatus(); | |
| 102 | 109 | } |
| 103 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 4 | 4 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | |
| 5 | 6 | |
| 6 | 7 | import java.util.Collection; |
| 8 | +import java.util.List; | |
| 7 | 9 | |
| 8 | 10 | /** |
| 9 | 11 | * 短信父接口 |
| ... | ... | @@ -23,4 +25,7 @@ public interface SmsClient { |
| 23 | 25 | */ |
| 24 | 26 | SmsResult send(String templateApiId, SmsBody smsBody, Collection<String> targets); |
| 25 | 27 | |
| 28 | + | |
| 29 | + //List<SmsResultDetail> getSmsSendResult(String jsonObjectParam); | |
| 30 | + | |
| 26 | 31 | } |
| 27 | 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 | 4 | import lombok.Data; |
| 5 | 5 | |
| 6 | 6 | import java.util.Map; |
| 7 | -import java.util.UUID; | |
| 8 | 7 | |
| 9 | 8 | /** |
| 10 | 9 | * 消息内容实体类 |
| ... | ... | @@ -23,6 +22,11 @@ public class SmsBody { |
| 23 | 22 | private String templateCode; |
| 24 | 23 | |
| 25 | 24 | /** |
| 25 | + * 模板编码 | |
| 26 | + */ | |
| 27 | + private String templateContent; | |
| 28 | + | |
| 29 | + /** | |
| 26 | 30 | * 参数列表 |
| 27 | 31 | */ |
| 28 | 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 | 1 | package cn.iocoder.dashboard.framework.sms.core; |
| 2 | 2 | |
| 3 | 3 | import lombok.Data; |
| 4 | +import lombok.experimental.Accessors; | |
| 4 | 5 | |
| 5 | 6 | import java.io.Serializable; |
| 6 | -import java.util.List; | |
| 7 | 7 | |
| 8 | 8 | /** |
| 9 | 9 | * 消息内容实体类 |
| 10 | 10 | */ |
| 11 | 11 | @Data |
| 12 | +@Accessors(chain = true) | |
| 12 | 13 | public class SmsResult implements Serializable { |
| 13 | 14 | |
| 14 | 15 | /** |
| ... | ... | @@ -17,6 +18,11 @@ public class SmsResult implements Serializable { |
| 17 | 18 | private Boolean success; |
| 18 | 19 | |
| 19 | 20 | /** |
| 21 | + * 第三方唯一标识 | |
| 22 | + */ | |
| 23 | + private String apiId; | |
| 24 | + | |
| 25 | + /** | |
| 20 | 26 | * 状态码 |
| 21 | 27 | */ |
| 22 | 28 | private String code; |
| ... | ... | @@ -27,10 +33,9 @@ public class SmsResult implements Serializable { |
| 27 | 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 | 40 | public static SmsResult failResult(String message) { |
| 36 | 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 | 14 | /** |
| 15 | 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 | 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 | 54 | @NotEmpty(message = "签名值不能为空") |
| 55 | 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 | 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 | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| 5 | 5 | import org.apache.ibatis.annotations.Mapper; |
| 6 | 6 | |
| 7 | 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 | 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 | 41 | * 渠道账号id |
| 32 | 42 | */ |
| 33 | 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 | 1 | package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms; |
| 2 | 2 | |
| 3 | +import com.baomidou.mybatisplus.annotation.TableField; | |
| 3 | 4 | import com.baomidou.mybatisplus.annotation.TableName; |
| 5 | +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | |
| 4 | 6 | import lombok.Data; |
| 5 | 7 | import lombok.EqualsAndHashCode; |
| 6 | 8 | import lombok.experimental.Accessors; |
| 7 | 9 | |
| 8 | 10 | import java.io.Serializable; |
| 9 | 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 | 20 | @Data |
| 18 | 21 | @EqualsAndHashCode |
| 19 | 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 | 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 | 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 | 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 | 13 | @AllArgsConstructor |
| 14 | 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 | 37 | private final int status; |
| 32 | 38 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java
| 1 | 1 | package cn.iocoder.dashboard.modules.system.mq.consumer.sms; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; |
| 4 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | |
| 4 | 5 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 5 | 6 | import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage; |
| 6 | 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 | 10 | import lombok.extern.slf4j.Slf4j; |
| 9 | 11 | import org.springframework.stereotype.Component; |
| 10 | 12 | |
| ... | ... | @@ -20,12 +22,20 @@ import javax.annotation.Resource; |
| 20 | 22 | public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> { |
| 21 | 23 | |
| 22 | 24 | @Resource |
| 23 | - private SysSmsService sysSmsService; | |
| 25 | + private SysSmsChannelService smsChannelService; | |
| 26 | + | |
| 27 | + @Resource | |
| 28 | + private SysSmsQueryLogService smsQueryLogService; | |
| 24 | 29 | |
| 25 | 30 | @Override |
| 26 | 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 | 7 | import java.util.List; |
| 8 | 8 | |
| 9 | 9 | /** |
| 10 | - * 短信渠道Service接口 | |
| 10 | + * 短信请求日志服务接口 | |
| 11 | 11 | * |
| 12 | 12 | * @author zzf |
| 13 | 13 | * @date 2021/1/25 9:24 |
| 14 | 14 | */ |
| 15 | -public interface SysSmsLogService { | |
| 15 | +public interface SysSmsQueryLogService { | |
| 16 | 16 | /** |
| 17 | 17 | * 发送短信前的日志处理 |
| 18 | 18 | * |
| 19 | 19 | * @param smsBody 短信内容 |
| 20 | 20 | * @param targetPhones 发送对象手机号集合 |
| 21 | 21 | * @param client 短信客户端 |
| 22 | - * @param isAsync 是否异步发送 | |
| 23 | 22 | * @return 生成的日志id |
| 24 | 23 | */ |
| 25 | 24 | // TODO FROM 芋艿 to ZZF: async 是针对发送的方式,对于日志不一定需要关心。这样,短信日志,实际就发送前插入,发送后更新结果. |
| 26 | 25 | // 这里只用于记录状态,毕竟异步可能推送失败,此时日志可记录该状态。 |
| 27 | 26 | |
| 28 | 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 | 1 | package cn.iocoder.dashboard.modules.system.service.sms; |
| 2 | 2 | |
| 3 | 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 | 6 | import java.util.Arrays; |
| 8 | 7 | import java.util.Collections; |
| 9 | 8 | import java.util.List; |
| 10 | 9 | |
| 11 | 10 | /** |
| 12 | 11 | * 短信Service接口 |
| 12 | + * 只支持异步,因此没有返回值 | |
| 13 | 13 | * |
| 14 | 14 | * @author zzf |
| 15 | 15 | * @date 2021/1/25 9:24 |
| ... | ... | @@ -21,23 +21,17 @@ public interface SysSmsService { |
| 21 | 21 | * |
| 22 | 22 | * @param smsBody 消息内容 |
| 23 | 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 | 30 | * @param smsBody 消息内容 |
| 32 | 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 | 39 | * |
| 46 | 40 | * @param smsBody 消息内容 |
| 47 | 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 | 4 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 5 | 5 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 6 | 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 | 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 | 11 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 12 | 12 | import org.springframework.stereotype.Service; |
| 13 | 13 | |
| ... | ... | @@ -15,61 +15,43 @@ import javax.annotation.Resource; |
| 15 | 15 | import java.util.List; |
| 16 | 16 | |
| 17 | 17 | /** |
| 18 | - * 短信日志Service实现类 | |
| 18 | + * 短信请求日志服务实现类 | |
| 19 | 19 | * |
| 20 | 20 | * @author zzf |
| 21 | - * @date 2021/1/25 9:25 | |
| 21 | + * @date 13:50 2021/3/2 | |
| 22 | 22 | */ |
| 23 | 23 | @Service |
| 24 | -public class SysSmsLogServiceImpl implements SysSmsLogService { | |
| 24 | +public class SysSmsQueryLogServiceImpl implements SysSmsQueryLogService { | |
| 25 | 25 | |
| 26 | 26 | @Resource |
| 27 | - private SysSmsLogMapper logMapper; | |
| 27 | + private SysSmsQueryLogMapper logMapper; | |
| 28 | 28 | |
| 29 | 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 | 45 | @Override |
| 57 | 46 | public void afterSendLog(Long logId, SmsResult result) { |
| 58 | - SysSmsLogDO smsLog = new SysSmsLogDO(); | |
| 47 | + SysSmsQueryLogDO smsLog = new SysSmsQueryLogDO(); | |
| 59 | 48 | smsLog.setId(logId); |
| 60 | 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 | 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 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; |
| 4 | 4 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 5 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 6 | 5 | import cn.iocoder.dashboard.modules.system.mq.producer.sms.SmsProducer; |
| 7 | 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 | 8 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; |
| 9 | +import org.springframework.scheduling.annotation.Scheduled; | |
| 10 | 10 | import org.springframework.stereotype.Service; |
| 11 | 11 | |
| 12 | 12 | import javax.annotation.Resource; |
| ... | ... | @@ -25,30 +25,18 @@ public class SysSmsServiceImpl implements SysSmsService { |
| 25 | 25 | private SysSmsChannelService channelService; |
| 26 | 26 | |
| 27 | 27 | @Resource |
| 28 | - private SysSmsLogService logService; | |
| 28 | + private SysSmsQueryLogService logService; | |
| 29 | 29 | |
| 30 | 30 | @Resource |
| 31 | 31 | private SmsProducer smsProducer; |
| 32 | 32 | |
| 33 | 33 | @Override |
| 34 | - public SmsResult send(SmsBody smsBody, List<String> targetPhones) { | |
| 34 | + public void send(SmsBody smsBody, List<String> targetPhones) { | |
| 35 | 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 | 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 | 20 | |
| 21 | 21 | /** |
| 22 | 22 | * 初始化 objectMapper 属性 |
| 23 | - * | |
| 23 | + * <p> | |
| 24 | 24 | * 通过这样的方式,使用 Spring 创建的 ObjectMapper Bean |
| 25 | 25 | * |
| 26 | 26 | * @param objectMapper ObjectMapper 对象 |
| ... | ... | @@ -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 | } | ... | ... |