Commit 225664550f3cd23b4c26a9aded828cf39c2ec0eb
1 parent
767cd902
实现短信通过redis stream异步发送,实现短信callback处理
Showing
53 changed files
with
819 additions
and
434 deletions
sql/sms.sql
| ... | ... | @@ -12,7 +12,6 @@ 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 | 15 | `callback_url` varchar(100) NOT NULL default '' COMMENT '回调请求路径', |
| 17 | 16 | `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识', |
| 18 | 17 | `name` varchar(50) NOT NULL COMMENT '名称', |
| ... | ... | @@ -61,7 +60,7 @@ CREATE TABLE `sms_template` |
| 61 | 60 | ) ENGINE = InnoDB |
| 62 | 61 | AUTO_INCREMENT = 1 |
| 63 | 62 | DEFAULT CHARSET = utf8mb4 COMMENT ='短信模板'; |
| 64 | - | |
| 63 | +/* | |
| 65 | 64 | -- ---------------------------- |
| 66 | 65 | -- Table structure for sms_query_log |
| 67 | 66 | -- ---------------------------- |
| ... | ... | @@ -73,7 +72,7 @@ CREATE TABLE `sms_query_log` |
| 73 | 72 | `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)', |
| 74 | 73 | `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id', |
| 75 | 74 | `template_code` varchar(50) NOT NULL COMMENT '渠道编码', |
| 76 | - `phones` varchar(2000) NOT NULL COMMENT '手机号(数组json字符串)', | |
| 75 | + `phone` char(11) NOT NULL COMMENT '手机号', | |
| 77 | 76 | `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', |
| 78 | 77 | `send_result_param` varchar(200) NOT NULL DEFAULT '' COMMENT '查询短信发送结果的参数', |
| 79 | 78 | `send_status` tinyint(1) NOT NULL DEFAULT 2 COMMENT '发送状态(0本地异步中 1发送请求失败 2发送请求成功)', |
| ... | ... | @@ -84,24 +83,26 @@ CREATE TABLE `sms_query_log` |
| 84 | 83 | PRIMARY KEY (`id`) USING BTREE |
| 85 | 84 | ) ENGINE = InnoDB |
| 86 | 85 | AUTO_INCREMENT = 1 |
| 87 | - DEFAULT CHARSET = utf8mb4 COMMENT ='短信请求日志'; | |
| 86 | + DEFAULT CHARSET = utf8mb4 COMMENT ='短信请求日志';*/ | |
| 88 | 87 | |
| 89 | 88 | -- ---------------------------- |
| 90 | 89 | -- Table structure for sms_log |
| 91 | 90 | -- ---------------------------- |
| 92 | -DROP TABLE IF EXISTS `sms_send_log`; | |
| 93 | -CREATE TABLE `sms_send_log` | |
| 91 | +DROP TABLE IF EXISTS `sms_query_log`; | |
| 92 | +CREATE TABLE `sms_query_log` | |
| 94 | 93 | ( |
| 95 | 94 | `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', |
| 95 | + `api_id` varchar(100) NOT NULL COMMENT '第三方唯一标识', | |
| 96 | 96 | `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)', |
| 97 | 97 | `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id', |
| 98 | 98 | `template_code` varchar(50) NOT NULL COMMENT '渠道编码', |
| 99 | - `query_log_id` bigint(20) NOT NULL COMMENT '请求日志id', | |
| 100 | 99 | `phone` char(11) NOT NULL COMMENT '手机号', |
| 101 | 100 | `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', |
| 101 | + `send_status` tinyint(1) NOT NULL DEFAULT 0 COMMENT '发送状态 详情见:SmsSendStatusEnum', | |
| 102 | 102 | `remark` varchar(200) DEFAULT NULL COMMENT '备注', |
| 103 | - `success` tinyint(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | |
| 104 | - `send_time` datetime DEFAULT NULL COMMENT '创建时间', | |
| 103 | + `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者', | |
| 104 | + `create_time` datetime DEFAULT NULL COMMENT '创建时间', | |
| 105 | + `send_time` datetime DEFAULT NULL COMMENT '发送时间', | |
| 105 | 106 | PRIMARY KEY (`id`) USING BTREE |
| 106 | 107 | ) ENGINE = InnoDB |
| 107 | 108 | AUTO_INCREMENT = 1 | ... | ... |
src/main/java/cn/iocoder/dashboard/DashboardApplication.java
| 1 | -//package cn.iocoder.dashboard; | |
| 2 | -// | |
| 3 | -//import de.codecentric.boot.admin.server.config.EnableAdminServer; | |
| 4 | -//import org.springframework.boot.SpringApplication; | |
| 5 | -//import org.springframework.boot.autoconfigure.SpringBootApplication; | |
| 6 | -// | |
| 7 | -//@SpringBootApplication | |
| 8 | -//@EnableAdminServer | |
| 9 | -//public class DashboardApplication { | |
| 10 | -// | |
| 11 | -// public static void main(String[] args) { | |
| 12 | -// SpringApplication.run(DashboardApplication.class, args); | |
| 13 | -// } | |
| 14 | -// | |
| 15 | -//} | |
| 1 | +package cn.iocoder.dashboard; | |
| 2 | + | |
| 3 | +import de.codecentric.boot.admin.server.config.EnableAdminServer; | |
| 4 | +import org.springframework.boot.SpringApplication; | |
| 5 | +import org.springframework.boot.autoconfigure.SpringBootApplication; | |
| 6 | + | |
| 7 | +@SpringBootApplication | |
| 8 | +@EnableAdminServer | |
| 9 | +public class DashboardApplication { | |
| 10 | + | |
| 11 | + public static void main(String[] args) { | |
| 12 | + SpringApplication.run(DashboardApplication.class, args); | |
| 13 | + } | |
| 14 | + | |
| 15 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/redis/core/util/RedisStreamUtils.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.redis.core.util; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.modules.system.redis.stream.sms.SmsSendMessage; | |
| 4 | +import org.springframework.data.redis.connection.stream.StreamRecords; | |
| 5 | +import org.springframework.data.redis.core.RedisTemplate; | |
| 6 | + | |
| 7 | +/** | |
| 8 | + * Redis 消息工具类 | |
| 9 | + * | |
| 10 | + * @author 芋道源码 | |
| 11 | + */ | |
| 12 | +public class RedisStreamUtils { | |
| 13 | + | |
| 14 | + public static final String KEY_SMS_SEND = "stream_sms_send"; | |
| 15 | + | |
| 16 | + public static final String GROUP_SMS_SEND = "group_sms_send"; | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 发送 Redis 消息,基于 Redis pub/sub 实现 | |
| 20 | + * | |
| 21 | + * @param redisTemplate Redis 操作模板 | |
| 22 | + * @param message 消息 | |
| 23 | + */ | |
| 24 | + public static void sendChannelMessage(RedisTemplate<String, ?> redisTemplate, SmsSendMessage message) { | |
| 25 | + | |
| 26 | + redisTemplate.opsForStream().add(StreamRecords.newRecord().ofObject(message).withStreamKey(KEY_SMS_SEND)); | |
| 27 | + } | |
| 28 | + | |
| 29 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/security/config/SecurityConfiguration.java
| ... | ... | @@ -134,6 +134,7 @@ public class SecurityConfiguration extends WebSecurityConfigurerAdapter { |
| 134 | 134 | .antMatchers(webProperties.getApiPrefix() + "/system/file/get/**").anonymous() |
| 135 | 135 | // TODO |
| 136 | 136 | .antMatchers("/swagger-ui.html").anonymous() |
| 137 | + .antMatchers("/**").anonymous() | |
| 137 | 138 | .antMatchers("/swagger-resources/**").anonymous() |
| 138 | 139 | .antMatchers("/webjars/**").anonymous() |
| 139 | 140 | .antMatchers("/*/api-docs").anonymous() | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java
| ... | ... | @@ -5,8 +5,6 @@ import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 5 | 5 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; |
| 6 | 6 | import lombok.extern.slf4j.Slf4j; |
| 7 | 7 | |
| 8 | -import java.util.Collection; | |
| 9 | - | |
| 10 | 8 | /** |
| 11 | 9 | * 抽象短息客户端 |
| 12 | 10 | * |
| ... | ... | @@ -35,12 +33,12 @@ public abstract class AbstractSmsClient implements SmsClient { |
| 35 | 33 | } |
| 36 | 34 | |
| 37 | 35 | @Override |
| 38 | - public final SmsResult send(String templateApiId, SmsBody smsBody, Collection<String> targets) { | |
| 36 | + public final SmsResult send(String templateApiId, SmsBody smsBody, String target) { | |
| 39 | 37 | SmsResult result; |
| 40 | 38 | try { |
| 41 | - beforeSend(templateApiId, smsBody, targets); | |
| 42 | - result = doSend(templateApiId, smsBody, targets); | |
| 43 | - afterSend(templateApiId, smsBody, targets, result); | |
| 39 | + beforeSend(templateApiId, smsBody, target); | |
| 40 | + result = doSend(templateApiId, smsBody, target); | |
| 41 | + afterSend(templateApiId, smsBody, target, result); | |
| 44 | 42 | } catch (Exception e) { |
| 45 | 43 | // exception handle |
| 46 | 44 | log.debug(e.getMessage(), e); |
| ... | ... | @@ -54,16 +52,16 @@ public abstract class AbstractSmsClient implements SmsClient { |
| 54 | 52 | * |
| 55 | 53 | * @param templateApiId 短信模板唯一标识 |
| 56 | 54 | * @param smsBody 消息内容 |
| 57 | - * @param targets 发送对象列表 | |
| 55 | + * @param targetPhone 发送对象手机号 | |
| 58 | 56 | * @return 短信发送结果 |
| 59 | 57 | * @throws Exception 调用发送失败,抛出异常 |
| 60 | 58 | */ |
| 61 | - protected abstract SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception; | |
| 59 | + protected abstract SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception; | |
| 62 | 60 | |
| 63 | - protected void beforeSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception { | |
| 61 | + protected void beforeSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception { | |
| 64 | 62 | } |
| 65 | 63 | |
| 66 | - protected void afterSend(String templateApiId, SmsBody smsBody, Collection<String> targets, SmsResult result) throws Exception { | |
| 64 | + protected void afterSend(String templateApiId, SmsBody smsBody, String targetPhone, SmsResult result) throws Exception { | |
| 67 | 65 | } |
| 68 | 66 | |
| 69 | 67 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java deleted
| 1 | -package cn.iocoder.dashboard.framework.sms.client; | |
| 2 | - | |
| 3 | -import cn.hutool.core.date.DateUtil; | |
| 4 | -import cn.hutool.core.util.ArrayUtil; | |
| 5 | -import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 6 | -import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 7 | -import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | |
| 8 | -import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; | |
| 9 | -import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; | |
| 10 | -import com.aliyuncs.DefaultAcsClient; | |
| 11 | -import com.aliyuncs.IAcsClient; | |
| 12 | -import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest; | |
| 13 | -import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse; | |
| 14 | -import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; | |
| 15 | -import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; | |
| 16 | -import com.aliyuncs.exceptions.ClientException; | |
| 17 | -import com.aliyuncs.http.MethodType; | |
| 18 | -import com.aliyuncs.profile.DefaultProfile; | |
| 19 | -import com.aliyuncs.profile.IClientProfile; | |
| 20 | -import lombok.extern.slf4j.Slf4j; | |
| 21 | - | |
| 22 | -import java.util.ArrayList; | |
| 23 | -import java.util.Collection; | |
| 24 | -import java.util.List; | |
| 25 | - | |
| 26 | -/** | |
| 27 | - * 阿里短信实现类 | |
| 28 | - * | |
| 29 | - * @author zzf | |
| 30 | - * @date 2021/1/25 14:17 | |
| 31 | - */ | |
| 32 | -@Slf4j | |
| 33 | -public class AliyunSmsClient extends AbstractSmsClient implements NeedQuerySendResultSmsClient { | |
| 34 | - | |
| 35 | - private static final String OK = "OK"; | |
| 36 | - | |
| 37 | - private static final String PRODUCT = "Dysmsapi"; | |
| 38 | - | |
| 39 | - private static final String DOMAIN = "dysmsapi.aliyuncs.com"; | |
| 40 | - | |
| 41 | - private static final String ENDPOINT = "cn-hangzhou"; | |
| 42 | - | |
| 43 | - private final IAcsClient acsClient; | |
| 44 | - | |
| 45 | - /** | |
| 46 | - * 构造阿里云短信发送处理 | |
| 47 | - * | |
| 48 | - * @param channelVO 阿里云短信配置 | |
| 49 | - */ | |
| 50 | - public AliyunSmsClient(SmsChannelProperty channelVO) { | |
| 51 | - super(channelVO); | |
| 52 | - | |
| 53 | - String accessKeyId = channelVO.getApiKey(); | |
| 54 | - String accessKeySecret = channelVO.getApiSecret(); | |
| 55 | - | |
| 56 | - IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, accessKeyId, accessKeySecret); | |
| 57 | - DefaultProfile.addEndpoint(ENDPOINT, PRODUCT, DOMAIN); | |
| 58 | - | |
| 59 | - acsClient = new DefaultAcsClient(profile); | |
| 60 | - } | |
| 61 | - | |
| 62 | - @Override | |
| 63 | - public SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) throws Exception { | |
| 64 | - SendSmsRequest request = new SendSmsRequest(); | |
| 65 | - request.setSysMethod(MethodType.POST); | |
| 66 | - request.setPhoneNumbers(ArrayUtil.join(targets, ",")); | |
| 67 | - request.setSignName(channelVO.getApiSignatureId()); | |
| 68 | - request.setTemplateCode(templateApiId); | |
| 69 | - request.setTemplateParam(smsBody.getParamsStr()); | |
| 70 | - SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); | |
| 71 | - | |
| 72 | - boolean success = OK.equals(sendSmsResponse.getCode()); | |
| 73 | - if (!success) { | |
| 74 | - log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage()); | |
| 75 | - } | |
| 76 | - return new SmsResult() | |
| 77 | - .setSuccess(success) | |
| 78 | - .setMessage(sendSmsResponse.getMessage()) | |
| 79 | - .setCode(sendSmsResponse.getCode()) | |
| 80 | - .setApiId(sendSmsResponse.getBizId()) | |
| 81 | - .setSendResultParam(sendSmsResponse.getBizId()); | |
| 82 | - } | |
| 83 | - | |
| 84 | - | |
| 85 | - @Override | |
| 86 | - public List<SmsResultDetail> getSmsSendResult(String param) throws ClientException { | |
| 87 | - QuerySendDetailsRequest querySendDetailsRequest = new QuerySendDetailsRequest(); | |
| 88 | - querySendDetailsRequest.setBizId(param); | |
| 89 | - // TODO FROM 芋艿 to zzf:发送完之后,基于短信平台回调,去更新回执状态。短信发送是否成功,和最终用户收到,是两个维度。这块有困惑,可以微信,我给个截图哈。 DONE | |
| 90 | - QuerySendDetailsResponse acsResponse = acsClient.getAcsResponse(querySendDetailsRequest); | |
| 91 | - List<SmsResultDetail> resultDetailList = new ArrayList<>(Integer.parseInt(acsResponse.getTotalCount())); | |
| 92 | - acsResponse.getSmsSendDetailDTOs().forEach(s -> { | |
| 93 | - SmsResultDetail resultDetail = new SmsResultDetail(); | |
| 94 | - resultDetail.setSendTime(DateUtil.parseDateTime(s.getSendDate())); | |
| 95 | - resultDetail.setMessage(s.getContent()); | |
| 96 | - resultDetail.setPhone(s.getPhoneNum()); | |
| 97 | - resultDetail.setSendStatus(statusConvert(s.getSendStatus())); | |
| 98 | - resultDetailList.add(resultDetail); | |
| 99 | - }); | |
| 100 | - return resultDetailList; | |
| 101 | - } | |
| 102 | - | |
| 103 | - private int statusConvert(Long aliSendStatus) { | |
| 104 | - if (aliSendStatus == 1L) { | |
| 105 | - return SmsSendStatusEnum.SEND_SUCCESS.getStatus(); | |
| 106 | - } | |
| 107 | - if (aliSendStatus == 2L) { | |
| 108 | - return SmsSendStatusEnum.SEND_FAIL.getStatus(); | |
| 109 | - } | |
| 110 | - return SmsSendStatusEnum.WAITING.getStatus(); | |
| 111 | - } | |
| 112 | - | |
| 113 | -} |
src/main/java/cn/iocoder/dashboard/framework/sms/client/HadCallbackSmsClient.java deleted
| 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 deleted
| 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
| ... | ... | @@ -4,8 +4,7 @@ import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 4 | 4 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 5 | 5 | import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; |
| 6 | 6 | |
| 7 | -import java.util.Collection; | |
| 8 | -import java.util.List; | |
| 7 | +import javax.servlet.ServletRequest; | |
| 9 | 8 | |
| 10 | 9 | /** |
| 11 | 10 | * 短信父接口 |
| ... | ... | @@ -23,9 +22,15 @@ public interface SmsClient { |
| 23 | 22 | * @param targets 发送对象列表 |
| 24 | 23 | * @return 短信发送结果 |
| 25 | 24 | */ |
| 26 | - SmsResult send(String templateApiId, SmsBody smsBody, Collection<String> targets); | |
| 25 | + SmsResult send(String templateApiId, SmsBody smsBody, String targets); | |
| 27 | 26 | |
| 28 | 27 | |
| 29 | - //List<SmsResultDetail> getSmsSendResult(String jsonObjectParam); | |
| 28 | + /** | |
| 29 | + * 短信发送回调请求处理 | |
| 30 | + * | |
| 31 | + * @param request 请求 | |
| 32 | + * @return 短信发送结果 | |
| 33 | + */ | |
| 34 | + SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws Exception; | |
| 30 | 35 | |
| 31 | 36 | } |
| 32 | 37 | \ No newline at end of file | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/client/impl/ali/AliyunSmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.client.impl.ali; | |
| 2 | + | |
| 3 | +import cn.hutool.core.collection.CollectionUtil; | |
| 4 | +import cn.hutool.core.date.DateUtil; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 7 | +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.aliyuncs.DefaultAcsClient; | |
| 13 | +import com.aliyuncs.IAcsClient; | |
| 14 | +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; | |
| 15 | +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; | |
| 16 | +import com.aliyuncs.http.MethodType; | |
| 17 | +import com.aliyuncs.profile.DefaultProfile; | |
| 18 | +import com.aliyuncs.profile.IClientProfile; | |
| 19 | +import com.fasterxml.jackson.core.type.TypeReference; | |
| 20 | +import lombok.extern.slf4j.Slf4j; | |
| 21 | + | |
| 22 | +import javax.servlet.ServletRequest; | |
| 23 | +import java.io.BufferedReader; | |
| 24 | +import java.io.InputStreamReader; | |
| 25 | +import java.util.Date; | |
| 26 | +import java.util.HashMap; | |
| 27 | +import java.util.List; | |
| 28 | +import java.util.Map; | |
| 29 | + | |
| 30 | +/** | |
| 31 | + * 阿里短信实现类 | |
| 32 | + * | |
| 33 | + * @author zzf | |
| 34 | + * @date 2021/1/25 14:17 | |
| 35 | + */ | |
| 36 | +@Slf4j | |
| 37 | +public class AliyunSmsClient extends AbstractSmsClient { | |
| 38 | + | |
| 39 | + private static final String OK = "OK"; | |
| 40 | + | |
| 41 | + private static final String PRODUCT = "Dystopi"; | |
| 42 | + | |
| 43 | + private static final String DOMAIN = "dysmsapi.aliyuncs.com"; | |
| 44 | + | |
| 45 | + private static final String ENDPOINT = "cn-hangzhou"; | |
| 46 | + | |
| 47 | + private final IAcsClient acsClient; | |
| 48 | + | |
| 49 | + /** | |
| 50 | + * 构造阿里云短信发送处理 | |
| 51 | + * | |
| 52 | + * @param channelVO 阿里云短信配置 | |
| 53 | + */ | |
| 54 | + public AliyunSmsClient(SmsChannelProperty channelVO) { | |
| 55 | + super(channelVO); | |
| 56 | + | |
| 57 | + String accessKeyId = channelVO.getApiKey(); | |
| 58 | + String accessKeySecret = channelVO.getApiSecret(); | |
| 59 | + | |
| 60 | + IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, accessKeyId, accessKeySecret); | |
| 61 | + DefaultProfile.addEndpoint(ENDPOINT, PRODUCT, DOMAIN); | |
| 62 | + | |
| 63 | + acsClient = new DefaultAcsClient(profile); | |
| 64 | + } | |
| 65 | + | |
| 66 | + @Override | |
| 67 | + public SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) throws Exception { | |
| 68 | + SendSmsRequest request = new SendSmsRequest(); | |
| 69 | + request.setSysMethod(MethodType.POST); | |
| 70 | + request.setPhoneNumbers(targetPhone); | |
| 71 | + request.setSignName(channelVO.getApiSignatureId()); | |
| 72 | + request.setTemplateCode(templateApiId); | |
| 73 | + request.setTemplateParam(smsBody.getParamsStr()); | |
| 74 | + SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); | |
| 75 | + | |
| 76 | + boolean success = OK.equals(sendSmsResponse.getCode()); | |
| 77 | + if (!success) { | |
| 78 | + log.debug("send fail[code={}, message={}]", sendSmsResponse.getCode(), sendSmsResponse.getMessage()); | |
| 79 | + } | |
| 80 | + return new SmsResult() | |
| 81 | + .setSuccess(success) | |
| 82 | + .setMessage(sendSmsResponse.getMessage()) | |
| 83 | + .setCode(sendSmsResponse.getCode()) | |
| 84 | + .setApiId(sendSmsResponse.getBizId()); | |
| 85 | + } | |
| 86 | + | |
| 87 | + /** | |
| 88 | + * [{ | |
| 89 | + * "send_time" : "2017-08-30 00:00:00", | |
| 90 | + * "report_time" : "2017-08-30 00:00:00", | |
| 91 | + * "success" : true, | |
| 92 | + * "err_msg" : "用户接收成功", | |
| 93 | + * "err_code" : "DELIVERED", | |
| 94 | + * "phone_number" : "18612345678", | |
| 95 | + * "sms_size" : "1", | |
| 96 | + * "biz_id" : "932702304080415357^0", | |
| 97 | + * "out_id" : "1184585343" | |
| 98 | + * }] | |
| 99 | + * | |
| 100 | + * @param request 请求 | |
| 101 | + * @return | |
| 102 | + * @throws Exception | |
| 103 | + */ | |
| 104 | + @Override | |
| 105 | + public SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws Exception { | |
| 106 | + BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream())); | |
| 107 | + String paramStr = reader.readLine(); | |
| 108 | + List<Map<String, Object>> params = JsonUtils.parseByType(paramStr, new TypeReference<List<Map<String, Object>>>() { | |
| 109 | + }); | |
| 110 | + if (CollectionUtil.isNotEmpty(params)) { | |
| 111 | + Map<String, Object> sendResultParamMap = params.get(0); | |
| 112 | + return CallbackHelper.of(sendResultParamMap).toResultDetail(); | |
| 113 | + } | |
| 114 | + return null; | |
| 115 | + } | |
| 116 | + | |
| 117 | + /** | |
| 118 | + * 短信发送回调辅助类 | |
| 119 | + */ | |
| 120 | + private static class CallbackHelper { | |
| 121 | + | |
| 122 | + private final Map<String, Object> sendResultParamMap; | |
| 123 | + | |
| 124 | + private CallbackHelper(Map<String, Object> sendResultParamMap) { | |
| 125 | + this.sendResultParamMap = sendResultParamMap; | |
| 126 | + } | |
| 127 | + | |
| 128 | + public static CallbackHelper of(Map<String, Object> sendResultParamMap) { | |
| 129 | + return new CallbackHelper(sendResultParamMap); | |
| 130 | + } | |
| 131 | + | |
| 132 | + public Integer getSendStatus() { | |
| 133 | + return ((Boolean) sendResultParamMap.get(CallbackField.SUCCESS)) | |
| 134 | + ? SmsSendStatusEnum.SEND_SUCCESS.getStatus() | |
| 135 | + : SmsSendStatusEnum.SEND_FAIL.getStatus(); | |
| 136 | + } | |
| 137 | + | |
| 138 | + public String getBizId() { | |
| 139 | + return sendResultParamMap.get(CallbackField.BIZ_ID).toString(); | |
| 140 | + } | |
| 141 | + | |
| 142 | + public String getErrMsg() { | |
| 143 | + return sendResultParamMap.get(CallbackField.ERR_MSG).toString(); | |
| 144 | + } | |
| 145 | + | |
| 146 | + public String getErrCode() { | |
| 147 | + return sendResultParamMap.get(CallbackField.ERR_CODE).toString(); | |
| 148 | + } | |
| 149 | + | |
| 150 | + public Date getSendTime() { | |
| 151 | + return DateUtil.parseTime(sendResultParamMap.get(CallbackField.SEND_TIME).toString()); | |
| 152 | + } | |
| 153 | + | |
| 154 | + public String getPhoneNumber() { | |
| 155 | + return sendResultParamMap.get(CallbackField.PHONE_NUMBER).toString(); | |
| 156 | + } | |
| 157 | + | |
| 158 | + public String getOutId() { | |
| 159 | + return sendResultParamMap.get(CallbackField.OUT_ID).toString(); | |
| 160 | + } | |
| 161 | + | |
| 162 | + public SmsResultDetail toResultDetail() { | |
| 163 | + SmsResultDetail resultDetail = new SmsResultDetail(); | |
| 164 | + resultDetail.setSendStatus(getSendStatus()); | |
| 165 | + resultDetail.setApiId(getBizId()); | |
| 166 | + resultDetail.setSendTime(getSendTime()); | |
| 167 | + resultDetail.setPhone(getPhoneNumber()); | |
| 168 | + resultDetail.setMessage(getErrMsg()); | |
| 169 | + | |
| 170 | + resultDetail.setCallbackResponseBody(generateSuccessResponseBody()); | |
| 171 | + return resultDetail; | |
| 172 | + } | |
| 173 | + | |
| 174 | + /** | |
| 175 | + * 生成回调成功的返回对象 | |
| 176 | + */ | |
| 177 | + private Map<String, Object> generateSuccessResponseBody() { | |
| 178 | + Map<String, Object> result = new HashMap<>(); | |
| 179 | + result.put("code", 0); | |
| 180 | + result.put("msg", "成功"); | |
| 181 | + return result; | |
| 182 | + } | |
| 183 | + | |
| 184 | + } | |
| 185 | + | |
| 186 | + /** | |
| 187 | + * 回调接口字段定义 | |
| 188 | + */ | |
| 189 | + private interface CallbackField { | |
| 190 | + //是否成功 boolean | |
| 191 | + String SUCCESS = "success"; | |
| 192 | + | |
| 193 | + //发送时间 | |
| 194 | + String SEND_TIME = "send_time"; | |
| 195 | + | |
| 196 | + //错误信息 | |
| 197 | + String ERR_MSG = "err_msg"; | |
| 198 | + | |
| 199 | + //错误编码 | |
| 200 | + String ERR_CODE = "err_code"; | |
| 201 | + | |
| 202 | + //手机号 | |
| 203 | + String PHONE_NUMBER = "phone_number"; | |
| 204 | + | |
| 205 | + //用户序列号 out_id | |
| 206 | + String OUT_ID = "out_id"; | |
| 207 | + | |
| 208 | + //biz_id 即 apiId 唯一标识 | |
| 209 | + String BIZ_ID = "biz_id"; | |
| 210 | + } | |
| 211 | + | |
| 212 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/client/YunpianSmsClient.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/client/impl/yunpian/YunpianSmsClient.java
| 1 | -package cn.iocoder.dashboard.framework.sms.client; | |
| 1 | +package cn.iocoder.dashboard.framework.sms.client.impl.yunpian; | |
| 2 | 2 | |
| 3 | +import cn.hutool.core.collection.CollectionUtil; | |
| 3 | 4 | import cn.hutool.core.date.DateUtil; |
| 4 | 5 | import cn.hutool.core.util.CharsetUtil; |
| 6 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | |
| 5 | 7 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 6 | 8 | import cn.iocoder.dashboard.framework.sms.core.SmsConstants; |
| 7 | 9 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| ... | ... | @@ -14,13 +16,15 @@ import com.yunpian.sdk.YunpianClient; |
| 14 | 16 | import com.yunpian.sdk.constant.Code; |
| 15 | 17 | import com.yunpian.sdk.constant.YunpianConstant; |
| 16 | 18 | import com.yunpian.sdk.model.Result; |
| 17 | -import com.yunpian.sdk.model.SmsBatchSend; | |
| 19 | +import com.yunpian.sdk.model.SmsSingleSend; | |
| 18 | 20 | import lombok.extern.slf4j.Slf4j; |
| 19 | 21 | |
| 20 | 22 | import javax.servlet.ServletRequest; |
| 21 | 23 | import java.io.UnsupportedEncodingException; |
| 22 | 24 | import java.net.URLEncoder; |
| 23 | -import java.util.*; | |
| 25 | +import java.util.HashMap; | |
| 26 | +import java.util.List; | |
| 27 | +import java.util.Map; | |
| 24 | 28 | |
| 25 | 29 | /** |
| 26 | 30 | * 云片短信实现类 |
| ... | ... | @@ -29,7 +33,7 @@ import java.util.*; |
| 29 | 33 | * @date 9:48 2021/3/5 |
| 30 | 34 | */ |
| 31 | 35 | @Slf4j |
| 32 | -public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSmsClient { | |
| 36 | +public class YunpianSmsClient extends AbstractSmsClient { | |
| 33 | 37 | |
| 34 | 38 | private final YunpianClient client; |
| 35 | 39 | |
| ... | ... | @@ -47,14 +51,14 @@ public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSm |
| 47 | 51 | } |
| 48 | 52 | |
| 49 | 53 | @Override |
| 50 | - public SmsResult doSend(String templateApiId, SmsBody smsBody, Collection<String> targets) { | |
| 54 | + public SmsResult doSend(String templateApiId, SmsBody smsBody, String targetPhone) { | |
| 51 | 55 | 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 | + paramMap.put(YunpianConstant.APIKEY, getProperty().getApiKey()); | |
| 57 | + paramMap.put(YunpianConstant.MOBILE, String.join(SmsConstants.COMMA, targetPhone)); | |
| 58 | + paramMap.put(YunpianConstant.TEXT, formatContent(smsBody)); | |
| 59 | + paramMap.put(Helper.CALLBACK, getProperty().getCallbackUrl()); | |
| 56 | 60 | |
| 57 | - Result<SmsBatchSend> sendResult = client.sms().batch_send(paramMap); | |
| 61 | + Result<SmsSingleSend> sendResult = client.sms().single_send(paramMap); | |
| 58 | 62 | boolean success = sendResult.getCode().equals(Code.OK); |
| 59 | 63 | |
| 60 | 64 | if (!success) { |
| ... | ... | @@ -64,7 +68,7 @@ public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSm |
| 64 | 68 | .setSuccess(success) |
| 65 | 69 | .setMessage(sendResult.getDetail()) |
| 66 | 70 | .setCode(sendResult.getCode().toString()) |
| 67 | - .setApiId(sendResult.getData().getData().get(0).getSid().toString()); | |
| 71 | + .setApiId(sendResult.getData().getSid().toString()); | |
| 68 | 72 | } |
| 69 | 73 | |
| 70 | 74 | |
| ... | ... | @@ -96,26 +100,16 @@ public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSm |
| 96 | 100 | } |
| 97 | 101 | |
| 98 | 102 | |
| 103 | + /** | |
| 104 | + * 云片的比较复杂,又是加密又是套娃的 | |
| 105 | + */ | |
| 99 | 106 | @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; | |
| 107 | + public SmsResultDetail smsSendCallbackHandle(ServletRequest request) throws UnsupportedEncodingException { | |
| 108 | + Map<String, String> map = getRequestParams(request); | |
| 109 | + return Helper.getSmsResultDetailByParam(map); | |
| 117 | 110 | } |
| 118 | 111 | |
| 112 | + | |
| 119 | 113 | /** |
| 120 | 114 | * 从 request 中获取请求中传入的短信发送结果信息 |
| 121 | 115 | * |
| ... | ... | @@ -123,10 +117,58 @@ public class YunpianSmsClient extends AbstractSmsClient implements HadCallbackSm |
| 123 | 117 | * @return 短信发送结果信息 |
| 124 | 118 | * @throws UnsupportedEncodingException 解码异常 |
| 125 | 119 | */ |
| 126 | - private List<Map<String, String>> getSendResult(ServletRequest request) throws UnsupportedEncodingException { | |
| 120 | + private Map<String, String> getRequestParams(ServletRequest request) throws UnsupportedEncodingException { | |
| 127 | 121 | Map<String, String[]> parameterMap = request.getParameterMap(); |
| 128 | 122 | String[] smsStatuses = parameterMap.get(YunpianConstant.SMS_STATUS); |
| 129 | 123 | String encode = URLEncoder.encode(smsStatuses[0], CharsetUtil.UTF_8); |
| 130 | - return JsonUtils.parseByType(encode, callbackType); | |
| 124 | + List<Map<String, String>> paramList = JsonUtils.parseByType(encode, callbackType); | |
| 125 | + if (CollectionUtil.isNotEmpty(paramList)) { | |
| 126 | + return paramList.get(0); | |
| 127 | + } | |
| 128 | + throw new IllegalArgumentException("YunpianSmsClient getRequestParams fail! can't format RequestParam: " | |
| 129 | + + JsonUtils.toJsonString(request.getParameterMap())); | |
| 130 | + } | |
| 131 | + | |
| 132 | + /** | |
| 133 | + * 云片的回调函数的一些辅助方法 | |
| 134 | + */ | |
| 135 | + private static class Helper { | |
| 136 | + | |
| 137 | + //短信唯一标识 | |
| 138 | + private final static String API_ID = "sid"; | |
| 139 | + | |
| 140 | + //回调地址· | |
| 141 | + private final static String CALLBACK = "callback"; | |
| 142 | + | |
| 143 | + //手机号 | |
| 144 | + private final static String MOBILE = "mobile"; | |
| 145 | + | |
| 146 | + //错误信息 | |
| 147 | + private final static String ERROR_MSG = "error_msg"; | |
| 148 | + | |
| 149 | + //用户接收时间 字符串 标准格式 | |
| 150 | + private final static String USER_RECEIVE_TIME = "user_receive_time"; | |
| 151 | + | |
| 152 | + //发送状态 | |
| 153 | + private final static String REPORT_STATUS = "report_status"; | |
| 154 | + | |
| 155 | + private static int getSendStatus(Map<String, String> map) { | |
| 156 | + String reportStatus = map.get(REPORT_STATUS); | |
| 157 | + return SmsConstants.SUCCESS.equals(reportStatus) | |
| 158 | + ? SmsSendStatusEnum.SEND_SUCCESS.getStatus() | |
| 159 | + : SmsSendStatusEnum.SEND_FAIL.getStatus(); | |
| 160 | + } | |
| 161 | + | |
| 162 | + public static SmsResultDetail getSmsResultDetailByParam(Map<String, String> map) { | |
| 163 | + SmsResultDetail detail = new SmsResultDetail(); | |
| 164 | + detail.setPhone(map.get(MOBILE)); | |
| 165 | + detail.setMessage(map.get(ERROR_MSG)); | |
| 166 | + detail.setSendTime(DateUtil.parseTime(map.get(USER_RECEIVE_TIME))); | |
| 167 | + detail.setSendStatus(getSendStatus(map)); | |
| 168 | + detail.setApiId(API_ID); | |
| 169 | + | |
| 170 | + detail.setCallbackResponseBody(SmsConstants.SUCCESS); | |
| 171 | + return detail; | |
| 172 | + } | |
| 131 | 173 | } |
| 132 | 174 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java
| ... | ... | @@ -2,12 +2,15 @@ package cn.iocoder.dashboard.framework.sms.core; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.common.exception.ServiceException; |
| 4 | 4 | import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; |
| 5 | -import cn.iocoder.dashboard.framework.sms.client.AliyunSmsClient; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.client.impl.ali.AliyunSmsClient; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.client.impl.yunpian.YunpianSmsClient; | |
| 6 | 7 | import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; |
| 7 | 8 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; |
| 8 | 9 | import cn.iocoder.dashboard.framework.sms.core.property.SmsTemplateProperty; |
| 10 | +import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 9 | 11 | import org.springframework.stereotype.Component; |
| 10 | 12 | |
| 13 | +import javax.servlet.ServletRequest; | |
| 11 | 14 | import java.util.Collection; |
| 12 | 15 | import java.util.Map; |
| 13 | 16 | import java.util.concurrent.ConcurrentHashMap; |
| ... | ... | @@ -54,6 +57,8 @@ public class SmsClientFactory { |
| 54 | 57 | switch (channelEnum) { |
| 55 | 58 | case ALI: |
| 56 | 59 | return new AliyunSmsClient(channelVO); |
| 60 | + case YUN_PIAN: | |
| 61 | + return new YunpianSmsClient(channelVO); | |
| 57 | 62 | // TODO fill more channel |
| 58 | 63 | default: |
| 59 | 64 | break; |
| ... | ... | @@ -102,4 +107,28 @@ public class SmsClientFactory { |
| 102 | 107 | return smsTemplateProperty.getApiTemplateId(); |
| 103 | 108 | } |
| 104 | 109 | |
| 110 | + | |
| 111 | + /** | |
| 112 | + * 从短信发送回调函数请求中获取用于唯一确定一条send_lod的apiId | |
| 113 | + * | |
| 114 | + * @param callbackRequest 短信发送回调函数请求 | |
| 115 | + * @return 第三方平台短信唯一标识 | |
| 116 | + */ | |
| 117 | + public SmsResultDetail getSmsResultDetailFromCallbackQuery(ServletRequest callbackRequest) { | |
| 118 | + | |
| 119 | + for (Long channelId : smsSenderMap.keySet()) { | |
| 120 | + AbstractSmsClient smsClient = smsSenderMap.get(channelId); | |
| 121 | + try { | |
| 122 | + SmsResultDetail smsSendResult = smsClient.smsSendCallbackHandle(callbackRequest); | |
| 123 | + if (smsSendResult != null) { | |
| 124 | + return smsSendResult; | |
| 125 | + } | |
| 126 | + } catch (Exception ignored) { | |
| 127 | + } | |
| 128 | + } | |
| 129 | + throw new IllegalArgumentException("getSmsResultDetailFromCallbackQuery fail! don't match SmsClient by RequestParam: " | |
| 130 | + + JsonUtils.toJsonString(callbackRequest.getParameterMap())); | |
| 131 | + } | |
| 132 | + | |
| 133 | + | |
| 105 | 134 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java
| ... | ... | @@ -32,11 +32,6 @@ public class SmsResult implements Serializable { |
| 32 | 32 | */ |
| 33 | 33 | private String message; |
| 34 | 34 | |
| 35 | - /** | |
| 36 | - * 用于查询发送结果的参数 | |
| 37 | - */ | |
| 38 | - private String sendResultParam; | |
| 39 | - | |
| 40 | 35 | public static SmsResult failResult(String message) { |
| 41 | 36 | SmsResult resultBody = new SmsResult(); |
| 42 | 37 | resultBody.setSuccess(false); | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResultDetail.java
| ... | ... | @@ -12,6 +12,11 @@ import java.util.Date; |
| 12 | 12 | public class SmsResultDetail implements Serializable { |
| 13 | 13 | |
| 14 | 14 | /** |
| 15 | + * 唯一标识 | |
| 16 | + */ | |
| 17 | + private String apiId; | |
| 18 | + | |
| 19 | + /** | |
| 15 | 20 | * 短信发送状态 {@link cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum} |
| 16 | 21 | */ |
| 17 | 22 | private Integer sendStatus; |
| ... | ... | @@ -30,4 +35,9 @@ public class SmsResultDetail implements Serializable { |
| 30 | 35 | * 时间 |
| 31 | 36 | */ |
| 32 | 37 | private Date sendTime; |
| 38 | + | |
| 39 | + /** | |
| 40 | + * 接口返回值 | |
| 41 | + */ | |
| 42 | + private Object callbackResponseBody; | |
| 33 | 43 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/enums/SmsChannelEnum.java
src/main/java/cn/iocoder/dashboard/modules/infra/controller/redis/RedisController.java
| 1 | -package cn.iocoder.dashboard.modules.infra.controller.redis; | |
| 2 | - | |
| 3 | -import cn.hutool.core.util.StrUtil; | |
| 4 | -import cn.iocoder.dashboard.common.pojo.CommonResult; | |
| 5 | -import cn.iocoder.dashboard.framework.redis.core.RedisKeyRegistry; | |
| 6 | -import cn.iocoder.dashboard.modules.infra.controller.redis.vo.InfRedisKeyRespVO; | |
| 7 | -import cn.iocoder.dashboard.modules.infra.controller.redis.vo.InfRedisMonitorRespVO; | |
| 8 | -import org.springframework.data.redis.connection.RedisServerCommands; | |
| 9 | -import org.springframework.data.redis.core.RedisCallback; | |
| 10 | -import org.springframework.data.redis.core.StringRedisTemplate; | |
| 11 | -import org.springframework.web.bind.annotation.GetMapping; | |
| 12 | -import org.springframework.web.bind.annotation.RequestMapping; | |
| 13 | -import org.springframework.web.bind.annotation.RestController; | |
| 14 | - | |
| 15 | -import javax.annotation.Resource; | |
| 16 | -import java.util.ArrayList; | |
| 17 | -import java.util.List; | |
| 18 | -import java.util.Properties; | |
| 19 | -import java.util.stream.Collectors; | |
| 20 | - | |
| 21 | -import static cn.iocoder.dashboard.common.pojo.CommonResult.success; | |
| 22 | - | |
| 23 | -@RestController | |
| 24 | -@RequestMapping("/infra/redis") | |
| 25 | -public class RedisController { | |
| 26 | - | |
| 27 | - @Resource | |
| 28 | - private StringRedisTemplate stringRedisTemplate; | |
| 29 | - | |
| 30 | -// @PreAuthorize("@ss.hasPermission('infra:redis:get-monitor-info')") | |
| 31 | - @GetMapping("/get-monitor-info") | |
| 32 | - public CommonResult<InfRedisMonitorRespVO> getRedisMonitorInfo() { | |
| 33 | - // 获得 Redis 统计信息 | |
| 34 | - Properties info = stringRedisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info); | |
| 35 | - Long dbSize = stringRedisTemplate.execute(RedisServerCommands::dbSize); | |
| 36 | - Properties commandStats = stringRedisTemplate.execute(( | |
| 37 | - RedisCallback<Properties>) connection -> connection.info("commandstats")); | |
| 38 | - assert commandStats != null; // 断言,避免警告 | |
| 39 | - | |
| 40 | - // 拼接结果返回 | |
| 41 | - InfRedisMonitorRespVO respVO = InfRedisMonitorRespVO.builder().info(info).dbSize(dbSize) | |
| 42 | - .commandStats(new ArrayList<>(commandStats.size())).build(); | |
| 43 | - commandStats.forEach((key, value) -> { | |
| 44 | - respVO.getCommandStats().add(InfRedisMonitorRespVO.CommandStat.builder() | |
| 45 | - .command(StrUtil.subAfter((String) key, "cmdstat_", false)) | |
| 46 | - .calls(Integer.valueOf(StrUtil.subBetween((String) value, "calls=", ","))) | |
| 47 | - .usec(Integer.valueOf(StrUtil.subBetween((String) value, "usec=", ","))) | |
| 48 | - .build()); | |
| 49 | - }); | |
| 50 | - return success(respVO); | |
| 51 | - } | |
| 52 | - | |
| 53 | -// @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") | |
| 54 | - @GetMapping("/get-key-list") | |
| 55 | - public CommonResult<List<InfRedisKeyRespVO>> getKeyList() { | |
| 56 | - List<InfRedisKeyRespVO> respVOList = RedisKeyRegistry.list().stream() | |
| 57 | - .map(define -> InfRedisKeyRespVO.builder() | |
| 58 | - .keyTemplate(define.getKeyTemplate()) | |
| 59 | - .keyType(define.getKeyType().name()) | |
| 60 | - .valueType(define.getValueType().getName()) | |
| 61 | - .timeoutType(define.getTimeoutType().getType()) | |
| 62 | - .timeout((int) define.getTimeout().getSeconds()) | |
| 63 | - .build()) | |
| 64 | - .collect(Collectors.toList()); | |
| 65 | - return success(respVOList); | |
| 66 | - } | |
| 67 | - | |
| 68 | -} | |
| 1 | +//package cn.iocoder.dashboard.modules.infra.controller.redis; | |
| 2 | +// | |
| 3 | +//import cn.hutool.core.util.StrUtil; | |
| 4 | +//import cn.iocoder.dashboard.common.pojo.CommonResult; | |
| 5 | +//import cn.iocoder.dashboard.framework.redis.core.RedisKeyRegistry; | |
| 6 | +//import cn.iocoder.dashboard.modules.infra.controller.redis.vo.InfRedisKeyRespVO; | |
| 7 | +//import cn.iocoder.dashboard.modules.infra.controller.redis.vo.InfRedisMonitorRespVO; | |
| 8 | +//import org.springframework.data.redis.connection.RedisServerCommands; | |
| 9 | +//import org.springframework.data.redis.core.RedisCallback; | |
| 10 | +//import org.springframework.data.redis.core.StringRedisTemplate; | |
| 11 | +//import org.springframework.web.bind.annotation.GetMapping; | |
| 12 | +//import org.springframework.web.bind.annotation.RequestMapping; | |
| 13 | +//import org.springframework.web.bind.annotation.RestController; | |
| 14 | +// | |
| 15 | +//import javax.annotation.Resource; | |
| 16 | +//import java.util.ArrayList; | |
| 17 | +//import java.util.List; | |
| 18 | +//import java.util.Properties; | |
| 19 | +//import java.util.stream.Collectors; | |
| 20 | +// | |
| 21 | +//import static cn.iocoder.dashboard.common.pojo.CommonResult.success; | |
| 22 | +// | |
| 23 | +//@RestController | |
| 24 | +//@RequestMapping("/infra/redis") | |
| 25 | +//public class RedisController { | |
| 26 | +// | |
| 27 | +// @Resource | |
| 28 | +// private StringRedisTemplate stringRedisTemplate; | |
| 29 | +// | |
| 30 | +//// @PreAuthorize("@ss.hasPermission('infra:redis:get-monitor-info')") | |
| 31 | +// @GetMapping("/get-monitor-info") | |
| 32 | +// public CommonResult<InfRedisMonitorRespVO> getRedisMonitorInfo() { | |
| 33 | +// // 获得 Redis 统计信息 | |
| 34 | +// Properties info = stringRedisTemplate.execute((RedisCallback<Properties>) RedisServerCommands::info); | |
| 35 | +// Long dbSize = stringRedisTemplate.execute(RedisServerCommands::dbSize); | |
| 36 | +// Properties commandStats = stringRedisTemplate.execute(( | |
| 37 | +// RedisCallback<Properties>) connection -> connection.info("commandstats")); | |
| 38 | +// assert commandStats != null; // 断言,避免警告 | |
| 39 | +// | |
| 40 | +// // 拼接结果返回 | |
| 41 | +// InfRedisMonitorRespVO respVO = InfRedisMonitorRespVO.builder().info(info).dbSize(dbSize) | |
| 42 | +// .commandStats(new ArrayList<>(commandStats.size())).build(); | |
| 43 | +// commandStats.forEach((key, value) -> { | |
| 44 | +// respVO.getCommandStats().add(InfRedisMonitorRespVO.CommandStat.builder() | |
| 45 | +// .command(StrUtil.subAfter((String) key, "cmdstat_", false)) | |
| 46 | +// .calls(Integer.valueOf(StrUtil.subBetween((String) value, "calls=", ","))) | |
| 47 | +// .usec(Integer.valueOf(StrUtil.subBetween((String) value, "usec=", ","))) | |
| 48 | +// .build()); | |
| 49 | +// }); | |
| 50 | +// return success(respVO); | |
| 51 | +// } | |
| 52 | +// | |
| 53 | +//// @PreAuthorize("@ss.hasPermission('infra:redis:get-key-list')") | |
| 54 | +// @GetMapping("/get-key-list") | |
| 55 | +// public CommonResult<List<InfRedisKeyRespVO>> getKeyList() { | |
| 56 | +// List<InfRedisKeyRespVO> respVOList = RedisKeyRegistry.list().stream() | |
| 57 | +// .map(define -> InfRedisKeyRespVO.builder() | |
| 58 | +// .keyTemplate(define.getKeyTemplate()) | |
| 59 | +// .keyType(define.getKeyType().name()) | |
| 60 | +// .valueType(define.getValueType().getName()) | |
| 61 | +// .timeoutType(define.getTimeoutType().getType()) | |
| 62 | +// .timeout((int) define.getTimeout().getSeconds()) | |
| 63 | +// .build()) | |
| 64 | +// .collect(Collectors.toList()); | |
| 65 | +// return success(respVOList); | |
| 66 | +// } | |
| 67 | +// | |
| 68 | +//} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsDefaultCallbackController.java
| 1 | 1 | package cn.iocoder.dashboard.modules.system.controller.sms; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.stream.sms.SmsSendStreamProducer; | |
| 3 | 5 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; |
| 6 | +import io.swagger.annotations.Api; | |
| 7 | +import io.swagger.annotations.ApiOperation; | |
| 8 | +import org.springframework.web.bind.annotation.GetMapping; | |
| 9 | +import org.springframework.web.bind.annotation.PostMapping; | |
| 4 | 10 | import org.springframework.web.bind.annotation.RequestMapping; |
| 5 | 11 | import org.springframework.web.bind.annotation.RestController; |
| 6 | 12 | |
| 7 | 13 | import javax.annotation.Resource; |
| 8 | 14 | import javax.servlet.ServletRequest; |
| 15 | +import java.util.Arrays; | |
| 16 | +import java.util.Map; | |
| 9 | 17 | |
| 10 | 18 | /** |
| 11 | 19 | * 短信默认回调接口 |
| ... | ... | @@ -13,15 +21,33 @@ import javax.servlet.ServletRequest; |
| 13 | 21 | * @author zzf |
| 14 | 22 | * @date 2021/3/5 8:59 |
| 15 | 23 | */ |
| 16 | -@RestController("/sms/callback") | |
| 24 | +@Api(tags = "短信回调api") | |
| 25 | +@RestController | |
| 26 | +@RequestMapping("/sms/callback") | |
| 17 | 27 | public class SmsDefaultCallbackController { |
| 18 | 28 | |
| 19 | 29 | @Resource |
| 20 | 30 | private SysSmsService smsService; |
| 21 | 31 | |
| 22 | - @RequestMapping("/sms-send") | |
| 23 | - public Object sendSmsCallback(ServletRequest request){ | |
| 32 | + | |
| 33 | + @ApiOperation(value = "短信发送回调接口") | |
| 34 | + @PostMapping("/sms-send") | |
| 35 | + public Object sendSmsCallback(ServletRequest request) { | |
| 24 | 36 | return smsService.smsSendCallbackHandle(request); |
| 25 | 37 | } |
| 26 | 38 | |
| 39 | +/* | |
| 40 | + @Resource | |
| 41 | + private SmsSendStreamProducer smsSendStreamProducer; | |
| 42 | + | |
| 43 | + @ApiOperation("redis stream测试") | |
| 44 | + @GetMapping("/test/redis/stream") | |
| 45 | + public void test() { | |
| 46 | + SmsBody smsBody = new SmsBody(); | |
| 47 | + smsBody.setSmsLogId(1L); | |
| 48 | + smsBody.setTemplateCode("sdf"); | |
| 49 | + smsBody.setTemplateContent("sdf"); | |
| 50 | + smsSendStreamProducer.sendSmsSendMessage(smsBody, "18216466755"); | |
| 51 | + }*/ | |
| 52 | + | |
| 27 | 53 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SysSmsQueryLogMapper.java
| ... | ... | @@ -14,14 +14,21 @@ public interface SysSmsQueryLogMapper extends BaseMapper<SysSmsQueryLogDO> { |
| 14 | 14 | |
| 15 | 15 | /** |
| 16 | 16 | * 查询还没有获取发送结果的短信请求信息 |
| 17 | - * | |
| 18 | - * @return | |
| 19 | 17 | */ |
| 20 | 18 | default List<SysSmsQueryLogDO> selectNoResultQueryLogList() { |
| 21 | 19 | return this.selectList(new LambdaQueryWrapper<SysSmsQueryLogDO>() |
| 22 | 20 | .eq(SysSmsQueryLogDO::getSendStatus, SmsSendStatusEnum.QUERY_SUCCESS) |
| 23 | 21 | .eq(SysSmsQueryLogDO::getGotResult, DefaultBitFieldEnum.NO) |
| 24 | - .eq(SysSmsQueryLogDO::getHadCallback, DefaultBitFieldEnum.NO) | |
| 25 | 22 | ); |
| 26 | 23 | } |
| 24 | + | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 根据APIId修改对象 | |
| 28 | + */ | |
| 29 | + default boolean updateByApiId(SysSmsQueryLogDO queryLogDO, String apiId) { | |
| 30 | + return update(queryLogDO, new LambdaQueryWrapper<SysSmsQueryLogDO>() | |
| 31 | + .eq(SysSmsQueryLogDO::getApiId, apiId) | |
| 32 | + ) > 0; | |
| 33 | + } | |
| 27 | 34 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SysSmsChannelDO.java
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; | |
| 4 | 3 | import com.baomidou.mybatisplus.annotation.TableName; |
| 5 | -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; | |
| 6 | 4 | import lombok.Data; |
| 7 | 5 | import lombok.EqualsAndHashCode; |
| 8 | 6 | import lombok.experimental.Accessors; |
| 9 | 7 | |
| 10 | 8 | import java.io.Serializable; |
| 11 | 9 | import java.util.Date; |
| 12 | -import java.util.List; | |
| 13 | 10 | |
| 14 | 11 | /** |
| 15 | 12 | * 短信日志 |
| ... | ... | @@ -29,6 +26,11 @@ public class SysSmsQueryLogDO implements Serializable { |
| 29 | 26 | private Long id; |
| 30 | 27 | |
| 31 | 28 | /** |
| 29 | + * 第三方唯一标识 | |
| 30 | + */ | |
| 31 | + private String apiId; | |
| 32 | + | |
| 33 | + /** | |
| 32 | 34 | * 短信渠道编码(来自枚举类) |
| 33 | 35 | */ |
| 34 | 36 | private String channelCode; |
| ... | ... | @@ -46,8 +48,7 @@ public class SysSmsQueryLogDO implements Serializable { |
| 46 | 48 | /** |
| 47 | 49 | * 手机号 |
| 48 | 50 | */ |
| 49 | - @TableField(typeHandler = JacksonTypeHandler.class) | |
| 50 | - private List<String> phones; | |
| 51 | + private String phone; | |
| 51 | 52 | |
| 52 | 53 | /** |
| 53 | 54 | * 内容 |
| ... | ... | @@ -67,16 +68,6 @@ public class SysSmsQueryLogDO implements Serializable { |
| 67 | 68 | private Integer gotResult; |
| 68 | 69 | |
| 69 | 70 | /** |
| 70 | - * 是否拥有回调函数(0否 1是) | |
| 71 | - */ | |
| 72 | - private Integer hadCallback; | |
| 73 | - | |
| 74 | - /** | |
| 75 | - * 结果(对象json字符串) | |
| 76 | - */ | |
| 77 | - private String sendResultParam; | |
| 78 | - | |
| 79 | - /** | |
| 80 | 71 | * 备注 |
| 81 | 72 | */ |
| 82 | 73 | private String remark; |
| ... | ... | @@ -91,4 +82,9 @@ public class SysSmsQueryLogDO implements Serializable { |
| 91 | 82 | */ |
| 92 | 83 | private Date createTime; |
| 93 | 84 | |
| 85 | + /** | |
| 86 | + * 发送时间 | |
| 87 | + */ | |
| 88 | + private Date sendTime; | |
| 89 | + | |
| 94 | 90 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/dal/redis/RedisKeyConstants.java
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/dept/SysDeptRefreshConsumer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/consumer/dept/SysDeptRefreshConsumer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.consumer.dept; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.mq.consumer.dept; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; |
| 4 | -import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.mq.message.dept.SysDeptRefreshMessage; | |
| 5 | 5 | import cn.iocoder.dashboard.modules.system.service.dept.SysDeptService; |
| 6 | 6 | import lombok.extern.slf4j.Slf4j; |
| 7 | 7 | import org.springframework.stereotype.Component; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/dict/SysDictDataRefreshConsumer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/consumer/dict/SysDictDataRefreshConsumer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.consumer.dict; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.mq.consumer.dict; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; |
| 4 | -import cn.iocoder.dashboard.modules.system.mq.message.dict.SysDictDataRefreshMessage; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.mq.message.dict.SysDictDataRefreshMessage; | |
| 5 | 5 | import cn.iocoder.dashboard.modules.system.service.dict.SysDictDataService; |
| 6 | 6 | import lombok.extern.slf4j.Slf4j; |
| 7 | 7 | import org.springframework.stereotype.Component; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/permission/SysMenuRefreshConsumer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/consumer/permission/SysMenuRefreshConsumer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.consumer.permission; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.mq.consumer.permission; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; |
| 4 | -import cn.iocoder.dashboard.modules.system.mq.message.permission.SysMenuRefreshMessage; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.mq.message.permission.SysMenuRefreshMessage; | |
| 5 | 5 | import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService; |
| 6 | 6 | import lombok.extern.slf4j.Slf4j; |
| 7 | 7 | import org.springframework.stereotype.Component; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/permission/SysRoleMenuRefreshConsumer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/consumer/permission/SysRoleMenuRefreshConsumer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.consumer.permission; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.mq.consumer.permission; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; |
| 4 | -import cn.iocoder.dashboard.modules.system.mq.message.permission.SysRoleMenuRefreshMessage; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.mq.message.permission.SysRoleMenuRefreshMessage; | |
| 5 | 5 | import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; |
| 6 | 6 | import lombok.extern.slf4j.Slf4j; |
| 7 | 7 | import org.springframework.stereotype.Component; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/permission/SysRoleRefreshConsumer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/consumer/permission/SysRoleRefreshConsumer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.consumer.permission; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.mq.consumer.permission; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; |
| 4 | -import cn.iocoder.dashboard.modules.system.mq.message.permission.SysRoleRefreshMessage; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.mq.message.permission.SysRoleRefreshMessage; | |
| 5 | 5 | import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService; |
| 6 | 6 | import lombok.extern.slf4j.Slf4j; |
| 7 | 7 | import org.springframework.stereotype.Component; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/consumer/sms/SmsSendConsumer.java
0 → 100644
| 1 | +//package cn.iocoder.dashboard.modules.system.redis.mq.consumer.sms; | |
| 2 | +// | |
| 3 | +//import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; | |
| 4 | +//import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | |
| 5 | +//import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 6 | +//import cn.iocoder.dashboard.modules.system.redis.mq.message.dept.SysDeptRefreshMessage; | |
| 7 | +//import cn.iocoder.dashboard.modules.system.redis.stream.sms.SmsSendMessage; | |
| 8 | +//import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; | |
| 9 | +//import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; | |
| 10 | +//import lombok.extern.slf4j.Slf4j; | |
| 11 | +//import org.springframework.beans.factory.annotation.Autowired; | |
| 12 | +//import org.springframework.data.redis.connection.stream.Consumer; | |
| 13 | +//import org.springframework.data.redis.connection.stream.ObjectRecord; | |
| 14 | +//import org.springframework.data.redis.connection.stream.ReadOffset; | |
| 15 | +//import org.springframework.data.redis.connection.stream.StreamOffset; | |
| 16 | +//import org.springframework.data.redis.core.StringRedisTemplate; | |
| 17 | +//import org.springframework.stereotype.Component; | |
| 18 | +// | |
| 19 | +//import javax.annotation.Resource; | |
| 20 | +// | |
| 21 | +///** | |
| 22 | +// * 针对 {@link SysDeptRefreshMessage} 的消费者 | |
| 23 | +// * | |
| 24 | +// * @author 芋道源码 | |
| 25 | +// */ | |
| 26 | +//@Component | |
| 27 | +//@Slf4j | |
| 28 | +//public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> { | |
| 29 | +// | |
| 30 | +// @Resource | |
| 31 | +// private SysSmsChannelService smsChannelService; | |
| 32 | +// | |
| 33 | +// @Resource | |
| 34 | +// private SysSmsQueryLogService smsQueryLogService; | |
| 35 | +// | |
| 36 | +// @Autowired | |
| 37 | +// StringRedisTemplate redisTemplate; | |
| 38 | +// | |
| 39 | +// @Override | |
| 40 | +// public void onMessage(SmsSendMessage message) { | |
| 41 | +// | |
| 42 | +// redisTemplate.opsForStream().add(ObjectRecord.create("String", message)); | |
| 43 | +// | |
| 44 | +// redisTemplate.opsForStream().read(Consumer.from("",""), StreamOffset.create("", ReadOffset.lastConsumed())); | |
| 45 | +// | |
| 46 | +// | |
| 47 | +// | |
| 48 | +// log.info("[onMessage][收到 发送短信 消息], content: " + message.toString()); | |
| 49 | +// AbstractSmsClient smsClient = smsChannelService.getSmsClient(message.getSmsBody().getTemplateCode()); | |
| 50 | +// String templateApiId = smsChannelService.getSmsTemplateApiIdByCode(message.getSmsBody().getTemplateCode()); | |
| 51 | +// | |
| 52 | +// SmsResult result = smsClient.send(templateApiId, message.getSmsBody(), message.getTargetPhone()); | |
| 53 | +// smsQueryLogService.afterSendLog(message.getSmsBody().getSmsLogId(), result); | |
| 54 | +// } | |
| 55 | +// | |
| 56 | +//} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/dept/SysDeptRefreshMessage.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/message/dept/SysDeptRefreshMessage.java
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/dict/SysDictDataRefreshMessage.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/message/dict/SysDictDataRefreshMessage.java
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/permission/SysMenuRefreshMessage.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/message/permission/SysMenuRefreshMessage.java
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/permission/SysRoleMenuRefreshMessage.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/message/permission/SysRoleMenuRefreshMessage.java
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/permission/SysRoleRefreshMessage.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/message/permission/SysRoleRefreshMessage.java
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/dept/SysDeptProducer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/producer/dept/SysDeptProducer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.producer.dept; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.mq.producer.dept; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils; |
| 4 | -import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.mq.message.dept.SysDeptRefreshMessage; | |
| 5 | 5 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 6 | 6 | import org.springframework.stereotype.Component; |
| 7 | 7 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/dict/SysDictDataProducer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/producer/dict/SysDictDataProducer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.producer.dict; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.mq.producer.dict; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils; |
| 4 | -import cn.iocoder.dashboard.modules.system.mq.message.dict.SysDictDataRefreshMessage; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.mq.message.dict.SysDictDataRefreshMessage; | |
| 5 | 5 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 6 | 6 | import org.springframework.stereotype.Component; |
| 7 | 7 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/permission/SysMenuProducer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/producer/permission/SysMenuProducer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.producer.permission; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.mq.producer.permission; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils; |
| 4 | -import cn.iocoder.dashboard.modules.system.mq.message.permission.SysMenuRefreshMessage; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.mq.message.permission.SysMenuRefreshMessage; | |
| 5 | 5 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 6 | 6 | import org.springframework.stereotype.Component; |
| 7 | 7 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/permission/SysPermissionProducer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/producer/permission/SysPermissionProducer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.producer.permission; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.mq.producer.permission; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils; |
| 4 | -import cn.iocoder.dashboard.modules.system.mq.message.permission.SysRoleMenuRefreshMessage; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.mq.message.permission.SysRoleMenuRefreshMessage; | |
| 5 | 5 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 6 | 6 | import org.springframework.stereotype.Component; |
| 7 | 7 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/permission/SysRoleProducer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/producer/permission/SysRoleProducer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.producer.permission; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.mq.producer.permission; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils; |
| 4 | -import cn.iocoder.dashboard.modules.system.mq.message.permission.SysRoleRefreshMessage; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.mq.message.permission.SysRoleRefreshMessage; | |
| 5 | 5 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 6 | 6 | import org.springframework.stereotype.Component; |
| 7 | 7 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/redis/mq/producer/sms/SmsProducer.java
0 → 100644
| 1 | +//package cn.iocoder.dashboard.modules.system.redis.mq.producer.sms; | |
| 2 | +// | |
| 3 | +//import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils; | |
| 4 | +//import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 5 | +//import cn.iocoder.dashboard.modules.system.redis.stream.sms.SmsSendMessage; | |
| 6 | +//import org.springframework.data.redis.core.StringRedisTemplate; | |
| 7 | +//import org.springframework.stereotype.Component; | |
| 8 | +// | |
| 9 | +//import javax.annotation.Resource; | |
| 10 | +// | |
| 11 | +///** | |
| 12 | +// * 短信的 Producer | |
| 13 | +// */ | |
| 14 | +//@Component | |
| 15 | +//public class SmsProducer { | |
| 16 | +// | |
| 17 | +// @Resource | |
| 18 | +// private StringRedisTemplate stringRedisTemplate; | |
| 19 | +// | |
| 20 | +// /** | |
| 21 | +// * 发送 {@link SmsSendMessage} 消息 | |
| 22 | +// */ | |
| 23 | +// public void sendSmsSendMessage(SmsBody smsBody, String targetPhone) { | |
| 24 | +// SmsSendMessage message = new SmsSendMessage(); | |
| 25 | +// message.setSmsBody(smsBody); | |
| 26 | +// message.setTargetPhone(targetPhone); | |
| 27 | +// // TODO FROM 芋艿 TO ZZF:这块等未来改哈。这个方法目前是广播消费,会导致每个节点都发送一次。等后续封装出 redis stream 消息 | |
| 28 | +// RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message); | |
| 29 | +// } | |
| 30 | +// | |
| 31 | +//} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/redis/stream/StreamConsumerRunner.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.redis.stream; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.util.RedisStreamUtils; | |
| 4 | +import cn.iocoder.dashboard.modules.system.redis.stream.sms.SmsSendMessage; | |
| 5 | +import cn.iocoder.dashboard.modules.system.redis.stream.sms.SmsSendStreamConsumer; | |
| 6 | +import lombok.extern.slf4j.Slf4j; | |
| 7 | +import org.springframework.beans.factory.DisposableBean; | |
| 8 | +import org.springframework.boot.ApplicationArguments; | |
| 9 | +import org.springframework.boot.ApplicationRunner; | |
| 10 | +import org.springframework.data.redis.connection.RedisConnectionFactory; | |
| 11 | +import org.springframework.data.redis.connection.stream.*; | |
| 12 | +import org.springframework.data.redis.core.StringRedisTemplate; | |
| 13 | +import org.springframework.data.redis.serializer.StringRedisSerializer; | |
| 14 | +import org.springframework.data.redis.stream.StreamMessageListenerContainer; | |
| 15 | +import org.springframework.data.redis.stream.StreamMessageListenerContainer.StreamMessageListenerContainerOptions; | |
| 16 | +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | |
| 17 | +import org.springframework.stereotype.Component; | |
| 18 | +import org.springframework.util.ErrorHandler; | |
| 19 | + | |
| 20 | +import javax.annotation.Resource; | |
| 21 | +import java.net.InetAddress; | |
| 22 | +import java.net.UnknownHostException; | |
| 23 | +import java.time.Duration; | |
| 24 | + | |
| 25 | + | |
| 26 | +@Slf4j | |
| 27 | +@Component | |
| 28 | +public class StreamConsumerRunner implements ApplicationRunner, DisposableBean { | |
| 29 | + | |
| 30 | + @Resource | |
| 31 | + RedisConnectionFactory redisConnectionFactory; | |
| 32 | + | |
| 33 | + @Resource | |
| 34 | + ThreadPoolTaskExecutor threadPoolTaskExecutor; | |
| 35 | + | |
| 36 | + @Resource | |
| 37 | + SmsSendStreamConsumer streamMessageListener; | |
| 38 | + | |
| 39 | + @Resource | |
| 40 | + StringRedisTemplate stringRedisTemplate; | |
| 41 | + | |
| 42 | + private StreamMessageListenerContainer<String, ObjectRecord<String, SmsSendMessage>> streamMessageListenerContainer; | |
| 43 | + | |
| 44 | + @Override | |
| 45 | + public void run(ApplicationArguments args) throws UnknownHostException { | |
| 46 | + | |
| 47 | + StreamInfo.XInfoGroups groups = stringRedisTemplate.opsForStream().groups(RedisStreamUtils.KEY_SMS_SEND); | |
| 48 | + if (groups.isEmpty()) { | |
| 49 | + stringRedisTemplate.opsForStream().createGroup(RedisStreamUtils.KEY_SMS_SEND, RedisStreamUtils.GROUP_SMS_SEND); | |
| 50 | + } | |
| 51 | + | |
| 52 | + | |
| 53 | + // 创建配置对象 | |
| 54 | + StreamMessageListenerContainerOptions<String, ObjectRecord<String, SmsSendMessage>> streamMessageListenerContainerOptions = StreamMessageListenerContainerOptions | |
| 55 | + .builder() | |
| 56 | + // 一次性最多拉取多少条消息 | |
| 57 | + .batchSize(10) | |
| 58 | + // 执行消息轮询的执行器 | |
| 59 | + .executor(this.threadPoolTaskExecutor) | |
| 60 | + // 消息消费异常的handler | |
| 61 | + .errorHandler(new ErrorHandler() { | |
| 62 | + @Override | |
| 63 | + public void handleError(Throwable t) { | |
| 64 | + // throw new RuntimeException(t); | |
| 65 | + t.printStackTrace(); | |
| 66 | + } | |
| 67 | + }) | |
| 68 | + // 超时时间,设置为0,表示不超时(超时后会抛出异常) | |
| 69 | + .pollTimeout(Duration.ZERO) | |
| 70 | + // 序列化器 | |
| 71 | + .serializer(new StringRedisSerializer()) | |
| 72 | + .targetType(SmsSendMessage.class) | |
| 73 | + .build(); | |
| 74 | + | |
| 75 | + // 根据配置对象创建监听容器对象 | |
| 76 | + StreamMessageListenerContainer<String, ObjectRecord<String, SmsSendMessage>> streamMessageListenerContainer = StreamMessageListenerContainer | |
| 77 | + .create(this.redisConnectionFactory, streamMessageListenerContainerOptions); | |
| 78 | + | |
| 79 | + // 使用监听容器对象开始监听消费(使用的是手动确认方式) | |
| 80 | + streamMessageListenerContainer.receive(Consumer.from(RedisStreamUtils.GROUP_SMS_SEND, InetAddress.getLocalHost().getHostName()), | |
| 81 | + StreamOffset.create(RedisStreamUtils.KEY_SMS_SEND, ReadOffset.lastConsumed()), this.streamMessageListener); | |
| 82 | + | |
| 83 | + this.streamMessageListenerContainer = streamMessageListenerContainer; | |
| 84 | + // 启动监听 | |
| 85 | + this.streamMessageListenerContainer.start(); | |
| 86 | + | |
| 87 | + } | |
| 88 | + | |
| 89 | + @Override | |
| 90 | + public void destroy() throws Exception { | |
| 91 | + this.streamMessageListenerContainer.stop(); | |
| 92 | + } | |
| 93 | +} | |
| 0 | 94 | \ No newline at end of file | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SmsSendMessage.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/stream/sms/SmsSendMessage.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.message.sms; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.stream.sms; | |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage; | |
| 4 | 3 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 5 | 4 | import lombok.Data; |
| 6 | 5 | |
| 7 | -import java.util.Collection; | |
| 8 | -import java.util.List; | |
| 9 | - | |
| 10 | 6 | /** |
| 11 | 7 | * 部门数据刷新 Message |
| 12 | 8 | */ |
| 13 | 9 | @Data |
| 14 | -public class SmsSendMessage implements ChannelMessage { | |
| 10 | +public class SmsSendMessage { | |
| 15 | 11 | |
| 16 | 12 | private SmsBody smsBody; |
| 17 | 13 | |
| 18 | - private List<String> targetPhones; | |
| 19 | - | |
| 20 | - @Override | |
| 21 | - public String getChannel() { | |
| 22 | - return "sms.send"; | |
| 23 | - } | |
| 14 | + private String targetPhone; | |
| 24 | 15 | |
| 25 | 16 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/stream/sms/SmsSendStreamConsumer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.consumer.sms; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.stream.sms; | |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; | |
| 4 | 3 | import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 5 | 5 | import cn.iocoder.dashboard.framework.sms.core.SmsResult; |
| 6 | -import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage; | |
| 7 | -import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage; | |
| 8 | 6 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; |
| 9 | 7 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; |
| 8 | +import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 10 | 9 | import lombok.extern.slf4j.Slf4j; |
| 10 | +import org.springframework.data.redis.connection.stream.ObjectRecord; | |
| 11 | +import org.springframework.data.redis.stream.StreamListener; | |
| 11 | 12 | import org.springframework.stereotype.Component; |
| 12 | 13 | |
| 13 | 14 | import javax.annotation.Resource; |
| 14 | 15 | |
| 15 | 16 | /** |
| 16 | - * 针对 {@link SysDeptRefreshMessage} 的消费者 | |
| 17 | + * 短信发送流消息监听器 | |
| 17 | 18 | * |
| 18 | - * @author 芋道源码 | |
| 19 | + * @author zzf | |
| 20 | + * @date 2021/3/9 16:35 | |
| 19 | 21 | */ |
| 20 | -@Component | |
| 21 | 22 | @Slf4j |
| 22 | -public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> { | |
| 23 | +@Component | |
| 24 | +public class SmsSendStreamConsumer implements StreamListener<String, ObjectRecord<String, SmsSendMessage>> { | |
| 23 | 25 | |
| 24 | 26 | @Resource |
| 25 | 27 | private SysSmsChannelService smsChannelService; |
| ... | ... | @@ -28,14 +30,14 @@ public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessa |
| 28 | 30 | private SysSmsQueryLogService smsQueryLogService; |
| 29 | 31 | |
| 30 | 32 | @Override |
| 31 | - public void onMessage(SmsSendMessage message) { | |
| 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); | |
| 33 | + public void onMessage(ObjectRecord<String, SmsSendMessage> record) { | |
| 34 | + SmsSendMessage message = record.getValue(); | |
| 35 | + SmsBody body = message.getSmsBody(); | |
| 36 | + log.info("[onMessage][收到 发送短信 消息], content: " + JsonUtils.toJsonString(body)); | |
| 37 | + AbstractSmsClient smsClient = smsChannelService.getSmsClient(body.getTemplateCode()); | |
| 38 | + String templateApiId = smsChannelService.getSmsTemplateApiIdByCode(body.getTemplateCode()); | |
| 39 | + | |
| 40 | + SmsResult result = smsClient.send(templateApiId, body, message.getTargetPhone()); | |
| 41 | + smsQueryLogService.afterSendLog(body.getSmsLogId(), result); | |
| 39 | 42 | } |
| 40 | - | |
| 41 | 43 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SmsProducer.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/redis/stream/sms/SmsSendStreamProducer.java
| 1 | -package cn.iocoder.dashboard.modules.system.mq.producer.sms; | |
| 1 | +package cn.iocoder.dashboard.modules.system.redis.stream.sms; | |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.framework.redis.core.util.RedisMessageUtils; | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.util.RedisStreamUtils; | |
| 4 | 4 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 5 | -import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage; | |
| 5 | +import lombok.extern.slf4j.Slf4j; | |
| 6 | 6 | import org.springframework.data.redis.core.StringRedisTemplate; |
| 7 | 7 | import org.springframework.stereotype.Component; |
| 8 | 8 | |
| 9 | 9 | import javax.annotation.Resource; |
| 10 | -import java.util.List; | |
| 11 | 10 | |
| 12 | 11 | /** |
| 13 | - * 短信的 Producer | |
| 12 | + * 短信发送流消息监听器 | |
| 13 | + * | |
| 14 | + * @author zzf | |
| 15 | + * @date 2021/3/9 16:35 | |
| 14 | 16 | */ |
| 17 | +@Slf4j | |
| 15 | 18 | @Component |
| 16 | -public class SmsProducer { | |
| 19 | +public class SmsSendStreamProducer { | |
| 17 | 20 | |
| 18 | 21 | @Resource |
| 19 | 22 | private StringRedisTemplate stringRedisTemplate; |
| ... | ... | @@ -21,12 +24,12 @@ public class SmsProducer { |
| 21 | 24 | /** |
| 22 | 25 | * 发送 {@link SmsSendMessage} 消息 |
| 23 | 26 | */ |
| 24 | - public void sendSmsSendMessage(SmsBody smsBody, List<String> targetPhoneList) { | |
| 27 | + public void sendSmsSendMessage(SmsBody smsBody, String targetPhone) { | |
| 25 | 28 | SmsSendMessage message = new SmsSendMessage(); |
| 26 | 29 | message.setSmsBody(smsBody); |
| 27 | - message.setTargetPhones(targetPhoneList); | |
| 28 | - // TODO FROM 芋艿 TO ZZF:这块等未来改哈。这个方法目前是广播消费,会导致每个节点都发送一次。等后续封装出 redis stream 消息 | |
| 29 | - RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message); | |
| 30 | + message.setTargetPhone(targetPhone); | |
| 31 | + | |
| 32 | + RedisStreamUtils.sendChannelMessage(stringRedisTemplate, message); | |
| 30 | 33 | } |
| 31 | 34 | |
| 32 | 35 | -} |
| 36 | +} | |
| 33 | 37 | \ No newline at end of file | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/dept/impl/SysDeptServiceImpl.java
| ... | ... | @@ -11,7 +11,7 @@ import cn.iocoder.dashboard.modules.system.convert.dept.SysDeptConvert; |
| 11 | 11 | import cn.iocoder.dashboard.modules.system.dal.mysql.dao.dept.SysDeptMapper; |
| 12 | 12 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dept.SysDeptDO; |
| 13 | 13 | import cn.iocoder.dashboard.modules.system.enums.dept.DeptIdEnum; |
| 14 | -import cn.iocoder.dashboard.modules.system.mq.producer.dept.SysDeptProducer; | |
| 14 | +import cn.iocoder.dashboard.modules.system.redis.mq.producer.dept.SysDeptProducer; | |
| 15 | 15 | import cn.iocoder.dashboard.modules.system.service.dept.SysDeptService; |
| 16 | 16 | import com.google.common.collect.ImmutableMap; |
| 17 | 17 | import com.google.common.collect.ImmutableMultimap; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/dict/impl/SysDictDataServiceImpl.java
| ... | ... | @@ -13,7 +13,7 @@ import cn.iocoder.dashboard.modules.system.convert.dict.SysDictDataConvert; |
| 13 | 13 | import cn.iocoder.dashboard.modules.system.dal.mysql.dao.dict.SysDictDataMapper; |
| 14 | 14 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dict.SysDictDataDO; |
| 15 | 15 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.dict.SysDictTypeDO; |
| 16 | -import cn.iocoder.dashboard.modules.system.mq.producer.dict.SysDictDataProducer; | |
| 16 | +import cn.iocoder.dashboard.modules.system.redis.mq.producer.dict.SysDictDataProducer; | |
| 17 | 17 | import cn.iocoder.dashboard.modules.system.service.dict.SysDictDataService; |
| 18 | 18 | import cn.iocoder.dashboard.modules.system.service.dict.SysDictTypeService; |
| 19 | 19 | import com.google.common.collect.ImmutableTable; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysMenuServiceImpl.java
| ... | ... | @@ -11,7 +11,7 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysMenuMappe |
| 11 | 11 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMenuDO; |
| 12 | 12 | import cn.iocoder.dashboard.modules.system.enums.permission.MenuIdEnum; |
| 13 | 13 | import cn.iocoder.dashboard.modules.system.enums.permission.MenuTypeEnum; |
| 14 | -import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysMenuProducer; | |
| 14 | +import cn.iocoder.dashboard.modules.system.redis.mq.producer.permission.SysMenuProducer; | |
| 15 | 15 | import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService; |
| 16 | 16 | import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; |
| 17 | 17 | import cn.iocoder.dashboard.util.collection.CollectionUtils; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysPermissionServiceImpl.java
| ... | ... | @@ -11,7 +11,7 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysMe |
| 11 | 11 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO; |
| 12 | 12 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleMenuDO; |
| 13 | 13 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysUserRoleDO; |
| 14 | -import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysPermissionProducer; | |
| 14 | +import cn.iocoder.dashboard.modules.system.redis.mq.producer.permission.SysPermissionProducer; | |
| 15 | 15 | import cn.iocoder.dashboard.modules.system.service.permission.SysMenuService; |
| 16 | 16 | import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; |
| 17 | 17 | import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/permission/impl/SysRoleServiceImpl.java
| ... | ... | @@ -15,7 +15,7 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dao.permission.SysRoleMappe |
| 15 | 15 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.permission.SysRoleDO; |
| 16 | 16 | import cn.iocoder.dashboard.modules.system.enums.permission.RoleCodeEnum; |
| 17 | 17 | import cn.iocoder.dashboard.modules.system.enums.permission.SysRoleTypeEnum; |
| 18 | -import cn.iocoder.dashboard.modules.system.mq.producer.permission.SysRoleProducer; | |
| 18 | +import cn.iocoder.dashboard.modules.system.redis.mq.producer.permission.SysRoleProducer; | |
| 19 | 19 | import cn.iocoder.dashboard.modules.system.service.permission.SysPermissionService; |
| 20 | 20 | import cn.iocoder.dashboard.modules.system.service.permission.SysRoleService; |
| 21 | 21 | import com.google.common.collect.ImmutableMap; | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsChannelService.java
| ... | ... | @@ -2,12 +2,14 @@ package cn.iocoder.dashboard.modules.system.service.sms; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.common.pojo.PageResult; |
| 4 | 4 | import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | |
| 5 | 6 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; |
| 6 | 7 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; |
| 7 | 8 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; |
| 8 | 9 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; |
| 9 | 10 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsChannelDO; |
| 10 | 11 | |
| 12 | +import javax.servlet.ServletRequest; | |
| 11 | 13 | import java.util.List; |
| 12 | 14 | |
| 13 | 15 | /** | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SysSmsQueryLogService.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.service.sms; |
| 3 | 3 | 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 | +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | |
| 6 | 7 | |
| 7 | 8 | import java.util.List; |
| 8 | 9 | |
| ... | ... | @@ -16,16 +17,16 @@ public interface SysSmsQueryLogService { |
| 16 | 17 | /** |
| 17 | 18 | * 发送短信前的日志处理 |
| 18 | 19 | * |
| 19 | - * @param smsBody 短信内容 | |
| 20 | - * @param targetPhones 发送对象手机号集合 | |
| 21 | - * @param client 短信客户端 | |
| 20 | + * @param smsBody 短信内容 | |
| 21 | + * @param targetPhone 发送对象手机号 | |
| 22 | + * @param client 短信客户端 | |
| 22 | 23 | * @return 生成的日志id |
| 23 | 24 | */ |
| 24 | 25 | // TODO FROM 芋艿 to ZZF: async 是针对发送的方式,对于日志不一定需要关心。这样,短信日志,实际就发送前插入,发送后更新结果. |
| 25 | 26 | // 这里只用于记录状态,毕竟异步可能推送失败,此时日志可记录该状态。 |
| 26 | 27 | |
| 27 | 28 | // TODO FROM 芋艿 to ZZF:短信日志,群发的情况,应该是每个手机一条哈。虽然是群发,但是可能部分成功,部分失败;对应到短信平台,实际也是多条。 |
| 28 | - void beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient client); | |
| 29 | + void beforeSendLog(SmsBody smsBody, String targetPhone, AbstractSmsClient client); | |
| 29 | 30 | |
| 30 | 31 | /** |
| 31 | 32 | * 发送消息后的日志处理 |
| ... | ... | @@ -35,4 +36,5 @@ public interface SysSmsQueryLogService { |
| 35 | 36 | */ |
| 36 | 37 | void afterSendLog(Long logId, SmsResult result); |
| 37 | 38 | |
| 39 | + void updateSendLogByResultDetail(SmsResultDetail smsResultDetail); | |
| 38 | 40 | } | ... | ... |
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 | +import cn.hutool.core.collection.CollectionUtil; | |
| 4 | +import cn.hutool.core.util.ArrayUtil; | |
| 3 | 5 | import cn.iocoder.dashboard.framework.sms.core.SmsBody; |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.enums.SmsChannelEnum; | |
| 4 | 7 | |
| 5 | 8 | import javax.servlet.ServletRequest; |
| 6 | 9 | import java.util.Arrays; |
| 7 | -import java.util.Collections; | |
| 8 | 10 | import java.util.List; |
| 9 | 11 | |
| 10 | 12 | /** |
| ... | ... | @@ -22,7 +24,12 @@ public interface SysSmsService { |
| 22 | 24 | * @param smsBody 消息内容 |
| 23 | 25 | * @param targetPhones 发送对象手机号列表 |
| 24 | 26 | */ |
| 25 | - void send(SmsBody smsBody, List<String> targetPhones); | |
| 27 | + default void send(SmsBody smsBody, List<String> targetPhones) { | |
| 28 | + if (CollectionUtil.isEmpty(targetPhones)) { | |
| 29 | + return; | |
| 30 | + } | |
| 31 | + targetPhones.forEach(s -> this.send(smsBody, s)); | |
| 32 | + } | |
| 26 | 33 | |
| 27 | 34 | /** |
| 28 | 35 | * 发送消息 |
| ... | ... | @@ -30,9 +37,7 @@ public interface SysSmsService { |
| 30 | 37 | * @param smsBody 消息内容 |
| 31 | 38 | * @param targetPhone 发送对象手机号 |
| 32 | 39 | */ |
| 33 | - default void send(SmsBody smsBody, String targetPhone) { | |
| 34 | - send(smsBody, Collections.singletonList(targetPhone)); | |
| 35 | - } | |
| 40 | + void send(SmsBody smsBody, String targetPhone); | |
| 36 | 41 | |
| 37 | 42 | /** |
| 38 | 43 | * 发送消息 |
| ... | ... | @@ -41,14 +46,18 @@ public interface SysSmsService { |
| 41 | 46 | * @param targetPhones 发送对象手机号数组 |
| 42 | 47 | */ |
| 43 | 48 | default void send(SmsBody smsBody, String... targetPhones) { |
| 49 | + if (ArrayUtil.isEmpty(targetPhones)) { | |
| 50 | + return; | |
| 51 | + } | |
| 44 | 52 | send(smsBody, Arrays.asList(targetPhones)); |
| 45 | 53 | } |
| 46 | 54 | |
| 47 | 55 | /** |
| 48 | 56 | * 处理短信发送回调函数 |
| 49 | 57 | * |
| 50 | - * @param request 请求 | |
| 58 | + * @param request 请求 | |
| 51 | 59 | * @return 响应数据 |
| 52 | 60 | */ |
| 53 | 61 | Object smsSendCallbackHandle(ServletRequest request); |
| 62 | + | |
| 54 | 63 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsQueryLogServiceImpl.java
| ... | ... | @@ -3,16 +3,15 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl; |
| 3 | 3 | 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 | +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | |
| 6 | 7 | import cn.iocoder.dashboard.framework.sms.core.property.SmsChannelProperty; |
| 7 | 8 | import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsQueryLogMapper; |
| 8 | 9 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SysSmsQueryLogDO; |
| 9 | 10 | import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; |
| 10 | 11 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; |
| 11 | -import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 12 | 12 | import org.springframework.stereotype.Service; |
| 13 | 13 | |
| 14 | 14 | import javax.annotation.Resource; |
| 15 | -import java.util.List; | |
| 16 | 15 | |
| 17 | 16 | /** |
| 18 | 17 | * 短信请求日志服务实现类 |
| ... | ... | @@ -27,14 +26,14 @@ public class SysSmsQueryLogServiceImpl implements SysSmsQueryLogService { |
| 27 | 26 | private SysSmsQueryLogMapper logMapper; |
| 28 | 27 | |
| 29 | 28 | @Override |
| 30 | - public void beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient client) { | |
| 29 | + public void beforeSendLog(SmsBody smsBody, String targetPhone, AbstractSmsClient client) { | |
| 31 | 30 | SysSmsQueryLogDO smsLog = new SysSmsQueryLogDO(); |
| 32 | 31 | SmsChannelProperty property = client.getProperty(); |
| 33 | 32 | |
| 34 | 33 | smsLog.setChannelCode(property.getCode()) |
| 35 | 34 | .setChannelId(property.getId()) |
| 36 | 35 | .setTemplateCode(smsBody.getTemplateCode()) |
| 37 | - .setPhones(targetPhones) | |
| 36 | + .setPhone(targetPhone) | |
| 38 | 37 | .setContent(smsBody.getParams().toString()); |
| 39 | 38 | |
| 40 | 39 | smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus()); |
| ... | ... | @@ -46,14 +45,19 @@ public class SysSmsQueryLogServiceImpl implements SysSmsQueryLogService { |
| 46 | 45 | public void afterSendLog(Long logId, SmsResult result) { |
| 47 | 46 | SysSmsQueryLogDO smsLog = new SysSmsQueryLogDO(); |
| 48 | 47 | smsLog.setId(logId); |
| 49 | - if (result.getSuccess()) { | |
| 50 | - smsLog.setSendStatus(SmsSendStatusEnum.QUERY_SUCCESS.getStatus()); | |
| 51 | - smsLog.setSendResultParam(result.getSendResultParam()); | |
| 52 | - } else { | |
| 53 | - smsLog.setSendStatus(SmsSendStatusEnum.QUERY_FAIL.getStatus()); | |
| 54 | - smsLog.setRemark(result.getMessage()); | |
| 55 | - } | |
| 48 | + smsLog.setApiId(result.getApiId()); | |
| 49 | + smsLog.setSendStatus(SmsSendStatusEnum.QUERY_FAIL.getStatus()); | |
| 50 | + smsLog.setRemark(result.getCode() + ": " + result.getMessage()); | |
| 56 | 51 | logMapper.updateById(smsLog); |
| 57 | 52 | } |
| 58 | 53 | |
| 54 | + @Override | |
| 55 | + public void updateSendLogByResultDetail(SmsResultDetail smsResultDetail) { | |
| 56 | + SysSmsQueryLogDO queryLogDO = new SysSmsQueryLogDO(); | |
| 57 | + queryLogDO.setSendStatus(smsResultDetail.getSendStatus()); | |
| 58 | + queryLogDO.setSendTime(smsResultDetail.getSendTime()); | |
| 59 | + queryLogDO.setRemark(smsResultDetail.getMessage()); | |
| 60 | + logMapper.updateByApiId(queryLogDO, smsResultDetail.getApiId()); | |
| 61 | + } | |
| 62 | + | |
| 59 | 63 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsSendLogServiceImpl.java
| ... | ... | @@ -2,7 +2,6 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollectionUtil; |
| 4 | 4 | import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; |
| 5 | -import cn.iocoder.dashboard.framework.sms.client.NeedQuerySendResultSmsClient; | |
| 6 | 5 | import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; |
| 7 | 6 | import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsQueryLogMapper; |
| 8 | 7 | import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SysSmsSendLogMapper; |
| ... | ... | @@ -62,7 +61,7 @@ public class SysSmsSendLogServiceImpl implements SysSmsSendLogService { |
| 62 | 61 | updateQueryLog.setId(queryLog.getId()); |
| 63 | 62 | |
| 64 | 63 | // 只处理实现了获取发送结果方法的短信客户端,理论上这里都是满足条件的,以防万一加个判断。 |
| 65 | - if (smsClient instanceof NeedQuerySendResultSmsClient) { | |
| 64 | + /*if (smsClient instanceof NeedQuerySendResultSmsClient) { | |
| 66 | 65 | //初始化点字段值 |
| 67 | 66 | queryLog2SendLong(insertSendLog, queryLog); |
| 68 | 67 | |
| ... | ... | @@ -89,7 +88,7 @@ public class SysSmsSendLogServiceImpl implements SysSmsSendLogService { |
| 89 | 88 | //理论上这里都是满足条件的,以防万一加个判断。 |
| 90 | 89 | updateQueryLog.setSendStatus(SmsSendStatusEnum.QUERY_SEND_FAIL.getStatus()); |
| 91 | 90 | smsQueryLogMapper.updateById(updateQueryLog); |
| 92 | - } | |
| 91 | + }*/ | |
| 93 | 92 | updateQueryLog.setSendStatus(SmsSendStatusEnum.SEND_SUCCESS.getStatus()); |
| 94 | 93 | updateQueryLog.setRemark(String.format("日志(id = %s)对应的客户端没有继承NeedQuerySendResultSmsClient, 不能获取短信结果。", queryLog.getId())); |
| 95 | 94 | smsQueryLogMapper.updateById(updateQueryLog); | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SysSmsServiceImpl.java
| ... | ... | @@ -2,15 +2,16 @@ 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.modules.system.mq.producer.sms.SmsProducer; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.core.SmsResultDetail; | |
| 7 | +import cn.iocoder.dashboard.modules.system.redis.stream.sms.SmsSendStreamProducer; | |
| 6 | 8 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsChannelService; |
| 7 | 9 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsQueryLogService; |
| 8 | 10 | import cn.iocoder.dashboard.modules.system.service.sms.SysSmsService; |
| 9 | -import org.springframework.scheduling.annotation.Scheduled; | |
| 10 | 11 | import org.springframework.stereotype.Service; |
| 11 | 12 | |
| 12 | 13 | import javax.annotation.Resource; |
| 13 | -import java.util.List; | |
| 14 | +import javax.servlet.ServletRequest; | |
| 14 | 15 | |
| 15 | 16 | /** |
| 16 | 17 | * 短信日志Service实现类 |
| ... | ... | @@ -28,15 +29,23 @@ public class SysSmsServiceImpl implements SysSmsService { |
| 28 | 29 | private SysSmsQueryLogService logService; |
| 29 | 30 | |
| 30 | 31 | @Resource |
| 31 | - private SmsProducer smsProducer; | |
| 32 | + private SmsSendStreamProducer smsProducer; | |
| 33 | + | |
| 34 | + @Resource | |
| 35 | + private SmsClientFactory smsClientFactory; | |
| 32 | 36 | |
| 33 | 37 | @Override |
| 34 | - public void send(SmsBody smsBody, List<String> targetPhones) { | |
| 38 | + public void send(SmsBody smsBody, String targetPhone) { | |
| 35 | 39 | AbstractSmsClient client = channelService.getSmsClient(smsBody.getTemplateCode()); |
| 36 | - logService.beforeSendLog(smsBody, targetPhones, client); | |
| 37 | - smsProducer.sendSmsSendMessage(smsBody, targetPhones); | |
| 40 | + logService.beforeSendLog(smsBody, targetPhone, client); | |
| 41 | + smsProducer.sendSmsSendMessage(smsBody, targetPhone); | |
| 38 | 42 | } |
| 39 | 43 | |
| 40 | - // TODO FROM 芋艿 to ZZF:可能要讨论下,对于短信发送来说,貌似只提供异步发送即可。对于业务来说,一定不能依赖短信的发送结果. | |
| 44 | + @Override | |
| 45 | + public Object smsSendCallbackHandle(ServletRequest request) { | |
| 46 | + SmsResultDetail smsResultDetail = smsClientFactory.getSmsResultDetailFromCallbackQuery(request); | |
| 47 | + logService.updateSendLogByResultDetail(smsResultDetail); | |
| 48 | + return smsResultDetail.getCallbackResponseBody(); | |
| 49 | + } | |
| 41 | 50 | |
| 42 | 51 | } | ... | ... |