Commit 009f33210680bb77a727636732ce23ab5c8c5fb5
1 parent
37c39365
重构短信功能
Showing
30 changed files
with
642 additions
and
439 deletions
sql/ruoyi-vue-pro.sql
| ... | ... | @@ -884,75 +884,4 @@ INSERT INTO `sys_user_role` VALUES (5, 100, 1, '', NULL, '', NULL, b'0'); |
| 884 | 884 | INSERT INTO `sys_user_role` VALUES (6, 100, 2, '', NULL, '', NULL, b'0'); |
| 885 | 885 | COMMIT; |
| 886 | 886 | |
| 887 | - | |
| 888 | --- ---------------------------- | |
| 889 | --- Table structure for sms_channel | |
| 890 | --- ---------------------------- | |
| 891 | -DROP TABLE IF EXISTS `sms_channel`; | |
| 892 | -CREATE TABLE `sms_channel` ( | |
| 893 | - `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', | |
| 894 | - `code` varchar(50) not null COMMENT '编码(来自枚举类 阿里、华为、七牛等)', | |
| 895 | - `api_key` varchar(100) NOT NULL COMMENT '账号id', | |
| 896 | - `api_secret` varchar(100) NOT NULL COMMENT '账号秘钥', | |
| 897 | - `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识', | |
| 898 | - `name` varchar(50) not null COMMENT '名称', | |
| 899 | - `signature` varchar(50) not null COMMENT '签名值', | |
| 900 | - `remark` varchar(200) NOT NULL COMMENT '备注', | |
| 901 | - | |
| 902 | - `status` tinyint(4) NOT NULL default 0 COMMENT '启用状态(0正常 1停用)', | |
| 903 | - `create_by` varchar(64) not null DEFAULT '' COMMENT '创建者', | |
| 904 | - `create_time` datetime DEFAULT NULL COMMENT '创建时间', | |
| 905 | - `update_by` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 906 | - `update_time` datetime DEFAULT NULL COMMENT '更新时间', | |
| 907 | - `deleted` bit(1) DEFAULT b'0' COMMENT '是否删除', | |
| 908 | - PRIMARY KEY (`id`) USING BTREE | |
| 909 | -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信渠道'; | |
| 910 | -/* | |
| 911 | - 优先级值一样时,按照id顺序取值 | |
| 912 | -*/ | |
| 913 | - | |
| 914 | --- ---------------------------- | |
| 915 | --- Table structure for sms_template | |
| 916 | --- ---------------------------- | |
| 917 | -DROP TABLE IF EXISTS `sms_template`; | |
| 918 | -CREATE TABLE `sms_template` ( | |
| 919 | - `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', | |
| 920 | - `channel_code` varchar(50) not null COMMENT '短信渠道编码(来自枚举类)', | |
| 921 | - `channel_id` bigint(20) not null COMMENT '短信渠道id (对于前端来说就是绑定一个签名)', | |
| 922 | - `type` tinyint(4) NOT NULL default 1 COMMENT '消息类型 [0验证码 1短信通知 2推广短信 3国际/港澳台消息]', | |
| 923 | - `biz_code` varchar(50) not null COMMENT '业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)', | |
| 924 | - `code` varchar(50) not null COMMENT '编码', | |
| 925 | - `name` varchar(50) not null COMMENT '名称', | |
| 926 | - `api_template_id` varchar(100) NOT NULL COMMENT '实际渠道模板唯一标识', | |
| 927 | - `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', | |
| 928 | - `params` varchar(200) NOT NULL DEFAULT '' COMMENT '参数数组(自动根据内容生成)', | |
| 929 | - `remark` varchar(200) NOT NULL COMMENT '备注', | |
| 930 | - | |
| 931 | - `status` tinyint(4) NOT NULL default 0 COMMENT '启用状态(0正常 1停用)', | |
| 932 | - `create_by` varchar(64) not null DEFAULT '' COMMENT '创建者', | |
| 933 | - `create_time` datetime DEFAULT NULL COMMENT '创建时间', | |
| 934 | - `update_by` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 935 | - `update_time` datetime DEFAULT NULL COMMENT '更新时间', | |
| 936 | - `deleted` bit(1) DEFAULT b'0' COMMENT '是否删除', | |
| 937 | - PRIMARY KEY (`id`) USING BTREE | |
| 938 | -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信模板'; | |
| 939 | - | |
| 940 | --- ---------------------------- | |
| 941 | --- Table structure for sms_log | |
| 942 | --- ---------------------------- | |
| 943 | -DROP TABLE IF EXISTS `sms_log`; | |
| 944 | -CREATE TABLE `sms_log` ( | |
| 945 | - `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', | |
| 946 | - `channel_code` varchar(50) not null COMMENT '短信渠道编码(来自枚举类)', | |
| 947 | - `api_sms_id` varchar(50) not null COMMENT '实际渠道短信唯一标识', | |
| 948 | - `template_id` bigint(20) NOT NULL COMMENT '模板id', | |
| 949 | - `phone` char(11) not null COMMENT '手机号', | |
| 950 | - `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', | |
| 951 | - `remark` varchar(200) NOT NULL COMMENT '备注', | |
| 952 | - `send_status` tinyint(4) NOT NULL default 0 COMMENT '发送状态(0发送中 1成功 2失败)', | |
| 953 | - `create_by` varchar(64) not null DEFAULT '' COMMENT '创建者', | |
| 954 | - `create_time` datetime DEFAULT NULL COMMENT '创建时间', | |
| 955 | - PRIMARY KEY (`id`) USING BTREE | |
| 956 | -) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='短信日志'; | |
| 957 | - | |
| 958 | 887 | SET FOREIGN_KEY_CHECKS = 1; | ... | ... |
sql/sms.sql
0 → 100644
| 1 | +/* | |
| 2 | + --2021.02.01 by fight, sms about table info | |
| 3 | +*/ | |
| 4 | + | |
| 5 | +-- ---------------------------- | |
| 6 | +-- Table structure for sms_channel | |
| 7 | +-- ---------------------------- | |
| 8 | +DROP TABLE IF EXISTS `sms_channel`; | |
| 9 | +CREATE TABLE `sms_channel` | |
| 10 | +( | |
| 11 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', | |
| 12 | + `code` varchar(50) NOT NULL COMMENT '编码(来自枚举类 阿里、华为、七牛等)', | |
| 13 | + `api_key` varchar(100) NOT NULL COMMENT '账号id', | |
| 14 | + `api_secret` varchar(100) NOT NULL COMMENT '账号秘钥', | |
| 15 | + `api_signature_id` varchar(100) NOT NULL COMMENT '实际渠道签名唯一标识', | |
| 16 | + `name` varchar(50) NOT NULL COMMENT '名称', | |
| 17 | + `signature` varchar(50) NOT NULL COMMENT '签名值', | |
| 18 | + `remark` varchar(200) NOT NULL COMMENT '备注', | |
| 19 | + | |
| 20 | + `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '启用状态(0正常 1停用)', | |
| 21 | + `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者', | |
| 22 | + `create_time` datetime DEFAULT NULL COMMENT '创建时间', | |
| 23 | + `update_by` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 24 | + `update_time` datetime DEFAULT NULL COMMENT '更新时间', | |
| 25 | + `deleted` bit(1) DEFAULT b'0' COMMENT '是否删除', | |
| 26 | + PRIMARY KEY (`id`) USING BTREE | |
| 27 | +) ENGINE = InnoDB | |
| 28 | + AUTO_INCREMENT = 1 | |
| 29 | + DEFAULT CHARSET = utf8mb4 COMMENT ='短信渠道'; | |
| 30 | +/* | |
| 31 | + 优先级值一样时,按照id顺序取值 | |
| 32 | +*/ | |
| 33 | + | |
| 34 | +-- ---------------------------- | |
| 35 | +-- Table structure for sms_template | |
| 36 | +-- ---------------------------- | |
| 37 | +DROP TABLE IF EXISTS `sms_template`; | |
| 38 | +CREATE TABLE `sms_template` | |
| 39 | +( | |
| 40 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', | |
| 41 | + `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)', | |
| 42 | + `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id (对于前端来说就是绑定一个签名)', | |
| 43 | + `type` tinyint(4) NOT NULL DEFAULT 1 COMMENT '消息类型 [0验证码 1短信通知 2推广短信 3国际/港澳台消息]', | |
| 44 | + `biz_code` varchar(50) NOT NULL COMMENT '业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板)', | |
| 45 | + `code` varchar(50) NOT NULL COMMENT '编码', | |
| 46 | + `name` varchar(50) NOT NULL COMMENT '名称', | |
| 47 | + `api_template_id` varchar(100) NOT NULL COMMENT '实际渠道模板唯一标识', | |
| 48 | + `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', | |
| 49 | + `params` varchar(200) NOT NULL DEFAULT '' COMMENT '参数数组(自动根据内容生成)', | |
| 50 | + `remark` varchar(200) NOT NULL COMMENT '备注', | |
| 51 | + | |
| 52 | + `status` tinyint(4) NOT NULL DEFAULT 0 COMMENT '启用状态(0正常 1停用)', | |
| 53 | + `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者', | |
| 54 | + `create_time` datetime DEFAULT NULL COMMENT '创建时间', | |
| 55 | + `update_by` varchar(64) DEFAULT '' COMMENT '更新者', | |
| 56 | + `update_time` datetime DEFAULT NULL COMMENT '更新时间', | |
| 57 | + `deleted` bit(1) DEFAULT b'0' COMMENT '是否删除', | |
| 58 | + PRIMARY KEY (`id`) USING BTREE | |
| 59 | +) ENGINE = InnoDB | |
| 60 | + AUTO_INCREMENT = 1 | |
| 61 | + DEFAULT CHARSET = utf8mb4 COMMENT ='短信模板'; | |
| 62 | + | |
| 63 | +-- ---------------------------- | |
| 64 | +-- Table structure for sms_log | |
| 65 | +-- ---------------------------- | |
| 66 | +DROP TABLE IF EXISTS `sms_log`; | |
| 67 | +CREATE TABLE `sms_log` | |
| 68 | +( | |
| 69 | + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增编号', | |
| 70 | + `channel_code` varchar(50) NOT NULL COMMENT '短信渠道编码(来自枚举类)', | |
| 71 | + `channel_id` bigint(20) NOT NULL COMMENT '短信渠道id', | |
| 72 | + `template_code` varchar(50) NOT NULL COMMENT '渠道编码', | |
| 73 | + `phones` char(11) NOT NULL COMMENT '手机号(数组json字符串)', | |
| 74 | + `content` varchar(1000) NOT NULL DEFAULT '' COMMENT '内容', | |
| 75 | + `remark` varchar(200) DEFAULT NULL COMMENT '备注', | |
| 76 | + `send_status` tinyint(4) NOT NULL DEFAULT 2 COMMENT '发送状态(1异步推送中 2发送中 3失败 4成功)', | |
| 77 | + `create_by` varchar(64) NOT NULL DEFAULT '' COMMENT '创建者', | |
| 78 | + `create_time` datetime DEFAULT NULL COMMENT '创建时间', | |
| 79 | + PRIMARY KEY (`id`) USING BTREE | |
| 80 | +) ENGINE = InnoDB | |
| 81 | + AUTO_INCREMENT = 1 | |
| 82 | + DEFAULT CHARSET = utf8mb4 COMMENT ='短信日志'; | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/SmsClientAdapter.java deleted
| 1 | -package cn.iocoder.dashboard.framework.sms; | |
| 2 | - | |
| 3 | -import cn.hutool.core.util.ObjectUtil; | |
| 4 | -import cn.iocoder.dashboard.common.exception.ServiceException; | |
| 5 | - | |
| 6 | -import java.util.Collection; | |
| 7 | -import java.util.Collections; | |
| 8 | -import java.util.Map; | |
| 9 | - | |
| 10 | -import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.SMS_CHANNEL_NOT_INIT; | |
| 11 | - | |
| 12 | -/** | |
| 13 | - * 抽象短信客户端工厂 | |
| 14 | - * | |
| 15 | - * @author zzf | |
| 16 | - * @date 2021/1/28 14:01 | |
| 17 | - */ | |
| 18 | -public class SmsClientAdapter { | |
| 19 | - | |
| 20 | - private final Map<Long, SmsClient<?>> smsSenderMap; | |
| 21 | - | |
| 22 | - public SmsClientAdapter(Map<Long, SmsClient<?>> smsSenderMap) { | |
| 23 | - if (ObjectUtil.isEmpty(smsSenderMap)) { | |
| 24 | - throw new ServiceException(SMS_CHANNEL_NOT_INIT); | |
| 25 | - } | |
| 26 | - this.smsSenderMap = smsSenderMap; | |
| 27 | - } | |
| 28 | - | |
| 29 | - public void flushClient(Map<Long, SmsClient<?>> smsSenderMap) { | |
| 30 | - this.smsSenderMap.clear(); | |
| 31 | - smsSenderMap.putAll(Collections.unmodifiableMap(smsSenderMap)); | |
| 32 | - } | |
| 33 | - | |
| 34 | - public SmsResult<?> send(Long channelId, SmsBody smsBody, Collection<String> targetPhone) { | |
| 35 | - SmsClient<?> smsClient = getSmsSender(channelId); | |
| 36 | - return smsClient.send(smsBody, targetPhone); | |
| 37 | - } | |
| 38 | - | |
| 39 | - private SmsClient<?> getSmsSender(Long channelId) { | |
| 40 | - return smsSenderMap.get(channelId); | |
| 41 | - } | |
| 42 | -} |
src/main/java/cn/iocoder/dashboard/framework/sms/client/AbstractSmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.client; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 抽象短息客户端 | |
| 7 | + * | |
| 8 | + * @author zzf | |
| 9 | + * @date 2021/2/1 9:28 | |
| 10 | + */ | |
| 11 | +public abstract class AbstractSmsClient<R> implements SmsClient<R> { | |
| 12 | + | |
| 13 | + /** | |
| 14 | + * 短信渠道参数 | |
| 15 | + */ | |
| 16 | + protected final SmsChannelPropertyVO channelVO; | |
| 17 | + | |
| 18 | + /** | |
| 19 | + * 构造阿里云短信发送处理 | |
| 20 | + * | |
| 21 | + * @param channelVO 阿里云短信配置 | |
| 22 | + */ | |
| 23 | + public AbstractSmsClient(SmsChannelPropertyVO channelVO) { | |
| 24 | + this.channelVO = channelVO; | |
| 25 | + } | |
| 26 | + | |
| 27 | + | |
| 28 | + public SmsChannelPropertyVO getProperty() { | |
| 29 | + return channelVO; | |
| 30 | + } | |
| 31 | + | |
| 32 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/sms/client/AliSmsClient.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/client/AliyunSmsClient.java
| 1 | -package cn.iocoder.dashboard.modules.system.sms.client; | |
| 1 | +package cn.iocoder.dashboard.framework.sms.client; | |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.framework.sms.SmsBody; | |
| 4 | -import cn.iocoder.dashboard.framework.sms.SmsClient; | |
| 5 | -import cn.iocoder.dashboard.framework.sms.SmsResult; | |
| 6 | -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 5 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO; | |
| 7 | 6 | import com.aliyuncs.DefaultAcsClient; |
| 8 | 7 | import com.aliyuncs.IAcsClient; |
| 9 | 8 | import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; |
| ... | ... | @@ -23,7 +22,7 @@ import java.util.Collection; |
| 23 | 22 | * @date 2021/1/25 14:17 |
| 24 | 23 | */ |
| 25 | 24 | @Slf4j |
| 26 | -public class AliSmsClient implements SmsClient<SendSmsResponse> { | |
| 25 | +public class AliyunSmsClient extends AbstractSmsClient<SendSmsResponse> { | |
| 27 | 26 | |
| 28 | 27 | private static final String OK = "OK"; |
| 29 | 28 | |
| ... | ... | @@ -33,8 +32,6 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> { |
| 33 | 32 | |
| 34 | 33 | private static final String ENDPOINT = "cn-hangzhou"; |
| 35 | 34 | |
| 36 | - private final SmsChannelAllVO channelVO; | |
| 37 | - | |
| 38 | 35 | private final IAcsClient acsClient; |
| 39 | 36 | |
| 40 | 37 | /** |
| ... | ... | @@ -42,9 +39,8 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> { |
| 42 | 39 | * |
| 43 | 40 | * @param channelVO 阿里云短信配置 |
| 44 | 41 | */ |
| 45 | - public AliSmsClient(SmsChannelAllVO channelVO) { | |
| 46 | - | |
| 47 | - this.channelVO = channelVO; | |
| 42 | + public AliyunSmsClient(SmsChannelPropertyVO channelVO) { | |
| 43 | + super(channelVO); | |
| 48 | 44 | |
| 49 | 45 | String accessKeyId = channelVO.getApiKey(); |
| 50 | 46 | String accessKeySecret = channelVO.getApiSecret(); |
| ... | ... | @@ -57,13 +53,13 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> { |
| 57 | 53 | |
| 58 | 54 | |
| 59 | 55 | @Override |
| 60 | - public SmsResult<SendSmsResponse> send(SmsBody msgBody, Collection<String> targets) { | |
| 56 | + public SmsResult<SendSmsResponse> send(SmsBody smsBody, Collection<String> targets) { | |
| 61 | 57 | SendSmsRequest request = new SendSmsRequest(); |
| 62 | 58 | request.setSysMethod(MethodType.POST); |
| 63 | 59 | request.setPhoneNumbers(StringUtils.join(targets, ",")); |
| 64 | 60 | request.setSignName(channelVO.getApiSignatureId()); |
| 65 | - request.setTemplateCode(channelVO.getTemplateByTemplateCode(msgBody.getCode()).getApiTemplateId()); | |
| 66 | - request.setTemplateParam(msgBody.getParamsStr()); | |
| 61 | + request.setTemplateCode(channelVO.getTemplateByTemplateCode(smsBody.getTemplateCode()).getApiTemplateId()); | |
| 62 | + request.setTemplateParam(smsBody.getParamsStr()); | |
| 67 | 63 | |
| 68 | 64 | try { |
| 69 | 65 | SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); |
| ... | ... | @@ -78,14 +74,15 @@ public class AliSmsClient implements SmsClient<SendSmsResponse> { |
| 78 | 74 | return resultBody; |
| 79 | 75 | } catch (Exception e) { |
| 80 | 76 | log.debug(e.getMessage(), e); |
| 77 | + return failResult("发送异常: " + e.getMessage()); | |
| 81 | 78 | } |
| 82 | - | |
| 83 | - return failResult(); | |
| 84 | 79 | } |
| 85 | 80 | |
| 86 | - @Override | |
| 87 | - public SmsResult<SendSmsResponse> sendAsync(SmsBody msgBody, Collection<String> targets) { | |
| 88 | - return null; | |
| 81 | + SmsResult<SendSmsResponse> failResult(String message) { | |
| 82 | + SmsResult<SendSmsResponse> resultBody = new SmsResult<>(); | |
| 83 | + resultBody.setSuccess(false); | |
| 84 | + resultBody.setMessage(message); | |
| 85 | + return resultBody; | |
| 89 | 86 | } |
| 90 | 87 | |
| 91 | 88 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/client/SmsClient.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.client; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 5 | + | |
| 6 | +import java.util.Collection; | |
| 7 | + | |
| 8 | +/** | |
| 9 | + * 短信父接口 | |
| 10 | + * | |
| 11 | + * @author zzf | |
| 12 | + * @date 2021/1/25 14:14 | |
| 13 | + */ | |
| 14 | +public interface SmsClient<R> { | |
| 15 | + | |
| 16 | + /** | |
| 17 | + * 发送消息 | |
| 18 | + * | |
| 19 | + * @param smsBody 消息内容 | |
| 20 | + * @param targets 发送对象列表 | |
| 21 | + * @return 是否发送成功 | |
| 22 | + */ | |
| 23 | + SmsResult<R> send(SmsBody smsBody, Collection<String> targets); | |
| 24 | + | |
| 25 | +} | |
| 0 | 26 | \ No newline at end of file | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/SmsBody.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsBody.java
| 1 | -package cn.iocoder.dashboard.framework.sms; | |
| 1 | +package cn.iocoder.dashboard.framework.sms.core; | |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.util.json.JsonUtils; |
| 4 | 4 | import lombok.Data; |
| 5 | 5 | |
| 6 | 6 | import java.util.Map; |
| 7 | +import java.util.UUID; | |
| 7 | 8 | |
| 8 | 9 | /** |
| 9 | 10 | * 消息内容实体类 |
| ... | ... | @@ -12,9 +13,14 @@ import java.util.Map; |
| 12 | 13 | public class SmsBody { |
| 13 | 14 | |
| 14 | 15 | /** |
| 16 | + * 消息日志id | |
| 17 | + */ | |
| 18 | + private Long smsLogId; | |
| 19 | + | |
| 20 | + /** | |
| 15 | 21 | * 模板编码 |
| 16 | 22 | */ |
| 17 | - private String code; | |
| 23 | + private String templateCode; | |
| 18 | 24 | |
| 19 | 25 | /** |
| 20 | 26 | * 参数列表 | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsClientFactory.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.framework.sms.core; | |
| 2 | + | |
| 3 | +import cn.hutool.core.util.ObjectUtil; | |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 5 | +import cn.iocoder.dashboard.common.enums.SmsChannelEnum; | |
| 6 | +import cn.iocoder.dashboard.common.exception.ServiceException; | |
| 7 | +import cn.iocoder.dashboard.common.exception.util.ServiceExceptionUtil; | |
| 8 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | |
| 9 | +import cn.iocoder.dashboard.framework.sms.client.AliyunSmsClient; | |
| 10 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO; | |
| 11 | +import org.springframework.stereotype.Component; | |
| 12 | + | |
| 13 | +import java.util.Map; | |
| 14 | +import java.util.concurrent.ConcurrentHashMap; | |
| 15 | + | |
| 16 | +import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | |
| 17 | + | |
| 18 | +/** | |
| 19 | + * 短信客户端工厂 | |
| 20 | + * | |
| 21 | + * @author zzf | |
| 22 | + * @date 2021/1/28 14:01 | |
| 23 | + */ | |
| 24 | +@Component | |
| 25 | +public class SmsClientFactory { | |
| 26 | + | |
| 27 | + private final Map<Long, AbstractSmsClient<?>> smsSenderMap = new ConcurrentHashMap<>(8); | |
| 28 | + | |
| 29 | + /** | |
| 30 | + * 创建短信客户端 | |
| 31 | + * | |
| 32 | + * @param propertyVO 参数对象 | |
| 33 | + * @return 客户端id(默认channelId) | |
| 34 | + */ | |
| 35 | + public Long createClient(SmsChannelPropertyVO propertyVO) { | |
| 36 | + if (StrUtil.isBlank(propertyVO.getCode())) { | |
| 37 | + throw ServiceExceptionUtil.exception(PARAM_VALUE_IS_NULL, "短信渠道编码"); | |
| 38 | + } | |
| 39 | + if (ObjectUtil.isNull(propertyVO.getId())) { | |
| 40 | + throw ServiceExceptionUtil.exception(PARAM_VALUE_IS_NULL, "短信渠道ID"); | |
| 41 | + } | |
| 42 | + | |
| 43 | + AbstractSmsClient<?> sender = createClient(SmsChannelEnum.getByCode(propertyVO.getCode()), propertyVO); | |
| 44 | + smsSenderMap.put(propertyVO.getId(), sender); | |
| 45 | + return propertyVO.getId(); | |
| 46 | + } | |
| 47 | + | |
| 48 | + private AbstractSmsClient<?> createClient(SmsChannelEnum channelEnum, SmsChannelPropertyVO channelVO) { | |
| 49 | + if (channelEnum == null) { | |
| 50 | + throw new ServiceException(INVALID_CHANNEL_CODE); | |
| 51 | + } | |
| 52 | + switch (channelEnum) { | |
| 53 | + case ALI: | |
| 54 | + return new AliyunSmsClient(channelVO); | |
| 55 | + // TODO fill more channel | |
| 56 | + default: | |
| 57 | + break; | |
| 58 | + } | |
| 59 | + throw new ServiceException(SMS_SENDER_NOT_FOUND); | |
| 60 | + } | |
| 61 | + | |
| 62 | + /** | |
| 63 | + * 获取短信客户端 | |
| 64 | + * | |
| 65 | + * @param channelId 渠道id | |
| 66 | + * @return 短信id | |
| 67 | + */ | |
| 68 | + public AbstractSmsClient<?> getClient(Long channelId) { | |
| 69 | + return smsSenderMap.get(channelId); | |
| 70 | + } | |
| 71 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/SmsResult.java renamed to src/main/java/cn/iocoder/dashboard/framework/sms/core/SmsResult.java
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/SmsChannelController.java
| ... | ... | @@ -44,11 +44,5 @@ public class SmsChannelController { |
| 44 | 44 | return success(service.createChannel(reqVO)); |
| 45 | 45 | } |
| 46 | 46 | |
| 47 | - @ApiOperation("刷新消息渠道信息") | |
| 48 | - @PutMapping("/flush") | |
| 49 | - public CommonResult<Boolean> flushChannel() { | |
| 50 | - return success(service.flushChannel()); | |
| 51 | - } | |
| 52 | - | |
| 53 | 47 | |
| 54 | 48 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsChannelAllVO.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsChannelPropertyVO.java
src/main/java/cn/iocoder/dashboard/modules/system/controller/sms/vo/SmsTemplateVO.java
| ... | ... | @@ -17,6 +17,7 @@ public class SmsTemplateVO { |
| 17 | 17 | * 业务编码(来自数据字典, 用户自定义业务场景 一个场景可以有多个模板) |
| 18 | 18 | */ |
| 19 | 19 | private String bizCode; |
| 20 | + | |
| 20 | 21 | /** |
| 21 | 22 | * 编码 |
| 22 | 23 | */ |
| ... | ... | @@ -27,4 +28,11 @@ public class SmsTemplateVO { |
| 27 | 28 | */ |
| 28 | 29 | private String apiTemplateId; |
| 29 | 30 | |
| 31 | + /** | |
| 32 | + * 内容 | |
| 33 | + */ | |
| 34 | + private String content; | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 30 | 38 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/convert/sms/SmsChannelConvert.java
| ... | ... | @@ -2,7 +2,7 @@ package cn.iocoder.dashboard.modules.system.convert.sms; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.common.enums.SmsChannelEnum; |
| 4 | 4 | import cn.iocoder.dashboard.common.pojo.PageResult; |
| 5 | -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; | |
| 5 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO; | |
| 6 | 6 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; |
| 7 | 7 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; |
| 8 | 8 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO; |
| ... | ... | @@ -28,7 +28,7 @@ public interface SmsChannelConvert { |
| 28 | 28 | |
| 29 | 29 | List<SmsChannelEnumRespVO> convertEnum(List<SmsChannelEnum> bean); |
| 30 | 30 | |
| 31 | - List<SmsChannelAllVO> convert(List<SmsChannelDO> bean); | |
| 31 | + List<SmsChannelPropertyVO> convert(List<SmsChannelDO> bean); | |
| 32 | 32 | |
| 33 | 33 | |
| 34 | 34 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dao/sms/SmsLogMapper.java
| 1 | 1 | package cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms; |
| 2 | 2 | |
| 3 | -import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLog; | |
| 3 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLogDO; | |
| 4 | 4 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| 5 | 5 | import org.apache.ibatis.annotations.Mapper; |
| 6 | 6 | |
| 7 | 7 | @Mapper |
| 8 | -public interface SmsLogMapper extends BaseMapper<SmsLog> { | |
| 8 | +public interface SmsLogMapper extends BaseMapper<SmsLogDO> { | |
| 9 | 9 | |
| 10 | 10 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SmsLog.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/dal/mysql/dataobject/sms/SmsLogDO.java
| ... | ... | @@ -3,6 +3,7 @@ package cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms; |
| 3 | 3 | import com.baomidou.mybatisplus.annotation.TableName; |
| 4 | 4 | import lombok.Data; |
| 5 | 5 | import lombok.EqualsAndHashCode; |
| 6 | +import lombok.experimental.Accessors; | |
| 6 | 7 | |
| 7 | 8 | import java.io.Serializable; |
| 8 | 9 | import java.util.Date; |
| ... | ... | @@ -15,8 +16,9 @@ import java.util.Date; |
| 15 | 16 | */ |
| 16 | 17 | @Data |
| 17 | 18 | @EqualsAndHashCode |
| 19 | +@Accessors(chain = true) | |
| 18 | 20 | @TableName(value = "sms_log", autoResultMap = true) |
| 19 | -public class SmsLog implements Serializable { | |
| 21 | +public class SmsLogDO implements Serializable { | |
| 20 | 22 | |
| 21 | 23 | /** |
| 22 | 24 | * 自增编号 |
| ... | ... | @@ -29,19 +31,19 @@ public class SmsLog implements Serializable { |
| 29 | 31 | private String channelCode; |
| 30 | 32 | |
| 31 | 33 | /** |
| 32 | - * 实际渠道短信唯一标识 | |
| 34 | + * 短信渠道id | |
| 33 | 35 | */ |
| 34 | - private String apiSmsId; | |
| 36 | + private Long channelId; | |
| 35 | 37 | |
| 36 | 38 | /** |
| 37 | 39 | * 模板id |
| 38 | 40 | */ |
| 39 | - private Long templateId; | |
| 41 | + private String templateCode; | |
| 40 | 42 | |
| 41 | 43 | /** |
| 42 | - * 手机号 | |
| 44 | + * 手机号(数组json字符串) | |
| 43 | 45 | */ |
| 44 | - private String phone; | |
| 46 | + private String phones; | |
| 45 | 47 | |
| 46 | 48 | /** |
| 47 | 49 | * 内容 |
| ... | ... | @@ -54,7 +56,7 @@ public class SmsLog implements Serializable { |
| 54 | 56 | private String remark; |
| 55 | 57 | |
| 56 | 58 | /** |
| 57 | - * 发送状态(0发送中 1成功 2失败) | |
| 59 | + * 发送状态(1异步推送中 2发送中 3失败 4成功) | |
| 58 | 60 | */ |
| 59 | 61 | private Integer sendStatus; |
| 60 | 62 | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/enums/SysErrorCodeConstants.java
| ... | ... | @@ -83,5 +83,6 @@ public interface SysErrorCodeConstants { |
| 83 | 83 | ErrorCode SMS_TEMPLATE_NOT_FOUND = new ErrorCode(1003001003, "没有短信模板信息, 请初始化sms_template表数据。"); |
| 84 | 84 | ErrorCode SMS_SENDER_NOT_FOUND = new ErrorCode(1003001004, "没有找到对应的短信发送对象,请检查sms_channel表和sms_template表数据"); |
| 85 | 85 | ErrorCode INVALID_CHANNEL_CODE = new ErrorCode(1003001005, "非法的短信渠道code,请检查sms_channel表的code值是否与SmsChannelEnum中的code值一致。"); |
| 86 | + ErrorCode PARAM_VALUE_IS_NULL = new ErrorCode(1003001006, "参数【{}】不能为空"); | |
| 86 | 87 | |
| 87 | 88 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/enums/sms/SmsSendStatusEnum.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.enums.sms; | |
| 2 | + | |
| 3 | +import lombok.AllArgsConstructor; | |
| 4 | +import lombok.Getter; | |
| 5 | + | |
| 6 | +/** | |
| 7 | + * 短信发送状态 | |
| 8 | + * | |
| 9 | + * @author zzf | |
| 10 | + * @date 2021/2/1 13:39 | |
| 11 | + */ | |
| 12 | +@Getter | |
| 13 | +@AllArgsConstructor | |
| 14 | +public enum SmsSendStatusEnum { | |
| 15 | + | |
| 16 | + //异步转发中 | |
| 17 | + ASYNC(1), | |
| 18 | + | |
| 19 | + //发送中 | |
| 20 | + SENDING(2), | |
| 21 | + | |
| 22 | + //失败 | |
| 23 | + FAIL(3), | |
| 24 | + | |
| 25 | + //成功 | |
| 26 | + SUCCESS(4); | |
| 27 | + | |
| 28 | + private final int status; | |
| 29 | + | |
| 30 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/consumer/sms/SmsSendConsumer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.consumer.sms; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.pubsub.AbstractChannelMessageListener; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 5 | +import cn.iocoder.dashboard.modules.system.mq.message.dept.SysDeptRefreshMessage; | |
| 6 | +import cn.iocoder.dashboard.modules.system.mq.message.sms.SmsSendMessage; | |
| 7 | +import cn.iocoder.dashboard.modules.system.service.sms.SmsService; | |
| 8 | +import lombok.extern.slf4j.Slf4j; | |
| 9 | +import org.springframework.stereotype.Component; | |
| 10 | + | |
| 11 | +import javax.annotation.Resource; | |
| 12 | + | |
| 13 | +/** | |
| 14 | + * 针对 {@link SysDeptRefreshMessage} 的消费者 | |
| 15 | + * | |
| 16 | + * @author 芋道源码 | |
| 17 | + */ | |
| 18 | +@Component | |
| 19 | +@Slf4j | |
| 20 | +public class SmsSendConsumer extends AbstractChannelMessageListener<SmsSendMessage> { | |
| 21 | + | |
| 22 | + @Resource | |
| 23 | + private SmsService smsService; | |
| 24 | + | |
| 25 | + @Override | |
| 26 | + public void onMessage(SmsSendMessage message) { | |
| 27 | + log.info("[onMessage][收到 发送短信 消息]"); | |
| 28 | + SmsResult<?> send = smsService.send(message.getSmsBody(), message.getTargetPhones()); | |
| 29 | + } | |
| 30 | + | |
| 31 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/message/sms/SmsSendMessage.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.mq.message.sms; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.redis.core.pubsub.ChannelMessage; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import java.util.Collection; | |
| 8 | +import java.util.List; | |
| 9 | + | |
| 10 | +/** | |
| 11 | + * 部门数据刷新 Message | |
| 12 | + */ | |
| 13 | +@Data | |
| 14 | +public class SmsSendMessage implements ChannelMessage { | |
| 15 | + | |
| 16 | + private SmsBody smsBody; | |
| 17 | + | |
| 18 | + private List<String> targetPhones; | |
| 19 | + | |
| 20 | + @Override | |
| 21 | + public String getChannel() { | |
| 22 | + return "sms.send"; | |
| 23 | + } | |
| 24 | + | |
| 25 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/mq/producer/sms/SmsProducer.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.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.mq.message.sms.SmsSendMessage; | |
| 6 | +import org.springframework.data.redis.core.StringRedisTemplate; | |
| 7 | +import org.springframework.stereotype.Component; | |
| 8 | + | |
| 9 | +import javax.annotation.Resource; | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 12 | +/** | |
| 13 | + * 短信的 Producer | |
| 14 | + */ | |
| 15 | +@Component | |
| 16 | +public class SmsProducer { | |
| 17 | + | |
| 18 | + @Resource | |
| 19 | + private StringRedisTemplate stringRedisTemplate; | |
| 20 | + | |
| 21 | + /** | |
| 22 | + * 发送 {@link SmsSendMessage} 消息 | |
| 23 | + */ | |
| 24 | + public void sendSmsSendMessage(SmsBody smsBody, List<String> targetPhoneList) { | |
| 25 | + SmsSendMessage message = new SmsSendMessage(); | |
| 26 | + message.setSmsBody(smsBody); | |
| 27 | + message.setTargetPhones(targetPhoneList); | |
| 28 | + RedisMessageUtils.sendChannelMessage(stringRedisTemplate, message); | |
| 29 | + } | |
| 30 | + | |
| 31 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsChannelService.java
| 1 | 1 | package cn.iocoder.dashboard.modules.system.service.sms; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.dashboard.common.pojo.PageResult; |
| 4 | -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | |
| 5 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO; | |
| 5 | 6 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; |
| 6 | 7 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; |
| 7 | 8 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; |
| ... | ... | @@ -17,18 +18,46 @@ import java.util.List; |
| 17 | 18 | */ |
| 18 | 19 | public interface SmsChannelService { |
| 19 | 20 | |
| 21 | + /** | |
| 22 | + * 初始化短信渠道 | |
| 23 | + */ | |
| 24 | + void initSmsClient(); | |
| 25 | + | |
| 26 | + /** | |
| 27 | + * 分页查询短信渠道信息 | |
| 28 | + * | |
| 29 | + * @param reqVO 参数对象 | |
| 30 | + * @return 短信渠道分页对象 | |
| 31 | + */ | |
| 20 | 32 | PageResult<SmsChannelDO> pageChannels(SmsChannelPageReqVO reqVO); |
| 21 | 33 | |
| 34 | + /** | |
| 35 | + * 创建新的渠道信息 | |
| 36 | + * | |
| 37 | + * @param reqVO 参数对象 | |
| 38 | + * @return 渠道id | |
| 39 | + */ | |
| 22 | 40 | Long createChannel(SmsChannelCreateReqVO reqVO); |
| 23 | 41 | |
| 42 | + /** | |
| 43 | + * 获取短信渠道枚举/渠道编码 | |
| 44 | + * | |
| 45 | + * @return 短信渠道枚举/渠道编码 | |
| 46 | + */ | |
| 24 | 47 | List<SmsChannelEnumRespVO> getChannelEnums(); |
| 25 | 48 | |
| 26 | 49 | /** |
| 50 | + * 根据短信模板编码获取短信客户端 | |
| 51 | + * | |
| 52 | + * @param templateCode 短信模板编码 | |
| 53 | + * @return 短信客户端 | |
| 54 | + */ | |
| 55 | + AbstractSmsClient<?> getClient(String templateCode); | |
| 56 | + | |
| 57 | + /** | |
| 27 | 58 | * 查询渠道(包含名下模块)信息集合 |
| 28 | 59 | * |
| 29 | 60 | * @return 渠道(包含名下模块)信息集合 |
| 30 | 61 | */ |
| 31 | - List<SmsChannelAllVO> listChannelAllEnabledInfo(); | |
| 32 | - | |
| 33 | - boolean flushChannel(); | |
| 62 | + List<SmsChannelPropertyVO> listChannelAllEnabledInfo(); | |
| 34 | 63 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsLogService.java
| 1 | 1 | package cn.iocoder.dashboard.modules.system.service.sms; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 6 | + | |
| 7 | +import java.util.List; | |
| 8 | + | |
| 3 | 9 | /** |
| 4 | 10 | * 短信渠道Service接口 |
| 5 | 11 | * |
| ... | ... | @@ -7,4 +13,23 @@ package cn.iocoder.dashboard.modules.system.service.sms; |
| 7 | 13 | * @date 2021/1/25 9:24 |
| 8 | 14 | */ |
| 9 | 15 | public interface SmsLogService { |
| 16 | + /** | |
| 17 | + * 发送短信前的日志处理 | |
| 18 | + * | |
| 19 | + * @param smsBody 短信内容 | |
| 20 | + * @param targetPhones 发送对象手机号集合 | |
| 21 | + * @param client 短信客户端 | |
| 22 | + * @param isAsync 是否异步发送 | |
| 23 | + * @return 生成的日志id | |
| 24 | + */ | |
| 25 | + Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient<?> client, Boolean isAsync); | |
| 26 | + | |
| 27 | + /** | |
| 28 | + * 发送消息后的日志处理 | |
| 29 | + * | |
| 30 | + * @param logId 日志id | |
| 31 | + * @param result 消息结果 | |
| 32 | + */ | |
| 33 | + void afterSendLog(Long logId, SmsResult<?> result); | |
| 34 | + | |
| 10 | 35 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/framework/sms/SmsClient.java renamed to src/main/java/cn/iocoder/dashboard/modules/system/service/sms/SmsService.java
| 1 | -package cn.iocoder.dashboard.framework.sms; | |
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms; | |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 3 | 5 | import org.apache.commons.lang3.StringUtils; |
| 4 | 6 | |
| 5 | 7 | import java.util.Arrays; |
| 6 | -import java.util.Collection; | |
| 7 | 8 | import java.util.Collections; |
| 9 | +import java.util.List; | |
| 8 | 10 | |
| 9 | 11 | /** |
| 10 | - * 短信父接口 | |
| 12 | + * 短信Service接口 | |
| 11 | 13 | * |
| 12 | 14 | * @author zzf |
| 13 | - * @date 2021/1/25 14:14 | |
| 15 | + * @date 2021/1/25 9:24 | |
| 14 | 16 | */ |
| 15 | -public interface SmsClient<R> { | |
| 17 | +public interface SmsService { | |
| 16 | 18 | |
| 17 | 19 | /** |
| 18 | 20 | * 发送消息 |
| 19 | 21 | * |
| 20 | - * @param msgBody 消息内容 | |
| 21 | - * @param targets 发送对象列表 | |
| 22 | + * @param smsBody 消息内容 | |
| 23 | + * @param targetPhones 发送对象手机号列表 | |
| 22 | 24 | * @return 是否发送成功 |
| 23 | 25 | */ |
| 24 | - SmsResult<R> send(SmsBody msgBody, Collection<String> targets); | |
| 26 | + SmsResult<?> send(SmsBody smsBody, List<String> targetPhones); | |
| 25 | 27 | |
| 26 | 28 | /** |
| 27 | 29 | * 发送消息 |
| 28 | 30 | * |
| 29 | - * @param msgBody 消息内容 | |
| 30 | - * @param target 发送对象 | |
| 31 | + * @param smsBody 消息内容 | |
| 32 | + * @param targetPhone 发送对象手机号 | |
| 31 | 33 | * @return 是否发送成功 |
| 32 | 34 | */ |
| 33 | - default SmsResult<R> send(SmsBody msgBody, String target) { | |
| 34 | - if (StringUtils.isBlank(target)) { | |
| 35 | - return failResult(); | |
| 35 | + default SmsResult<?> send(SmsBody smsBody, String targetPhone) { | |
| 36 | + if (StringUtils.isBlank(targetPhone)) { | |
| 37 | + return failResult("targetPhone must not null."); | |
| 36 | 38 | } |
| 37 | 39 | |
| 38 | - return send(msgBody, Collections.singletonList(target)); | |
| 40 | + return send(smsBody, Collections.singletonList(targetPhone)); | |
| 39 | 41 | } |
| 40 | 42 | |
| 41 | 43 | /** |
| 42 | 44 | * 发送消息 |
| 43 | 45 | * |
| 44 | - * @param msgBody 消息内容 | |
| 45 | - * @param targets 发送对象列表 | |
| 46 | + * @param smsBody 消息内容 | |
| 47 | + * @param targetPhones 发送对象手机号数组 | |
| 46 | 48 | * @return 是否发送成功 |
| 47 | 49 | */ |
| 48 | - default SmsResult<R> send(SmsBody msgBody, String... targets) { | |
| 49 | - if (targets == null) { | |
| 50 | - return failResult(); | |
| 50 | + default SmsResult<?> send(SmsBody smsBody, String... targetPhones) { | |
| 51 | + if (targetPhones == null) { | |
| 52 | + return failResult("targetPhones must not null."); | |
| 51 | 53 | } |
| 52 | 54 | |
| 53 | - return send(msgBody, Arrays.asList(targets)); | |
| 55 | + return send(smsBody, Arrays.asList(targetPhones)); | |
| 54 | 56 | } |
| 55 | 57 | |
| 56 | 58 | |
| 57 | 59 | /** |
| 58 | 60 | * 异步发送消息 |
| 59 | 61 | * |
| 60 | - * @param msgBody 消息内容 | |
| 61 | - * @param targets 发送对象列表 | |
| 62 | - * @return 是否发送成功 | |
| 62 | + * @param msgBody 消息内容 | |
| 63 | + * @param targetPhones 发送对象列表 | |
| 63 | 64 | */ |
| 64 | - SmsResult<R> sendAsync(SmsBody msgBody, Collection<String> targets); | |
| 65 | + void sendAsync(SmsBody msgBody, List<String> targetPhones); | |
| 65 | 66 | |
| 66 | 67 | /** |
| 67 | 68 | * 异步发送消息 |
| 68 | 69 | * |
| 69 | - * @param msgBody 消息内容 | |
| 70 | - * @param target 发送对象 | |
| 71 | - * @return 是否发送成功 | |
| 70 | + * @param msgBody 消息内容 | |
| 71 | + * @param targetPhone 发送对象 | |
| 72 | 72 | */ |
| 73 | - default SmsResult<R> sendAsync(SmsBody msgBody, String target) { | |
| 74 | - if (StringUtils.isBlank(target)) { | |
| 75 | - return failResult("target must not null."); | |
| 73 | + default void sendAsync(SmsBody msgBody, String targetPhone) { | |
| 74 | + if (StringUtils.isBlank(targetPhone)) { | |
| 75 | + return; | |
| 76 | 76 | } |
| 77 | - | |
| 78 | - return sendAsync(msgBody, Collections.singletonList(target)); | |
| 77 | + sendAsync(msgBody, Collections.singletonList(targetPhone)); | |
| 79 | 78 | } |
| 80 | 79 | |
| 81 | 80 | /** |
| 82 | 81 | * 异步发送消息 |
| 83 | 82 | * |
| 84 | - * @param msgBody 消息内容 | |
| 85 | - * @param targets 发送对象列表 | |
| 86 | - * @return 是否发送成功 | |
| 83 | + * @param msgBody 消息内容 | |
| 84 | + * @param targetPhones 发送对象列表 | |
| 87 | 85 | */ |
| 88 | - default SmsResult<R> sendAsync(SmsBody msgBody, String... targets) { | |
| 89 | - if (targets == null) { | |
| 90 | - return failResult("targets must not null."); | |
| 86 | + default void sendAsync(SmsBody msgBody, String... targetPhones) { | |
| 87 | + if (targetPhones == null) { | |
| 88 | + return; | |
| 91 | 89 | } |
| 92 | - | |
| 93 | - return sendAsync(msgBody, Arrays.asList(targets)); | |
| 90 | + sendAsync(msgBody, Arrays.asList(targetPhones)); | |
| 94 | 91 | } |
| 95 | 92 | |
| 96 | - default SmsResult<R> failResult() { | |
| 97 | - SmsResult<R> resultBody = new SmsResult<>(); | |
| 98 | - resultBody.setSuccess(false); | |
| 99 | - return resultBody; | |
| 100 | - } | |
| 101 | 93 | |
| 102 | - default SmsResult<R> failResult(String message) { | |
| 103 | - SmsResult<R> resultBody = failResult(); | |
| 94 | + default SmsResult<?> failResult(String message) { | |
| 95 | + SmsResult<?> resultBody = new SmsResult<>(); | |
| 96 | + resultBody.setSuccess(false); | |
| 104 | 97 | resultBody.setMessage(message); |
| 105 | 98 | return resultBody; |
| 106 | 99 | } |
| 107 | -} | |
| 108 | 100 | \ No newline at end of file |
| 101 | + | |
| 102 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsChannelServiceImpl.java
| ... | ... | @@ -3,7 +3,9 @@ package cn.iocoder.dashboard.modules.system.service.sms.impl; |
| 3 | 3 | import cn.hutool.core.util.ObjectUtil; |
| 4 | 4 | import cn.iocoder.dashboard.common.enums.SmsChannelEnum; |
| 5 | 5 | import cn.iocoder.dashboard.common.pojo.PageResult; |
| 6 | -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; | |
| 6 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | |
| 7 | +import cn.iocoder.dashboard.framework.sms.core.SmsClientFactory; | |
| 8 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO; | |
| 7 | 9 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelCreateReqVO; |
| 8 | 10 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.req.SmsChannelPageReqVO; |
| 9 | 11 | import cn.iocoder.dashboard.modules.system.controller.sms.vo.resp.SmsChannelEnumRespVO; |
| ... | ... | @@ -14,12 +16,16 @@ import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsTemplateMapper; |
| 14 | 16 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsChannelDO; |
| 15 | 17 | import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsTemplateDO; |
| 16 | 18 | import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService; |
| 19 | +import org.springframework.beans.factory.annotation.Autowired; | |
| 17 | 20 | import org.springframework.stereotype.Service; |
| 18 | 21 | |
| 22 | +import javax.annotation.PostConstruct; | |
| 19 | 23 | import javax.annotation.Resource; |
| 20 | 24 | import java.util.ArrayList; |
| 21 | 25 | import java.util.Arrays; |
| 22 | 26 | import java.util.List; |
| 27 | +import java.util.Map; | |
| 28 | +import java.util.concurrent.ConcurrentHashMap; | |
| 23 | 29 | |
| 24 | 30 | /** |
| 25 | 31 | * 短信渠道Service实现类 |
| ... | ... | @@ -30,6 +36,30 @@ import java.util.List; |
| 30 | 36 | @Service |
| 31 | 37 | public class SmsChannelServiceImpl implements SmsChannelService { |
| 32 | 38 | |
| 39 | + private final Map<String, Long> templateCode2ChannelIdMap = new ConcurrentHashMap<>(32); | |
| 40 | + | |
| 41 | + @Autowired | |
| 42 | + private SmsClientFactory smsClientFactory; | |
| 43 | + | |
| 44 | + /** | |
| 45 | + * 初始化短信客户端 | |
| 46 | + */ | |
| 47 | + @PostConstruct | |
| 48 | + @Override | |
| 49 | + public void initSmsClient() { | |
| 50 | + List<SmsChannelPropertyVO> smsChannelPropertyVOList = listChannelAllEnabledInfo(); | |
| 51 | + if (ObjectUtil.isEmpty(smsChannelPropertyVOList)) { | |
| 52 | + return; | |
| 53 | + } | |
| 54 | + smsChannelPropertyVOList.forEach(smsChannelPropertyVO -> { | |
| 55 | + Long clientId = smsClientFactory.createClient(smsChannelPropertyVO); | |
| 56 | + smsChannelPropertyVO.getTemplateList().forEach(smsTemplateVO -> { | |
| 57 | + templateCode2ChannelIdMap.put(smsTemplateVO.getCode(), clientId); | |
| 58 | + }); | |
| 59 | + }); | |
| 60 | + } | |
| 61 | + | |
| 62 | + | |
| 33 | 63 | @Resource |
| 34 | 64 | private SmsChannelMapper mapper; |
| 35 | 65 | |
| ... | ... | @@ -54,12 +84,17 @@ public class SmsChannelServiceImpl implements SmsChannelService { |
| 54 | 84 | } |
| 55 | 85 | |
| 56 | 86 | @Override |
| 57 | - public List<SmsChannelAllVO> listChannelAllEnabledInfo() { | |
| 87 | + public AbstractSmsClient<?> getClient(String templateCode) { | |
| 88 | + return smsClientFactory.getClient(templateCode2ChannelIdMap.get(templateCode)); | |
| 89 | + } | |
| 90 | + | |
| 91 | + @Override | |
| 92 | + public List<SmsChannelPropertyVO> listChannelAllEnabledInfo() { | |
| 58 | 93 | List<SmsChannelDO> channelDOList = mapper.selectEnabledList(); |
| 59 | 94 | if (ObjectUtil.isNull(channelDOList)) { |
| 60 | 95 | return null; |
| 61 | 96 | } |
| 62 | - List<SmsChannelAllVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList); | |
| 97 | + List<SmsChannelPropertyVO> channelAllVOList = SmsChannelConvert.INSTANCE.convert(channelDOList); | |
| 63 | 98 | |
| 64 | 99 | channelAllVOList.forEach(smsChannelDO -> { |
| 65 | 100 | |
| ... | ... | @@ -71,10 +106,4 @@ public class SmsChannelServiceImpl implements SmsChannelService { |
| 71 | 106 | }); |
| 72 | 107 | return channelAllVOList; |
| 73 | 108 | } |
| 74 | - | |
| 75 | - @Override | |
| 76 | - public boolean flushChannel() { | |
| 77 | - | |
| 78 | - return true; | |
| 79 | - } | |
| 80 | 109 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsLogServiceImpl.java
| 1 | 1 | package cn.iocoder.dashboard.modules.system.service.sms.impl; |
| 2 | 2 | |
| 3 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 6 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelPropertyVO; | |
| 7 | +import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO; | |
| 8 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dao.sms.SmsLogMapper; | |
| 9 | +import cn.iocoder.dashboard.modules.system.dal.mysql.dataobject.sms.SmsLogDO; | |
| 10 | +import cn.iocoder.dashboard.modules.system.enums.sms.SmsSendStatusEnum; | |
| 3 | 11 | import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService; |
| 12 | +import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 13 | +import cn.iocoder.dashboard.util.string.StrUtils; | |
| 4 | 14 | import org.springframework.stereotype.Service; |
| 5 | 15 | |
| 16 | +import javax.annotation.Resource; | |
| 17 | +import java.util.List; | |
| 18 | + | |
| 6 | 19 | /** |
| 7 | 20 | * 短信日志Service实现类 |
| 8 | 21 | * |
| ... | ... | @@ -12,4 +25,48 @@ import org.springframework.stereotype.Service; |
| 12 | 25 | @Service |
| 13 | 26 | public class SmsLogServiceImpl implements SmsLogService { |
| 14 | 27 | |
| 28 | + @Resource | |
| 29 | + private SmsLogMapper smsLogMapper; | |
| 30 | + | |
| 31 | + @Override | |
| 32 | + public Long beforeSendLog(SmsBody smsBody, List<String> targetPhones, AbstractSmsClient<?> client, Boolean isAsync) { | |
| 33 | + SmsLogDO smsLog = new SmsLogDO(); | |
| 34 | + if (smsBody.getSmsLogId() != null) { | |
| 35 | + smsLog.setId(smsBody.getSmsLogId()); | |
| 36 | + smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus()); | |
| 37 | + smsLogMapper.updateById(smsLog); | |
| 38 | + return smsBody.getSmsLogId(); | |
| 39 | + } else { | |
| 40 | + SmsChannelPropertyVO property = client.getProperty(); | |
| 41 | + SmsTemplateVO smsTemplate = property.getTemplateByTemplateCode(smsBody.getTemplateCode()); | |
| 42 | + | |
| 43 | + smsLog.setChannelCode(property.getCode()) | |
| 44 | + .setChannelId(property.getId()) | |
| 45 | + .setTemplateCode(smsTemplate.getCode()) | |
| 46 | + .setPhones(JsonUtils.toJsonString(targetPhones)) | |
| 47 | + .setContent(StrUtils.replace(smsTemplate.getContent(), smsBody.getParams())); | |
| 48 | + | |
| 49 | + if (isAsync) { | |
| 50 | + smsLog.setSendStatus(SmsSendStatusEnum.ASYNC.getStatus()); | |
| 51 | + } else { | |
| 52 | + smsLog.setSendStatus(SmsSendStatusEnum.SENDING.getStatus()); | |
| 53 | + } | |
| 54 | + smsLogMapper.insert(smsLog); | |
| 55 | + return smsLog.getId(); | |
| 56 | + } | |
| 57 | + } | |
| 58 | + | |
| 59 | + @Override | |
| 60 | + public void afterSendLog(Long logId, SmsResult<?> result) { | |
| 61 | + SmsLogDO smsLog = new SmsLogDO(); | |
| 62 | + smsLog.setId(logId); | |
| 63 | + if (result.getSuccess()) { | |
| 64 | + smsLog.setSendStatus(SmsSendStatusEnum.SUCCESS.getStatus()); | |
| 65 | + } else { | |
| 66 | + smsLog.setSendStatus(SmsSendStatusEnum.FAIL.getStatus()); | |
| 67 | + smsLog.setRemark(result.getMessage() + JsonUtils.toJsonString(result.getResult())); | |
| 68 | + } | |
| 69 | + smsLogMapper.updateById(smsLog); | |
| 70 | + } | |
| 71 | + | |
| 15 | 72 | } | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/service/sms/impl/SmsServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.dashboard.modules.system.service.sms.impl; | |
| 2 | + | |
| 3 | +import cn.iocoder.dashboard.framework.sms.client.AbstractSmsClient; | |
| 4 | +import cn.iocoder.dashboard.framework.sms.core.SmsBody; | |
| 5 | +import cn.iocoder.dashboard.framework.sms.core.SmsResult; | |
| 6 | +import cn.iocoder.dashboard.modules.system.mq.producer.sms.SmsProducer; | |
| 7 | +import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService; | |
| 8 | +import cn.iocoder.dashboard.modules.system.service.sms.SmsLogService; | |
| 9 | +import cn.iocoder.dashboard.modules.system.service.sms.SmsService; | |
| 10 | +import org.springframework.stereotype.Service; | |
| 11 | + | |
| 12 | +import javax.annotation.Resource; | |
| 13 | +import java.util.List; | |
| 14 | + | |
| 15 | +/** | |
| 16 | + * 短信日志Service实现类 | |
| 17 | + * | |
| 18 | + * @author zzf | |
| 19 | + * @date 2021/1/25 9:25 | |
| 20 | + */ | |
| 21 | +@Service | |
| 22 | +public class SmsServiceImpl implements SmsService { | |
| 23 | + | |
| 24 | + @Resource | |
| 25 | + private SmsChannelService channelService; | |
| 26 | + | |
| 27 | + @Resource | |
| 28 | + private SmsLogService smsLogService; | |
| 29 | + | |
| 30 | + @Resource | |
| 31 | + private SmsProducer smsProducer; | |
| 32 | + | |
| 33 | + @Override | |
| 34 | + public SmsResult<?> send(SmsBody smsBody, List<String> targetPhones) { | |
| 35 | + AbstractSmsClient<?> client = channelService.getClient(smsBody.getTemplateCode()); | |
| 36 | + Long logId = smsLogService.beforeSendLog(smsBody, targetPhones, client, false); | |
| 37 | + | |
| 38 | + SmsResult<?> result = client.send(smsBody, targetPhones); | |
| 39 | + | |
| 40 | + smsLogService.afterSendLog(logId, result); | |
| 41 | + | |
| 42 | + return result; | |
| 43 | + } | |
| 44 | + | |
| 45 | + @Override | |
| 46 | + public void sendAsync(SmsBody smsBody, List<String> targetPhones) { | |
| 47 | + AbstractSmsClient<?> client = channelService.getClient(smsBody.getTemplateCode()); | |
| 48 | + smsLogService.beforeSendLog(smsBody, targetPhones, client, true); | |
| 49 | + smsProducer.sendSmsSendMessage(smsBody, targetPhones); | |
| 50 | + } | |
| 51 | +} | ... | ... |
src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsConfiguration.java deleted
| 1 | -package cn.iocoder.dashboard.modules.system.sms; | |
| 2 | - | |
| 3 | -import cn.iocoder.dashboard.framework.sms.SmsClient; | |
| 4 | -import cn.iocoder.dashboard.framework.sms.SmsClientAdapter; | |
| 5 | -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; | |
| 6 | -import cn.iocoder.dashboard.modules.system.service.sms.SmsChannelService; | |
| 7 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
| 8 | -import org.springframework.context.annotation.Bean; | |
| 9 | -import org.springframework.context.annotation.Configuration; | |
| 10 | - | |
| 11 | -import javax.annotation.Resource; | |
| 12 | -import java.util.List; | |
| 13 | -import java.util.Map; | |
| 14 | - | |
| 15 | -/** | |
| 16 | - * 短信服务配置 | |
| 17 | - * | |
| 18 | - * @author guer | |
| 19 | - */ | |
| 20 | -@Configuration | |
| 21 | -@ConditionalOnProperty("sms.enabled") | |
| 22 | -public class SmsConfiguration { | |
| 23 | - | |
| 24 | - @Resource | |
| 25 | - private SmsChannelService channelService; | |
| 26 | - | |
| 27 | - @Bean | |
| 28 | - public SmsClientAdapter smsClientWrapper() { | |
| 29 | - List<SmsChannelAllVO> smsChannelAllVOList = channelService.listChannelAllEnabledInfo(); | |
| 30 | - Map<Long, SmsClient<?>> channelId2SmsClientMap = SmsSenderUtils.init(smsChannelAllVOList); | |
| 31 | - return new SmsClientAdapter(channelId2SmsClientMap); | |
| 32 | - } | |
| 33 | - | |
| 34 | -} |
src/main/java/cn/iocoder/dashboard/modules/system/sms/SmsSenderUtils.java deleted
| 1 | -package cn.iocoder.dashboard.modules.system.sms; | |
| 2 | - | |
| 3 | -import cn.hutool.core.util.ObjectUtil; | |
| 4 | -import cn.iocoder.dashboard.common.enums.SmsChannelEnum; | |
| 5 | -import cn.iocoder.dashboard.common.exception.ServiceException; | |
| 6 | -import cn.iocoder.dashboard.framework.sms.SmsBody; | |
| 7 | -import cn.iocoder.dashboard.framework.sms.SmsClient; | |
| 8 | -import cn.iocoder.dashboard.framework.sms.SmsClientAdapter; | |
| 9 | -import cn.iocoder.dashboard.framework.sms.SmsResult; | |
| 10 | -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsChannelAllVO; | |
| 11 | -import cn.iocoder.dashboard.modules.system.controller.sms.vo.SmsTemplateVO; | |
| 12 | -import cn.iocoder.dashboard.modules.system.sms.client.AliSmsClient; | |
| 13 | -import cn.iocoder.dashboard.modules.system.sms.proxy.SmsClientLogProxy; | |
| 14 | - | |
| 15 | -import java.util.*; | |
| 16 | -import java.util.concurrent.ConcurrentHashMap; | |
| 17 | - | |
| 18 | -import static cn.iocoder.dashboard.modules.system.enums.SysErrorCodeConstants.*; | |
| 19 | - | |
| 20 | -/** | |
| 21 | - * 短信发送者工厂 | |
| 22 | - * | |
| 23 | - * @author zzf | |
| 24 | - * @date 2021/1/25 16:18 | |
| 25 | - */ | |
| 26 | -public class SmsSenderUtils { | |
| 27 | - | |
| 28 | - /** | |
| 29 | - * 短信渠道id:短信客户端map | |
| 30 | - * key: channelId | |
| 31 | - * val: SmsClient | |
| 32 | - */ | |
| 33 | - private static final Map<Long, SmsClient<?>> smsSenderMap = new ConcurrentHashMap<>(8); | |
| 34 | - | |
| 35 | - /** | |
| 36 | - * 短信模板code: 短信渠道id map | |
| 37 | - * key: templateCode | |
| 38 | - * val: channelId | |
| 39 | - */ | |
| 40 | - private static final Map<String, Long> templateCode2ChannelIdMap = new HashMap<>(); | |
| 41 | - | |
| 42 | - /** | |
| 43 | - * 将短信渠道信息初始化成短信客户端 | |
| 44 | - * | |
| 45 | - * @param smsChannelAllVOList 短信渠道信息 | |
| 46 | - * @return 短信渠道id:短信客户端map | |
| 47 | - */ | |
| 48 | - public synchronized static Map<Long, SmsClient<?>> init(List<SmsChannelAllVO> smsChannelAllVOList) { | |
| 49 | - if (ObjectUtil.isEmpty(smsChannelAllVOList)) { | |
| 50 | - throw new ServiceException(SMS_CHANNEL_NOT_FOUND); | |
| 51 | - } | |
| 52 | - addSender(smsChannelAllVOList); | |
| 53 | - return smsSenderMap; | |
| 54 | - } | |
| 55 | - | |
| 56 | - /** | |
| 57 | - * 重置短信客户端信息 | |
| 58 | - * | |
| 59 | - * @param smsClientAdapter 短信客户端适配器 | |
| 60 | - * @param smsChannelAllVOList 短信渠道信息集合 | |
| 61 | - */ | |
| 62 | - public synchronized static void flush(SmsClientAdapter smsClientAdapter, List<SmsChannelAllVO> smsChannelAllVOList) { | |
| 63 | - smsSenderMap.clear(); | |
| 64 | - smsClientAdapter.flushClient(init(smsChannelAllVOList)); | |
| 65 | - } | |
| 66 | - | |
| 67 | - /** | |
| 68 | - * 发送短信 | |
| 69 | - * | |
| 70 | - * @param smsClientAdapter 短信客户端适配器 | |
| 71 | - * @param smsBody 短信内容 | |
| 72 | - * @param targetPhones 对象手机集合 | |
| 73 | - * @return 短信发送结果 | |
| 74 | - */ | |
| 75 | - public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, Collection<String> targetPhones) { | |
| 76 | - Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode()); | |
| 77 | - if (channelId == null) { | |
| 78 | - throw new ServiceException(SMS_SENDER_NOT_FOUND); | |
| 79 | - } | |
| 80 | - return smsClientAdapter.send(channelId, smsBody, targetPhones); | |
| 81 | - } | |
| 82 | - | |
| 83 | - /** | |
| 84 | - * 发送短信 | |
| 85 | - * | |
| 86 | - * @param smsClientAdapter 短信客户端适配器 | |
| 87 | - * @param smsBody 短信内容 | |
| 88 | - * @param targetPhone 对象手机 | |
| 89 | - * @return 短信发送结果 | |
| 90 | - */ | |
| 91 | - public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, String targetPhone) { | |
| 92 | - Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode()); | |
| 93 | - if (channelId == null) { | |
| 94 | - throw new ServiceException(SMS_SENDER_NOT_FOUND); | |
| 95 | - } | |
| 96 | - return smsClientAdapter.send(channelId, smsBody, Collections.singletonList(targetPhone)); | |
| 97 | - } | |
| 98 | - | |
| 99 | - /** | |
| 100 | - * 发送短信 | |
| 101 | - * | |
| 102 | - * @param smsClientAdapter 短信客户端适配器 | |
| 103 | - * @param smsBody 短信内容 | |
| 104 | - * @param targetPhones 对象手机数组 | |
| 105 | - * @return 短信发送结果 | |
| 106 | - */ | |
| 107 | - public static SmsResult<?> send(SmsClientAdapter smsClientAdapter, SmsBody smsBody, String... targetPhones) { | |
| 108 | - Long channelId = templateCode2ChannelIdMap.get(smsBody.getCode()); | |
| 109 | - if (channelId == null) { | |
| 110 | - throw new ServiceException(SMS_SENDER_NOT_FOUND); | |
| 111 | - } | |
| 112 | - return smsClientAdapter.send(channelId, smsBody, Arrays.asList(targetPhones)); | |
| 113 | - } | |
| 114 | - | |
| 115 | - | |
| 116 | - private static void addSender(List<SmsChannelAllVO> smsChannelAllVOList) { | |
| 117 | - smsChannelAllVOList.forEach(channelAllVO -> addSender(SmsChannelEnum.getByCode(channelAllVO.getCode()), channelAllVO)); | |
| 118 | - } | |
| 119 | - | |
| 120 | - private static void addSender(SmsChannelEnum channelEnum, SmsChannelAllVO channelAllVO) { | |
| 121 | - if (channelEnum == null) { | |
| 122 | - throw new ServiceException(INVALID_CHANNEL_CODE); | |
| 123 | - } | |
| 124 | - List<SmsTemplateVO> templateList = channelAllVO.getTemplateList(); | |
| 125 | - if (ObjectUtil.isEmpty(templateList)) { | |
| 126 | - throw new ServiceException(SMS_TEMPLATE_NOT_FOUND); | |
| 127 | - } | |
| 128 | - SmsClient<?> aliSmsClient = getSender(channelEnum, channelAllVO); | |
| 129 | - templateList.forEach(smsTemplateVO -> templateCode2ChannelIdMap.put(smsTemplateVO.getCode(), channelAllVO.getId())); | |
| 130 | - smsSenderMap.put(channelAllVO.getId(), aliSmsClient); | |
| 131 | - } | |
| 132 | - | |
| 133 | - private static SmsClient<?> getSender(SmsChannelEnum channelEnum, SmsChannelAllVO channelAllVO) { | |
| 134 | - switch (channelEnum) { | |
| 135 | - case ALI: | |
| 136 | - return new SmsClientLogProxy<>(new AliSmsClient(channelAllVO)); | |
| 137 | - // TODO fill more channel | |
| 138 | - default: | |
| 139 | - break; | |
| 140 | - } | |
| 141 | - throw new ServiceException(SMS_SENDER_NOT_FOUND); | |
| 142 | - } | |
| 143 | -} |
src/main/java/cn/iocoder/dashboard/modules/system/sms/proxy/SmsClientLogProxy.java deleted
| 1 | -package cn.iocoder.dashboard.modules.system.sms.proxy; | |
| 2 | - | |
| 3 | -import cn.iocoder.dashboard.framework.sms.SmsBody; | |
| 4 | -import cn.iocoder.dashboard.framework.sms.SmsClient; | |
| 5 | -import cn.iocoder.dashboard.framework.sms.SmsResult; | |
| 6 | -import cn.iocoder.dashboard.util.json.JsonUtils; | |
| 7 | -import lombok.extern.slf4j.Slf4j; | |
| 8 | - | |
| 9 | -import java.util.Collection; | |
| 10 | - | |
| 11 | -/** | |
| 12 | - * 消息父接口 | |
| 13 | - * | |
| 14 | - * @author zzf | |
| 15 | - * @date 2021/1/22 15:46 | |
| 16 | - */ | |
| 17 | -@Slf4j | |
| 18 | -public class SmsClientLogProxy<R> implements SmsClient<R> { | |
| 19 | - | |
| 20 | - private final SmsClient<R> smsClient; | |
| 21 | - | |
| 22 | - @Override | |
| 23 | - public SmsResult<R> send(SmsBody msgBody, Collection<String> targets) { | |
| 24 | - log.debug("ready send sms, body: {}, target: {}", JsonUtils.toJsonString(msgBody), targets); | |
| 25 | - | |
| 26 | - SmsResult<R> resultBody = smsClient.send(msgBody, targets); | |
| 27 | - | |
| 28 | - if (resultBody.getSuccess()) { | |
| 29 | - // | |
| 30 | - } else { | |
| 31 | - log.warn("send sms fail, body: {}, target: {}, resultBody: {}", | |
| 32 | - JsonUtils.toJsonString(msgBody), | |
| 33 | - targets, | |
| 34 | - JsonUtils.toJsonString(resultBody) | |
| 35 | - ); | |
| 36 | - } | |
| 37 | - return resultBody; | |
| 38 | - } | |
| 39 | - | |
| 40 | - @Override | |
| 41 | - public SmsResult<R> sendAsync(SmsBody msgBody, Collection<String> targets) { | |
| 42 | - return send(msgBody, targets); | |
| 43 | - } | |
| 44 | - | |
| 45 | - public SmsClientLogProxy(SmsClient<R> smsClient) { | |
| 46 | - this.smsClient = smsClient; | |
| 47 | - } | |
| 48 | -} |
src/main/java/cn/iocoder/dashboard/util/string/StrUtils.java
| 1 | 1 | package cn.iocoder.dashboard.util.string; |
| 2 | 2 | |
| 3 | +import cn.hutool.core.util.ObjectUtil; | |
| 3 | 4 | import cn.hutool.core.util.StrUtil; |
| 4 | 5 | |
| 6 | +import java.util.Map; | |
| 7 | + | |
| 5 | 8 | /** |
| 6 | 9 | * 字符串工具类 |
| 7 | 10 | * |
| ... | ... | @@ -13,4 +16,22 @@ public class StrUtils { |
| 13 | 16 | return StrUtil.maxLength(str, maxLength - 3); // -3 的原因,是该方法会补充 ... 恰好 |
| 14 | 17 | } |
| 15 | 18 | |
| 19 | + /** | |
| 20 | + * 指定字符串的 | |
| 21 | + * @param str | |
| 22 | + * @param replaceMap | |
| 23 | + * @return | |
| 24 | + */ | |
| 25 | + public static String replace(String str, Map<String, String> replaceMap) { | |
| 26 | + assert StrUtil.isNotBlank(str); | |
| 27 | + if (ObjectUtil.isEmpty(replaceMap)) { | |
| 28 | + return str; | |
| 29 | + } | |
| 30 | + String result = null; | |
| 31 | + for (String key : replaceMap.keySet()) { | |
| 32 | + result = str.replace(key, replaceMap.get(key)); | |
| 33 | + } | |
| 34 | + return result; | |
| 35 | + } | |
| 36 | + | |
| 16 | 37 | } | ... | ... |