mirror of
https://gitee.com/dromara/sms4j.git
synced 2025-12-06 17:08:40 +08:00
Merge remote-tracking branch 'origin/dev-3.0.x-xiaoyan' into dev-3.0.x
# Conflicts: # sms4j-provider/src/main/java/org/dromara/sms4j/netease/service/NeteaseSmsImpl.java
This commit is contained in:
commit
8235c34a7d
1
pom.xml
1
pom.xml
@ -131,6 +131,7 @@
|
||||
<groupId>com.jdcloud.sdk</groupId>
|
||||
<artifactId>sms</artifactId>
|
||||
<version>${jdcloud.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@ -2,453 +2,13 @@ package org.dromara.email.api;
|
||||
|
||||
import org.dromara.email.comm.entity.MailMessage;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.String;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface MailClient {
|
||||
|
||||
/**
|
||||
* sendMail
|
||||
* <p> 发送纯文本邮件
|
||||
* @param mailAddress 收件人地址
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件正文
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendMail(String mailAddress, String title ,String body);
|
||||
|
||||
/**
|
||||
* sendMail
|
||||
* <p> 群体发送纯文本邮件
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件正文
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendMail(List<String> mailAddress ,String title ,String body);
|
||||
|
||||
/**
|
||||
* sendEmail
|
||||
* <p>发送带有附件的文本邮件
|
||||
* @param mailAddress 收件人地址
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件正文
|
||||
* @param files 附件,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendEmail(String mailAddress, String title, String body,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendEmail
|
||||
* <p>发送带有附件的文本邮件
|
||||
* @param mailAddress 收件人地址 多个收件人地址请按英文','字符隔开
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件正文
|
||||
* @param zipName 压缩包名称 比如 附件.zip
|
||||
* @param files 附件,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendEmail(String mailAddress, String title, String body, String zipName, Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendEmail
|
||||
* <p>群体发送带有附件的文本邮件
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件正文
|
||||
* @param files 附件,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendEmail(List<String> mailAddress, String title, String body, Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendEmail
|
||||
* <p>单人发送带有附件的文本邮件,同时可以携带密送人和抄送人
|
||||
* <p>需要注意的是,密送人和抄送人也不能存在于黑名单内,否则也会被过滤
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件正文
|
||||
* @param files 附件,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendEmail(String mailAddress, String title, String body,List<String> cc,List<String> bcc,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendEmail
|
||||
* <p>群体发送带有附件的文本邮件,同时可以携带密送人和抄送人
|
||||
* <p>需要注意的是,密送人和抄送人也不能存在于黑名单内,否则也会被过滤
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件正文
|
||||
* @param files 附件,可添加多个
|
||||
* @param bcc 密送人
|
||||
* @param cc 抄送人
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendEmail(List<String> mailAddress, String title, String body,List<String> cc,List<String> bcc,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendEmail
|
||||
* <p> 发送邮件,可以通过对象构造群体发送或者单体发送,取决于添加进去的收件人,同时可以添加
|
||||
* 密送人,抄送人,附件等参数
|
||||
* @param mailMessage 发送邮件参数对象
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendEmail(MailMessage mailMessage);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,无正文
|
||||
* <p> 将默认读取resources/template下的html文件,第三个参数为html的名称,需携带尾缀
|
||||
* @param mailAddress 收件人地址
|
||||
* @param title 邮件标题
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter key为模板的变量名称 无需携带大括号 value为模板变量所对应的值
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(String mailAddress, String title , String htmlName, Map<String,String> parameter);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,无正文
|
||||
* <p> 将默认读取resources/template下的html文件,第三个参数为html的名称,需携带尾缀
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter key为模板的变量名称 无需携带大括号 value为模板变量所对应的值
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title , String htmlName, Map<String,String> parameter);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,无正文
|
||||
* <p> 将默认读取resources/template下的html文件,第三个参数为html的名称,需携带尾缀
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址
|
||||
* @param title 邮件标题
|
||||
* @param htmlName 邮件模板名称
|
||||
* @param parameter 实体
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(String mailAddress, String title , String htmlName, Parameter parameter);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,无正文
|
||||
* <p> 将默认读取resources/template下的html文件,第三个参数为html的名称,需携带尾缀
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param htmlName 邮件模板名称
|
||||
* @param parameter 实体
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title , String htmlName, Parameter parameter);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,无正文,带附件
|
||||
* <p> 将默认读取resources/template下的html文件,第三个参数为html的名称,需携带尾缀
|
||||
* @param mailAddress 收件人地址
|
||||
* @param title 邮件标题
|
||||
* @param htmlName 邮件模板名称
|
||||
* @param parameter 实体
|
||||
* @param files 附件,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(String mailAddress, String title , String htmlName,Map<String,String> parameter,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,无正文,带附件
|
||||
* <p> 将默认读取resources/template下的html文件,第三个参数为html的名称,需携带尾缀
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param htmlName 邮件模板名称
|
||||
* @param parameter 实体
|
||||
* @param files 附件,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title , String htmlName,Map<String,String> parameter,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,无正文,带附件
|
||||
* <p> 将默认读取resources/template下的html文件,第三个参数为html的名称,需携带尾缀
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址
|
||||
* @param title 邮件标题
|
||||
* @param htmlName 邮件模板名称
|
||||
* @param parameter 实体
|
||||
* @param files 附件,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(String mailAddress, String title , String htmlName,Parameter parameter,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,无正文,带附件
|
||||
* <p> 将默认读取resources/template下的html文件,第三个参数为html的名称,需携带尾缀
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param htmlName 邮件模板名称
|
||||
* @param parameter 实体
|
||||
* @param files 附件,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title , String htmlName,Parameter parameter,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文
|
||||
* <p> 将默认读取resources/template下的html文件,第四个参数为html的名称,需携带尾缀
|
||||
* @param mailAddress 收件人地址
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter key为模板的变量名称 无需携带大括号 value为模板变量所对应的值
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(String mailAddress, String title ,String body, String htmlName, Map<String,String> parameter);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文
|
||||
* <p> 将默认读取resources/template下的html文件,第四个参数为html的名称,需携带尾缀
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter key为模板的变量名称 无需携带大括号 value为模板变量所对应的值
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title ,String body, String htmlName, Map<String,String> parameter);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文
|
||||
* <p> 将默认读取resources/template下的html文件,第四个参数为html的名称,需携带尾缀
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter 实体
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(String mailAddress, String title ,String body, String htmlName, Parameter parameter);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文
|
||||
* <p> 将默认读取resources/template下的html文件,第四个参数为html的名称,需携带尾缀
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter 实体
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title ,String body, String htmlName, Parameter parameter);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 将默认读取resources/template下的html文件,第四个参数为html的名称,需携带尾缀
|
||||
* @param mailAddress 收件人地址
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter key为模板的变量名称 无需携带大括号 value为模板变量所对应的值
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(String mailAddress, String title ,String body, String htmlName, Map<String,String> parameter,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 将默认读取resources/template下的html文件,第四个参数为html的名称,需携带尾缀
|
||||
* @param mailAddress 收件人地址 多个收件人地址请按英文','字符隔开
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文 可为空
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter key为模板的变量名称 无需携带大括号 value为模板变量所对应的值
|
||||
* @param zipName 压缩包名称 比如 附件.zip
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @author :bleachtred
|
||||
*/
|
||||
void sendHtml(String mailAddress, String title, String body, String htmlName, Map<String,String> parameter, String zipName, Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 将默认读取resources/template下的html文件,第四个参数为html的名称,需携带尾缀
|
||||
* @param mailAddress 收件人地址 多个收件人地址请按英文','字符隔开
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文 可为空
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter 字段名称为变量名称,字段值为变量值
|
||||
* @param zipName 压缩包名称 比如 附件.zip
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(String mailAddress, String title, String body, String htmlName, Parameter parameter, String zipName, Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 将默认读取resources/template下的html文件,第四个参数为html的名称,需携带尾缀
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter key为模板的变量名称 无需携带大括号 value为模板变量所对应的值
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title ,String body, String htmlName, Map<String,String> parameter,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 将默认读取resources/template下的html文件,第四个参数为html的名称,需携带尾缀
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter 字段名称为变量名称,字段值为变量值
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(String mailAddress, String title ,String body, String htmlName, Parameter parameter,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 将默认读取resources/template下的html文件,第四个参数为html的名称,需携带尾缀
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param htmlName 邮件正文
|
||||
* @param parameter 字段名称为变量名称,字段值为变量值
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title ,String body, String htmlName, Parameter parameter,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 从用户给定的输入流获取html模板文件
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param html html模板的输入流,这个流可以来自任何来源,例如网络请求,或是本地文件,或者对象存储等
|
||||
* @param parameter key为模板的变量名称 无需携带大括号 value为模板变量所对应的值
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title , String body, InputStream html, Map<String, String> parameter, Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 从用户给定的输入流获取html模板文件
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param html html模板的输入流,这个流可以来自任何来源,例如网络请求,或是本地文件,或者对象存储等
|
||||
* @param parameter 字段名称为变量名称,字段值为变量值
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title ,String body, InputStream html, Parameter parameter,Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 从用户给定的输入流获取html模板文件
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param html html模板的输入流,这个流可以来自任何来源,例如网络请求,或是本地文件,或者对象存储等
|
||||
* @param parameter key为变量名称,value为变量值
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @param cc 抄送人,可添加多个
|
||||
* @param bcc 密送人,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title,String body,InputStream html,List<String> cc,List<String> bcc,Map<String, String> parameter, Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 从用户给定的输入流获取html模板文件
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param html html模板的输入流,这个流可以来自任何来源,例如网络请求,或是本地文件,或者对象存储等
|
||||
* @param parameter 字段名称为变量名称,字段值为变量值
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @param cc 抄送人,可添加多个
|
||||
* @param bcc 密送人,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title,String body,InputStream html,List<String> cc,List<String> bcc,Parameter parameter, Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 从用户给定的输入流获取html模板文件
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param html html模板的名称
|
||||
* @param parameter 字段名称为变量名称,字段值为变量值
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @param cc 抄送人,可添加多个
|
||||
* @param bcc 密送人,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title,String body,String html,List<String> cc,List<String> bcc,Map<String, String> parameter, Map<String,String> files);
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 读取模板发送html邮件,并携带正文和附件
|
||||
* <p> 从用户给定的输入流获取html模板文件
|
||||
* <p> 用户可以自己编写一个实体类,并实现Parameter接口,编写get和set方法,这样一来字段的名称则为模板变量名称,对象的值则为模板变量的值
|
||||
* @param mailAddress 收件人地址,添加多个
|
||||
* @param title 邮件标题
|
||||
* @param body 邮件文本正文
|
||||
* @param html html模板的名称
|
||||
* @param parameter 字段名称为变量名称,字段值为变量值
|
||||
* @param files 附件,可添加多个 key 为文件名,value为文件的路径
|
||||
* @param cc 抄送人,可添加多个
|
||||
* @param bcc 密送人,可添加多个
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(List<String> mailAddress, String title,String body,String html,List<String> cc,List<String> bcc,Parameter parameter, Map<String,String> files);
|
||||
|
||||
|
||||
/**
|
||||
* sendHtml
|
||||
* <p> 发送邮件,可以通过对象构造群体发送或者单体发送,取决于添加进去的收件人,同时可以添加
|
||||
* 发送邮件,可以通过对象构造群体发送或者单体发送,取决于添加进去的收件人,同时可以添加
|
||||
* 密送人,抄送人,附件等参数
|
||||
* @param mailMessage 发送邮件参数对象
|
||||
* @author :Wind
|
||||
*/
|
||||
void sendHtml(MailMessage mailMessage);
|
||||
void send(MailMessage mailMessage);
|
||||
}
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
package org.dromara.email.comm.entity;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.io.InputStream;
|
||||
@ -9,136 +12,62 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class MailMessage {
|
||||
|
||||
/** 收件人地址*/
|
||||
private List<String> mailAddress;
|
||||
/**
|
||||
* 收件人地址
|
||||
*/
|
||||
private String mailAddress;
|
||||
|
||||
/** 邮件主题*/
|
||||
/**
|
||||
* 收件人地址多人(优先使用)
|
||||
*/
|
||||
private List<String> mailAddressList;
|
||||
|
||||
/**
|
||||
* 邮件主题
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/** 文字正文*/
|
||||
/**
|
||||
* 文字正文
|
||||
*/
|
||||
private String body;
|
||||
|
||||
/** html模板文件路径(resources目录下的路径)*/
|
||||
/**
|
||||
* html模板文件路径(resources目录下的路径)
|
||||
*/
|
||||
private String htmlPath;
|
||||
|
||||
/** html模板文件的输入流,可来自任意可读取位置*/
|
||||
/**
|
||||
* html模板文件的输入流,可来自任意可读取位置
|
||||
*/
|
||||
private InputStream htmlInputStream;
|
||||
|
||||
/** html 模板参数*/
|
||||
private Map<String,String> htmlValues;
|
||||
/**
|
||||
* html 模板参数
|
||||
*/
|
||||
private Map<String, String> htmlValues;
|
||||
|
||||
/** 抄送人*/
|
||||
/**
|
||||
* zip文件名称
|
||||
*/
|
||||
private String zipName;
|
||||
|
||||
/**
|
||||
* 抄送人
|
||||
*/
|
||||
private List<String> cc;
|
||||
|
||||
/** 密送人*/
|
||||
/**
|
||||
* 密送人
|
||||
*/
|
||||
private List<String> bcc;
|
||||
|
||||
/** 附件*/
|
||||
private Map<String,String> files;
|
||||
/**
|
||||
* 附件
|
||||
*/
|
||||
private Map<String, String> files;
|
||||
|
||||
public static MailsBuilder Builder(){
|
||||
return new MailsBuilder();
|
||||
}
|
||||
|
||||
static class MailsBuilder{
|
||||
private final MailMessage mailMessage = new MailMessage();
|
||||
public MailsBuilder() {
|
||||
}
|
||||
public MailMessage build(){
|
||||
return mailMessage;
|
||||
}
|
||||
|
||||
public MailsBuilder setMailAddress(List<String> mailAddress) {
|
||||
mailMessage.mailAddress = mailAddress;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setMailAddress(String mailAddress){
|
||||
if ( mailMessage.mailAddress == null){
|
||||
mailMessage.mailAddress = new ArrayList<>();
|
||||
}
|
||||
mailMessage.mailAddress.add(mailAddress);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setTitle(String title){
|
||||
mailMessage.title = title;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setBody(String body){
|
||||
mailMessage.body = body;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setCc(List<String> cc){
|
||||
mailMessage.cc = cc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setCc(String cc){
|
||||
if (mailMessage.cc == null){
|
||||
mailMessage.cc = new ArrayList<>();
|
||||
}
|
||||
mailMessage.cc.add(cc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setBcc(List<String> bcc){
|
||||
mailMessage.bcc = bcc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setBcc(String bcc){
|
||||
if (mailMessage.bcc == null){
|
||||
mailMessage.bcc = new ArrayList<>();
|
||||
}
|
||||
mailMessage.bcc.add(bcc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setFiles(Map<String, String> files){
|
||||
if (mailMessage.files == null){
|
||||
mailMessage.files = new HashMap<>();
|
||||
}
|
||||
mailMessage.files.putAll(files);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setFiles(String fileName,String filePath){
|
||||
if (mailMessage.files == null){
|
||||
mailMessage.files = new HashMap<>();
|
||||
}
|
||||
mailMessage.files.put(fileName,filePath);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setHtmlPath(String htmlPath){
|
||||
mailMessage.htmlPath = htmlPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setHtmlInputStream(InputStream htmlInputStream){
|
||||
mailMessage.htmlInputStream = htmlInputStream;
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setHtmlValues(String key, String value){
|
||||
if (mailMessage.files == null){
|
||||
mailMessage.files = new HashMap<>();
|
||||
}
|
||||
mailMessage.htmlValues.put(key,value);
|
||||
return this;
|
||||
}
|
||||
|
||||
public MailsBuilder setHtmlValues(Map<String,String> htmlValues){
|
||||
if (mailMessage.files == null){
|
||||
mailMessage.files = new HashMap<>();
|
||||
}
|
||||
mailMessage.htmlValues.putAll(htmlValues);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,7 +90,8 @@ public final class HtmlUtil {
|
||||
for (Map.Entry<String, String> s : parameter.entrySet()) {
|
||||
String piece = piece(s.getKey());
|
||||
if (data.get(i).contains(piece)){
|
||||
data.set(i,s.getValue());
|
||||
String replace = data.get(i).replace(piece, s.getValue());
|
||||
data.set(i,replace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* <p> 邮件插件通用模块
|
||||
* @author :Wind
|
||||
* 2023/7/27 10:59
|
||||
**/
|
||||
package org.dromara.email.comm;
|
||||
@ -33,6 +33,7 @@ public class MailFactory{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* createMailClient
|
||||
* <p>从工厂获取一个邮件发送实例,该实例发送短信将依照黑名单中的数据进行过滤
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
/**
|
||||
* <p> 邮件插件核心模块
|
||||
* @author :Wind
|
||||
* 2023/7/27 10:58
|
||||
**/
|
||||
package org.dromara.email.core;
|
||||
@ -72,10 +72,10 @@ public class MailBuild {
|
||||
}
|
||||
|
||||
public static MailClient build(MailSmtpConfig config) throws MessagingException {
|
||||
return MailService.NewMailService(new MailBuild(config));
|
||||
return MailService.instance(new MailBuild(config));
|
||||
}
|
||||
public static MailClient build(MailSmtpConfig config,Blacklist blacklist)throws MessagingException {
|
||||
return MailService.NewMailService(new MailBuild(config,blacklist));
|
||||
return MailService.instance(new MailBuild(config,blacklist));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
package org.dromara.email.core.service;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.lang.UUID;
|
||||
@ -7,13 +8,11 @@ import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import org.dromara.email.api.MailClient;
|
||||
import org.dromara.email.api.Parameter;
|
||||
import org.dromara.email.comm.constants.FileConstants;
|
||||
import org.dromara.email.comm.entity.MailMessage;
|
||||
import org.dromara.email.comm.errors.MailException;
|
||||
import org.dromara.email.comm.utils.HtmlUtil;
|
||||
import org.dromara.email.comm.utils.ZipUtils;
|
||||
import org.dromara.email.core.ReflectUtil;
|
||||
|
||||
import javax.activation.DataHandler;
|
||||
import javax.activation.DataSource;
|
||||
@ -28,8 +27,6 @@ import javax.mail.util.ByteArrayDataSource;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -44,211 +41,37 @@ public class MailService implements MailClient {
|
||||
this.mailBuild = mailBuild;
|
||||
}
|
||||
|
||||
public static MailClient NewMailService(MailBuild mailBuild) {
|
||||
public static MailClient instance(MailBuild mailBuild) {
|
||||
return new MailService(mailBuild);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMail(String mailAddress, String title, String body) {
|
||||
sendEmail(mailAddress, title, body, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMail(List<String> mailAddress, String title, String body) {
|
||||
sendEmail(mailAddress, title, body, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEmail(String mailAddress, String title, String body, Map<String, String> files) {
|
||||
sendEmail(Collections.singletonList(mailAddress), title, body, files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEmail(List<String> mailAddress, String title, String body, Map<String, String> files) {
|
||||
sendEmail(mailAddress, title, body, null, null, files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEmail(String mailAddress, String title, String body, List<String> cc, List<String> bcc, Map<String, String> files) {
|
||||
sendEmail(Collections.singletonList(mailAddress), title, body, cc, bcc, files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEmail(MailMessage mailMessage) {
|
||||
sendEmail(mailMessage.getMailAddress(),
|
||||
public void send(MailMessage mailMessage) {
|
||||
List<String> html;
|
||||
if (mailMessage.getHtmlInputStream() == null) {
|
||||
html = HtmlUtil.readHtml(mailMessage.getHtmlPath());
|
||||
} else {
|
||||
html = HtmlUtil.readHtml(mailMessage.getHtmlInputStream());
|
||||
}
|
||||
List<String> address;
|
||||
if (CollUtil.isEmpty(mailMessage.getMailAddressList())) {
|
||||
if (StrUtil.isBlank(mailMessage.getMailAddress())) {
|
||||
throw new MailException("收件人地址不能为空!");
|
||||
}
|
||||
address = CollUtil.newArrayList(mailMessage.getMailAddress());
|
||||
} else {
|
||||
address = mailMessage.getMailAddressList();
|
||||
}
|
||||
send(address,
|
||||
mailMessage.getTitle(),
|
||||
mailMessage.getBody(),
|
||||
html,
|
||||
mailMessage.getHtmlValues(),
|
||||
mailMessage.getZipName(),
|
||||
mailMessage.getFiles(),
|
||||
mailMessage.getCc(),
|
||||
mailMessage.getBcc(),
|
||||
mailMessage.getFiles());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(String mailAddress, String title, String htmlName, Map<String, String> parameter) {
|
||||
sendHtml(Collections.singletonList(mailAddress), title, htmlName, parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress, String title, String htmlName, Map<String, String> parameter) {
|
||||
sendHtml(mailAddress, title, htmlName, parameter, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(String mailAddress, String title, String htmlName, Parameter parameter) {
|
||||
sendHtml(Collections.singletonList(mailAddress), title, htmlName, parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress, String title, String htmlName, Parameter parameter) {
|
||||
sendHtml(mailAddress, title, htmlName, ReflectUtil.getValues(parameter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(String mailAddress, String title, String htmlName, Map<String, String> parameter, Map<String, String> files) {
|
||||
sendHtml(mailAddress, title, "", htmlName, parameter, files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress, String title, String htmlName, Map<String, String> parameter, Map<String, String> files) {
|
||||
sendHtml(mailAddress, title, "", htmlName, parameter, files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(String mailAddress, String title, String htmlName, Parameter parameter, Map<String, String> files) {
|
||||
sendHtml(mailAddress, title, htmlName, ReflectUtil.getValues(parameter), files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress, String title, String htmlName, Parameter parameter, Map<String, String> files) {
|
||||
sendHtml(mailAddress, title, htmlName, ReflectUtil.getValues(parameter), files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(String mailAddress, String title, String body, String htmlName, Map<String, String> parameter) {
|
||||
sendHtml(mailAddress, title, body, htmlName, parameter, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress, String title, String body, String htmlName, Map<String, String> parameter) {
|
||||
sendHtml(mailAddress, title, body, htmlName, parameter, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(String mailAddress, String title, String body, String htmlName, Parameter parameter) {
|
||||
sendHtml(Collections.singletonList(mailAddress), title, body, htmlName, parameter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress, String title, String body, String htmlName, Parameter parameter) {
|
||||
sendHtml(mailAddress, title, body, htmlName, parameter, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(String mailAddress, String title, String body, String htmlName, Map<String, String> parameter, Map<String, String> files) {
|
||||
sendHtml(Collections.singletonList(mailAddress), title, body, htmlName, parameter, files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(String mailAddress, String title, String body, String htmlName, Parameter parameter, String zipName, Map<String, String> files) {
|
||||
sendHtml(mailAddress, title, body, htmlName, ReflectUtil.getValues(parameter), zipName, files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress, String title, String body, String htmlName, Map<String, String> parameter, Map<String, String> files) {
|
||||
send(mailAddress, title, body, HtmlUtil.readHtml(htmlName), parameter, files, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(String mailAddress, String title, String body, String htmlName, Parameter parameter, Map<String, String> files) {
|
||||
sendHtml(Collections.singletonList(mailAddress), title, body, htmlName, parameter, files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress, String title, String body, String htmlName, Parameter parameter, Map<String, String> files) {
|
||||
sendHtml(mailAddress, title, body, htmlName, ReflectUtil.getValues(parameter), files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress, String title, String body, InputStream html, Map<String, String> parameter, Map<String, String> files) {
|
||||
send(mailAddress, title, body, HtmlUtil.readHtml(html), parameter, files, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress, String title, String body, InputStream html, Parameter parameter, Map<String, String> files) {
|
||||
sendHtml(mailAddress, title, body, html, ReflectUtil.getValues(parameter), files);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
InputStream html,
|
||||
List<String> cc,
|
||||
List<String> bcc,
|
||||
Map<String, String> parameter,
|
||||
Map<String, String> files) {
|
||||
send(mailAddress, title, body, HtmlUtil.readHtml(html), parameter, files, cc, bcc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
InputStream html,
|
||||
List<String> cc,
|
||||
List<String> bcc,
|
||||
Parameter parameter,
|
||||
Map<String, String> files) {
|
||||
send(mailAddress, title, body, HtmlUtil.readHtml(html), ReflectUtil.getValues(parameter), files, cc, bcc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
String html,
|
||||
List<String> cc,
|
||||
List<String> bcc,
|
||||
Map<String, String> parameter,
|
||||
Map<String, String> files) {
|
||||
send(mailAddress, title, body, HtmlUtil.readHtml(html), parameter, files, cc, bcc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(List<String> mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
String html,
|
||||
List<String> cc,
|
||||
List<String> bcc,
|
||||
Parameter parameter,
|
||||
Map<String, String> files) {
|
||||
send(mailAddress, title, body, HtmlUtil.readHtml(html), ReflectUtil.getValues(parameter), files, cc, bcc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(MailMessage mailMessage) {
|
||||
if (mailMessage.getHtmlInputStream() == null) {
|
||||
sendHtml(mailMessage.getMailAddress(),
|
||||
mailMessage.getTitle(),
|
||||
mailMessage.getBody(),
|
||||
mailMessage.getHtmlPath(),
|
||||
mailMessage.getCc(),
|
||||
mailMessage.getBcc(),
|
||||
mailMessage.getHtmlValues(),
|
||||
mailMessage.getFiles()
|
||||
);
|
||||
} else {
|
||||
sendHtml(mailMessage.getMailAddress(),
|
||||
mailMessage.getTitle(),
|
||||
mailMessage.getBody(),
|
||||
mailMessage.getHtmlInputStream(),
|
||||
mailMessage.getCc(),
|
||||
mailMessage.getBcc(),
|
||||
mailMessage.getHtmlValues(),
|
||||
mailMessage.getFiles()
|
||||
);
|
||||
}
|
||||
mailMessage.getBcc()
|
||||
);
|
||||
}
|
||||
|
||||
private void forFiles(Multipart multipart, Map<String, String> files) throws MessagingException {
|
||||
@ -261,7 +84,7 @@ public class MailService implements MailClient {
|
||||
if (v.startsWith("http")) {
|
||||
byte[] bytes = HttpUtil.downloadBytes(v);
|
||||
source = new ByteArrayDataSource(bytes, FileConstants.IO_FILE_TYPE);
|
||||
}else {
|
||||
} else {
|
||||
source = new FileDataSource(v);
|
||||
}
|
||||
messageBodyPart.setDataHandler(new DataHandler(source));
|
||||
@ -287,72 +110,18 @@ public class MailService implements MailClient {
|
||||
String body,
|
||||
List<String> html,
|
||||
Map<String, String> parameter,
|
||||
String zipName,
|
||||
Map<String, String> files,
|
||||
List<String> cc,
|
||||
List<String> bcc) {
|
||||
try {
|
||||
Message message = messageBuild(mailAddress, title, body, html, parameter, files, cc, bcc);
|
||||
Message message = messageBuild(mailAddress, title, body, html, parameter, zipName, cc, bcc, files);
|
||||
Transport.send(message);
|
||||
logger.info("邮件发送成功!^_^");
|
||||
} catch (MessagingException e) {
|
||||
} catch (MessagingException | IOException e) {
|
||||
// 防止 maxRetries 数值小于0带来的其他问题
|
||||
if (mailBuild.getMaxRetries() > 0){
|
||||
ReSendList(mailAddress,
|
||||
title,
|
||||
body,
|
||||
html,
|
||||
parameter,
|
||||
files,
|
||||
cc,
|
||||
bcc);
|
||||
} else {
|
||||
logger.warning(e.getMessage());
|
||||
throw new MailException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEmail(List<String> mailAddress, String title, String body, List<String> cc, List<String> bcc, Map<String, String> files) {
|
||||
try {
|
||||
Message message = messageBuild(mailAddress, title, body, cc, bcc, files);
|
||||
Transport.send(message);
|
||||
logger.info("邮件发送成功!^_^");
|
||||
} catch (MessagingException e) {
|
||||
if (mailBuild.getMaxRetries() > 0) {
|
||||
ReSendList(mailAddress, title, body, cc, bcc, files);
|
||||
} else {
|
||||
logger.warning(e.getMessage());
|
||||
throw new MailException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEmail(String mailAddress, String title, String body, String zipName, Map<String, String> files) {
|
||||
try {
|
||||
Message message = messageBuild(mailAddress, title, body, zipName, files);
|
||||
Transport.send(message);
|
||||
logger.info("邮件发送成功!^_^");
|
||||
} catch (MessagingException | IOException e) {
|
||||
if (mailBuild.getMaxRetries() > 1) {
|
||||
ReSend(mailAddress, title, body, zipName, files);
|
||||
} else {
|
||||
logger.warning(e.getMessage());
|
||||
throw new MailException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendHtml(String mailAddress, String title, String body, String htmlName, Map<String, String> parameter, String zipName, Map<String, String> files) {
|
||||
try {
|
||||
Message message = messageBuild(mailAddress, title, body, htmlName, parameter, zipName, files);
|
||||
Transport.send(message);
|
||||
logger.info("邮件发送成功!^_^");
|
||||
} catch (MessagingException | IOException e) {
|
||||
if (mailBuild.getMaxRetries() > 1) {
|
||||
ReSend(mailAddress, title, body, htmlName, parameter, zipName, files);
|
||||
ReSendList(mailAddress, title, body, html, parameter, zipName, files, cc, bcc);
|
||||
} else {
|
||||
logger.warning(e.getMessage());
|
||||
throw new MailException(e);
|
||||
@ -361,13 +130,14 @@ public class MailService implements MailClient {
|
||||
}
|
||||
|
||||
private void ReSendList(List<String> mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
List<String> html,
|
||||
Map<String, String> parameter,
|
||||
Map<String, String> files,
|
||||
List<String> cc,
|
||||
List<String> bcc) {
|
||||
String title,
|
||||
String body,
|
||||
List<String> html,
|
||||
Map<String, String> parameter,
|
||||
String zipName,
|
||||
Map<String, String> files,
|
||||
List<String> cc,
|
||||
List<String> bcc) {
|
||||
int maxRetries = mailBuild.getMaxRetries();
|
||||
int retryCount = 1; // 初始值为1;则while循环中少发送一次,最后一次发送在判断 retryCount >= maxRetries 这里。
|
||||
boolean retryOnFailure = true;
|
||||
@ -376,14 +146,14 @@ public class MailService implements MailClient {
|
||||
try {
|
||||
logger.warning("邮件第 {" + retryCount + "} 次重新发送");
|
||||
Message message;
|
||||
if (html != null || parameter != null){
|
||||
message = messageBuild(mailAddress, title, body, html, parameter, files, cc, bcc);
|
||||
if (html != null || parameter != null) {
|
||||
message = messageBuild(mailAddress, title, body, html, parameter, zipName, cc, bcc, files);
|
||||
} else {
|
||||
message = messageBuild(mailAddress, title, body, cc, bcc, files);
|
||||
message = messageBuild(mailAddress, title, body, null, null, zipName, cc, bcc, files);
|
||||
}
|
||||
Transport.send(message);
|
||||
retryOnFailure = false; // 发送成功,停止重试
|
||||
} catch (MessagingException e) {
|
||||
} catch (MessagingException | IOException e) {
|
||||
retryCount++;
|
||||
try {
|
||||
// 间隔秒数
|
||||
@ -397,180 +167,42 @@ public class MailService implements MailClient {
|
||||
if (retryCount >= maxRetries && retryOnFailure) {
|
||||
try {
|
||||
Message message;
|
||||
if (html != null || parameter != null){
|
||||
message = messageBuild(mailAddress, title, body, html, parameter, files, cc, bcc);
|
||||
if (html != null || parameter != null) {
|
||||
message = messageBuild(mailAddress, title, body, html, parameter, null, cc, bcc, files);
|
||||
} else {
|
||||
message = messageBuild(mailAddress, title, body, cc, bcc, files);
|
||||
message = messageBuild(mailAddress, title, body, null, null, null, cc, bcc, files);
|
||||
}
|
||||
Transport.send(message);
|
||||
} catch (MessagingException e) {
|
||||
} catch (MessagingException | IOException e) {
|
||||
throw new MailException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ReSendList(List<String> mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
List<String> cc,
|
||||
List<String> bcc,
|
||||
Map<String, String> files) {
|
||||
ReSendList(mailAddress, title, body, null, null, files, cc, bcc);
|
||||
}
|
||||
|
||||
private void ReSend(String mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
String htmlName,
|
||||
Map<String, String> parameter,
|
||||
String zipName,
|
||||
Map<String, String> files) {
|
||||
int maxRetries = mailBuild.getMaxRetries();
|
||||
int retryCount = 1; // 初始值为1;则while循环中少发送一次,最后一次发送在判断 retryCount >= maxRetries 这里。
|
||||
boolean retryOnFailure = true;
|
||||
|
||||
while (retryOnFailure && retryCount < maxRetries) {
|
||||
try {
|
||||
logger.warning("邮件第 {" + retryCount + "} 次重新发送");
|
||||
if (htmlName != null || parameter != null){
|
||||
Message message = messageBuild(mailAddress, title, body, htmlName, parameter, zipName, files);
|
||||
Transport.send(message);
|
||||
} else {
|
||||
Message message = messageBuild(mailAddress, title, body, zipName, files);
|
||||
Transport.send(message);
|
||||
}
|
||||
retryOnFailure = false; // 发送成功,停止重试
|
||||
} catch (MessagingException e) {
|
||||
retryCount++;
|
||||
try {
|
||||
// 间隔秒数
|
||||
TimeUnit.SECONDS.sleep(mailBuild.getRetryInterval());
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (retryCount >= maxRetries && retryOnFailure) {
|
||||
try {
|
||||
if (htmlName != null || parameter != null){
|
||||
Message message = messageBuild(mailAddress, title, body, htmlName, parameter, zipName, files);
|
||||
Transport.send(message);
|
||||
} else {
|
||||
Message message = messageBuild(mailAddress, title, body, zipName, files);
|
||||
Transport.send(message);
|
||||
}
|
||||
} catch (MessagingException e) {
|
||||
throw new MailException(e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ReSend(String mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
String zipName,
|
||||
Map<String, String> files) {
|
||||
ReSend(mailAddress, title, body, null, null, zipName, files);
|
||||
}
|
||||
|
||||
// messageBuild 方法重载
|
||||
private Message messageBuild(List<String> mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
List<String> html,
|
||||
Map<String, String> parameter,
|
||||
Map<String, String> files,
|
||||
List<String> cc,
|
||||
List<String> bcc) throws MessagingException {
|
||||
Message message = mailBuild.getMessage();
|
||||
message.setRecipients(Message.RecipientType.TO, mailBuild.eliminate(mailAddress));
|
||||
message.setSubject(title);
|
||||
|
||||
Multipart multipart = new MimeMultipart("alternative");
|
||||
// 读取模板并进行变量替换
|
||||
List<String> strings = HtmlUtil.replacePlaceholder(html, parameter);
|
||||
// 拼合HTML数据
|
||||
String htmlData = HtmlUtil.pieceHtml(strings);
|
||||
if (!body.isEmpty()) {
|
||||
// 创建文本正文部分
|
||||
MimeBodyPart textPart = new MimeBodyPart();
|
||||
textPart.setText(body);
|
||||
multipart.addBodyPart(textPart);
|
||||
}
|
||||
// 添加附件
|
||||
if (files != null && files.size() != 0) {
|
||||
forFiles(multipart, files);
|
||||
}
|
||||
|
||||
MimeBodyPart htmlPart = new MimeBodyPart();
|
||||
htmlPart.setContent(htmlData, "text/html;charset=UTF-8");
|
||||
addCC(cc, bcc, message);
|
||||
multipart.addBodyPart(htmlPart);
|
||||
message.setContent(multipart);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message messageBuild(List<String> mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
List<String> html,
|
||||
Map<String, String> parameter,
|
||||
String zipName,
|
||||
List<String> cc,
|
||||
List<String> bcc,
|
||||
Map<String, String> files) throws MessagingException {
|
||||
Map<String, String> files) throws MessagingException, IOException {
|
||||
Message message = mailBuild.getMessage();
|
||||
message.setRecipients(Message.RecipientType.TO, mailBuild.eliminate(mailAddress));
|
||||
message.setSubject(title);
|
||||
if (StrUtil.isNotBlank(body)) {
|
||||
message.setText(body);
|
||||
}
|
||||
if (files != null && files.size() != 0) {
|
||||
Multipart multipart = new MimeMultipart();
|
||||
forFiles(multipart, files);
|
||||
message.setContent(multipart);
|
||||
}
|
||||
addCC(cc, bcc, message);
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message messageBuild(String mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
String zipName,
|
||||
Map<String, String> files) throws MessagingException, IOException {
|
||||
Message message = mailBuild.getMessage();
|
||||
message.setRecipients(Message.RecipientType.TO, mailBuild.eliminate(Convert.toList(String.class, mailAddress)));
|
||||
message.setSubject(title);
|
||||
if (StrUtil.isNotBlank(body)) {
|
||||
message.setText(body);
|
||||
}
|
||||
if (files != null && files.size() != 0) {
|
||||
Multipart multipart = new MimeMultipart();
|
||||
zipFiles(multipart, zipName, files);
|
||||
message.setContent(multipart);
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
public Message messageBuild(String mailAddress,
|
||||
String title,
|
||||
String body,
|
||||
String htmlName,
|
||||
Map<String, String> parameter,
|
||||
String zipName,
|
||||
Map<String, String> files) throws MessagingException, IOException {
|
||||
Message message = mailBuild.getMessage();
|
||||
message.setRecipients(Message.RecipientType.TO, mailBuild.eliminate(Convert.toList(String.class, mailAddress)));
|
||||
message.setSubject(title);
|
||||
|
||||
Multipart multipart = new MimeMultipart("alternative");
|
||||
//读取模板并进行变量替换
|
||||
List<String> strings = HtmlUtil.replacePlaceholder(HtmlUtil.readHtml(htmlName), parameter);
|
||||
//拼合HTML数据
|
||||
String htmlData = HtmlUtil.pieceHtml(strings);
|
||||
if (CollUtil.isNotEmpty(html) && MapUtil.isNotEmpty(parameter)) {
|
||||
//读取模板并进行变量替换
|
||||
List<String> strings = HtmlUtil.replacePlaceholder(html, parameter);
|
||||
//拼合HTML数据
|
||||
String htmlData = HtmlUtil.pieceHtml(strings);
|
||||
MimeBodyPart htmlPart = new MimeBodyPart();
|
||||
htmlPart.setContent(htmlData, "text/html;charset=UTF-8");
|
||||
multipart.addBodyPart(htmlPart);
|
||||
}
|
||||
|
||||
if (StrUtil.isNotBlank(body)) {
|
||||
// 创建文本正文部分
|
||||
MimeBodyPart textPart = new MimeBodyPart();
|
||||
@ -578,21 +210,26 @@ public class MailService implements MailClient {
|
||||
multipart.addBodyPart(textPart);
|
||||
}
|
||||
//添加附件
|
||||
if (MapUtil.isNotEmpty(files)) {
|
||||
if (MapUtil.isNotEmpty(files) && StrUtil.isNotBlank(zipName)) {
|
||||
zipFiles(multipart, zipName, files);
|
||||
} else {
|
||||
if (MapUtil.isNotEmpty(files)) {
|
||||
forFiles(multipart, files);
|
||||
message.setContent(multipart);
|
||||
}
|
||||
}
|
||||
if (CollUtil.isNotEmpty(cc) || CollUtil.isNotEmpty(bcc)) {
|
||||
addCC(cc, bcc, message);
|
||||
}
|
||||
MimeBodyPart htmlPart = new MimeBodyPart();
|
||||
htmlPart.setContent(htmlData, "text/html;charset=UTF-8");
|
||||
multipart.addBodyPart(htmlPart);
|
||||
message.setContent(multipart);
|
||||
return message;
|
||||
}
|
||||
|
||||
private void addCC(List<String> cc, List<String> bcc, Message message) throws MessagingException {
|
||||
if (cc != null && cc.size() > 0) {
|
||||
if (CollUtil.isNotEmpty(cc)) {
|
||||
message.addRecipients(Message.RecipientType.CC, mailBuild.eliminate(cc));
|
||||
}
|
||||
if (bcc != null && bcc.size() > 0) {
|
||||
if (CollUtil.isNotEmpty(bcc)) {
|
||||
message.addRecipients(Message.RecipientType.BCC, mailBuild.eliminate(bcc));
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,10 +19,5 @@
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-comm</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@ -14,6 +14,10 @@ import java.util.List;
|
||||
**/
|
||||
public interface SmsBlend {
|
||||
|
||||
String getConfigId();
|
||||
|
||||
String getSupplier();
|
||||
|
||||
SmsResponse sendMessage(String phone, String message);
|
||||
|
||||
SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages);
|
||||
@ -37,4 +41,5 @@ public interface SmsBlend {
|
||||
void delayMassTexting(List<String> phones, String message, Long delayedTime);
|
||||
|
||||
void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
package org.dromara.sms4j.api.dao;
|
||||
|
||||
/**
|
||||
* DAO 接口
|
||||
*
|
||||
* @author Wind
|
||||
* @author Charles7c
|
||||
* @since 2023/8/5 20:03
|
||||
*/
|
||||
public interface SmsDao {
|
||||
|
||||
/**
|
||||
* 存储
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
* @param cacheTime 缓存时间(单位:秒)
|
||||
*/
|
||||
void set(String key, Object value, long cacheTime);
|
||||
|
||||
/**
|
||||
* 存储
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
*/
|
||||
void set(String key, Object value);
|
||||
|
||||
/**
|
||||
* 读取
|
||||
*
|
||||
* @param key 键
|
||||
* @return 值
|
||||
*/
|
||||
Object get(String key);
|
||||
|
||||
/**
|
||||
* 清空
|
||||
*/
|
||||
void clean();
|
||||
}
|
||||
@ -0,0 +1,166 @@
|
||||
package org.dromara.sms4j.api.dao;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* DAO 默认实现(内部缓存)
|
||||
*
|
||||
* @author Wind
|
||||
* @author Charles7c
|
||||
* @since 2023/8/5 20:36
|
||||
*/
|
||||
@Slf4j
|
||||
public class SmsDaoDefaultImpl implements SmsDao {
|
||||
|
||||
private static volatile SmsDaoDefaultImpl INSTANCE;
|
||||
private static final Timer TIMER = new Timer();
|
||||
private static final ConcurrentHashMap<String, DataWrapper> DATA_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 缓存时间(单位:毫秒,默认 24 小时)
|
||||
*/
|
||||
private static final long DEFAULT_CACHE_TIME = 24 * 60 * 60 * 1000L;
|
||||
|
||||
/**
|
||||
* 定时器执行频率(单位:毫秒,默认 30 秒)
|
||||
*/
|
||||
private static final long TIMER_INTERVAL = 30 * 1000L;
|
||||
|
||||
private SmsDaoDefaultImpl() {}
|
||||
|
||||
/**
|
||||
* 获取唯一实例
|
||||
*
|
||||
* @return 唯一实例
|
||||
*/
|
||||
public static SmsDaoDefaultImpl getInstance() {
|
||||
if (null == INSTANCE) {
|
||||
synchronized (SmsDaoDefaultImpl.class) {
|
||||
if (null == INSTANCE) {
|
||||
INSTANCE = new SmsDaoDefaultImpl();
|
||||
// 初始化定时器
|
||||
initTimer();
|
||||
}
|
||||
}
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, Object value, long cacheTime) {
|
||||
cacheTime = cacheTime * 1000L;
|
||||
DataWrapper dataWrapper = DATA_MAP.get(key);
|
||||
if (null != dataWrapper) {
|
||||
dataWrapper.update(value, cacheTime);
|
||||
} else {
|
||||
dataWrapper = new DataWrapper(value, cacheTime);
|
||||
DATA_MAP.put(key, dataWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, Object value) {
|
||||
this.set(key, value, DEFAULT_CACHE_TIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String key) {
|
||||
DataWrapper dataWrapper = DATA_MAP.get(key);
|
||||
if (dataWrapper != null && !dataWrapper.isExpired()) {
|
||||
return dataWrapper.data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clean() {
|
||||
DATA_MAP.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化定时器
|
||||
*/
|
||||
private static void initTimer() {
|
||||
TIMER.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
clearExpiredData();
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}, TIMER_INTERVAL, TIMER_INTERVAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除过期数据
|
||||
*/
|
||||
private static void clearExpiredData() {
|
||||
List<String> expiredKeyList = new LinkedList<>();
|
||||
for (Map.Entry<String, DataWrapper> entry : DATA_MAP.entrySet()) {
|
||||
if (entry.getValue().isExpired()) {
|
||||
expiredKeyList.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
for (String key : expiredKeyList) {
|
||||
DATA_MAP.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据封装
|
||||
*/
|
||||
@Data
|
||||
private static class DataWrapper {
|
||||
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
private Object data;
|
||||
|
||||
/**
|
||||
* 过期时间
|
||||
*/
|
||||
private long expiredTime;
|
||||
|
||||
/**
|
||||
* 缓存时间
|
||||
*/
|
||||
private long cacheTime;
|
||||
|
||||
private DataWrapper(Object data, long cacheTime) {
|
||||
this.update(data, cacheTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新数据及缓存时间
|
||||
*
|
||||
* @param data 数据
|
||||
* @param cacheTime 缓存时间
|
||||
*/
|
||||
public void update(Object data, long cacheTime) {
|
||||
this.data = data;
|
||||
this.cacheTime = cacheTime;
|
||||
this.expiredTime = System.currentTimeMillis() + cacheTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据是否过期
|
||||
*
|
||||
* @return true:过期,false:未过期
|
||||
*/
|
||||
public boolean isExpired() {
|
||||
if (this.expiredTime > 0) {
|
||||
return System.currentTimeMillis() > this.expiredTime;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package org.dromara.sms4j.api.proxy;
|
||||
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
|
||||
/**
|
||||
* 短信拦截处理接口
|
||||
*/
|
||||
public interface RestrictedProcess {
|
||||
|
||||
/**
|
||||
* 拦截校验过程
|
||||
* @param phone
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
SmsBlendException process(String phone) throws Exception;
|
||||
|
||||
}
|
||||
@ -1,43 +0,0 @@
|
||||
package org.dromara.sms4j.api.smsProxy;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.comm.config.SmsConfig;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.comm.utils.TimeExpiredPoolCache;
|
||||
|
||||
@Slf4j
|
||||
public class RestrictedProcess {
|
||||
static Long minTimer = 60 * 1000L;
|
||||
static Long accTimer = 24 * 60 * 60 * 1000L;
|
||||
public SmsBlendException process(SmsConfig config, String args) throws Exception{
|
||||
TimeExpiredPoolCache instance = TimeExpiredPoolCache.getInstance();//缓存实例
|
||||
Integer accountMax = config.getAccountMax();//每日最大发送量
|
||||
Integer minuteMax = config.getMinuteMax();//每分钟最大发送量
|
||||
if (SmsUtil.isNotEmpty(accountMax)) { //是否配置了每日限制
|
||||
Integer i = instance.get(args + "max");
|
||||
if (SmsUtil.isEmpty(i)) {
|
||||
instance.put(args + "max", 1, accTimer);
|
||||
} else if (i > accountMax) {
|
||||
log.info("The phone:" + args + ",number of short messages reached the maximum today");
|
||||
return new SmsBlendException("The phone:" + args + ",number of short messages reached the maximum today");
|
||||
} else {
|
||||
instance.put(args + "max", i + 1, accTimer);
|
||||
}
|
||||
}
|
||||
if (SmsUtil.isNotEmpty(minuteMax)) { //是否配置了每分钟最大限制
|
||||
Integer o = instance.get(args);
|
||||
if (SmsUtil.isNotEmpty(o)) {
|
||||
if (o < minuteMax) {
|
||||
instance.put(args, o + 1, minTimer);
|
||||
} else {
|
||||
log.info("The phone:", args + " Text messages are sent too often!");
|
||||
return new SmsBlendException("The phone:", args + " Text messages are sent too often!");
|
||||
}
|
||||
} else {
|
||||
instance.put(args, 1, minTimer);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
package org.dromara.sms4j.api.universal;
|
||||
|
||||
/**
|
||||
* SmsRedisUtil
|
||||
* <p> redis工具接口,用户可自主实现以更换redis的来源
|
||||
*@since 2.2.0
|
||||
* @author :Wind
|
||||
* 2023/6/6 22:21
|
||||
**/
|
||||
public interface SmsRedisUtil {
|
||||
|
||||
/**
|
||||
* setOrTime
|
||||
* <p>设置带有过期时间的key
|
||||
* @param key redis的key
|
||||
* @param value redis 的value
|
||||
* @param time 过期时间(秒级单位)
|
||||
* @author :Wind
|
||||
*/
|
||||
public boolean setOrTime(String key, Object value, Long time);
|
||||
|
||||
/**
|
||||
* getByKey
|
||||
* <p>根据key获取redis中缓存的数据
|
||||
* @param key redis的key
|
||||
* @author :Wind
|
||||
*/
|
||||
public Object getByKey(String key);
|
||||
}
|
||||
@ -2,9 +2,24 @@ package org.dromara.sms4j.api.universal;
|
||||
|
||||
/**
|
||||
* SupplierConfig
|
||||
* <p> 空接口,无含义,只为标定配置类的额外类型
|
||||
* <p> 标定配置类的额外类型
|
||||
* @author :Wind
|
||||
* 2023/5/16 15:14
|
||||
**/
|
||||
public interface SupplierConfig {
|
||||
|
||||
/**
|
||||
* 获取配置标识名
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
String getConfigId();
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
String getSupplier();
|
||||
|
||||
}
|
||||
|
||||
@ -25,5 +25,11 @@
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-json</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-http</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
package org.dromara.sms4j.comm.annotation;
|
||||
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* <p>类名: Restricted
|
||||
* <p>说明: 发送短信限制
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/3/26 17:12
|
||||
**/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Restricted {
|
||||
}
|
||||
@ -1,51 +0,0 @@
|
||||
package org.dromara.sms4j.comm.config;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 短信配置属性基类
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/20 23:03
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
public class BaseConfig {
|
||||
|
||||
/**
|
||||
* Access Key
|
||||
*/
|
||||
private String accessKeyId;
|
||||
|
||||
/**
|
||||
* Access Key Secret
|
||||
*/
|
||||
private String accessKeySecret;
|
||||
|
||||
/**
|
||||
* 短信签名
|
||||
*/
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* 模板 ID
|
||||
*/
|
||||
private String templateId;
|
||||
|
||||
/**
|
||||
* 权重
|
||||
*/
|
||||
@Builder.Default
|
||||
private Integer weight = 1;
|
||||
|
||||
/**
|
||||
* 配置标识名 如未配置取对应渠道名例如 Alibaba
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private String configId;
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
package org.dromara.sms4j.comm.config;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* SmsSqlConfig
|
||||
* <p> sql配置信息
|
||||
* @author :Wind
|
||||
* 2023/4/5 18:28
|
||||
**/
|
||||
@Data
|
||||
public class SmsSqlConfig {
|
||||
|
||||
/**
|
||||
* 连接地址
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 驱动
|
||||
*/
|
||||
private String driverClassName;
|
||||
|
||||
/**
|
||||
* 账号名
|
||||
*/
|
||||
private String username;
|
||||
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
private String password;
|
||||
|
||||
/**
|
||||
* 数据库名
|
||||
*/
|
||||
@Deprecated
|
||||
private String databaseName;
|
||||
|
||||
/**
|
||||
* 配置表 表名
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 启用字段名
|
||||
*/
|
||||
private String startName;
|
||||
|
||||
/**
|
||||
* 启用字段标识
|
||||
*/
|
||||
private String isStart;
|
||||
|
||||
/**
|
||||
* 配置字段名
|
||||
*/
|
||||
private String configName;
|
||||
|
||||
/** 厂商配置字段名*/
|
||||
private String supplierFieldName;
|
||||
|
||||
}
|
||||
@ -28,6 +28,8 @@ public abstract class Constant {
|
||||
*/
|
||||
public static final String FROM_URLENCODED = "application/x-www-form-urlencoded";
|
||||
|
||||
public static final String ACCEPT = "application/json";
|
||||
|
||||
public static final String APPLICATION_JSON_UTF8 = "application/json; charset=utf-8";
|
||||
|
||||
/**
|
||||
@ -43,6 +45,11 @@ public abstract class Constant {
|
||||
*/
|
||||
public static final String HTTPS_PREFIX = "https://";
|
||||
|
||||
/**
|
||||
* 供应商配置键名
|
||||
*/
|
||||
public static final String SUPPLIER_KEY = "supplier";
|
||||
|
||||
private Constant() {
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,16 +7,10 @@ package org.dromara.sms4j.comm.enumerate;
|
||||
* 2023/4/5 19:08
|
||||
**/
|
||||
public enum ConfigType {
|
||||
/** 配置文件*/
|
||||
CONFIG_FILE("configFile"),
|
||||
/** setting配置文件*/
|
||||
SETTINGS_FILE("settingsFile"),
|
||||
|
||||
/** 数据库配置*/
|
||||
SQL_CONFIG("sqlConfig"),
|
||||
|
||||
/** nacos配置*/
|
||||
NACOS_CONFIG("nacosConfig");
|
||||
/** yaml配置文件 */
|
||||
YAML("yaml"),
|
||||
/** 接口 */
|
||||
INTERFACE("interface");
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
package org.dromara.sms4j.comm.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class FileTool {
|
||||
|
||||
private FileTool(){}
|
||||
|
||||
/**
|
||||
* getPath
|
||||
* <p>获取用户工作目录
|
||||
* @author :Wind
|
||||
*/
|
||||
public static String getPath(){
|
||||
return System.getProperty("user.dir");
|
||||
}
|
||||
|
||||
/**
|
||||
* createFile
|
||||
* <p>创建文件
|
||||
* @param path 要创建的文件路径
|
||||
* @return 文件创建成功或存在则为true
|
||||
* @author :Wind
|
||||
*/
|
||||
public static boolean createFile(String path){
|
||||
File file = new File(path);
|
||||
try {
|
||||
file.createNewFile();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* writeFile
|
||||
* <p>写入文件
|
||||
* @param isAppend 是否追加写入,true追加写入,false覆盖写入
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void writeFile(File file,String content,boolean isAppend){
|
||||
Writer writer = null;
|
||||
try {
|
||||
writer = new FileWriter(file,isAppend);
|
||||
writer.write(content);
|
||||
}catch (IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}finally {
|
||||
try {
|
||||
writer.close();
|
||||
}catch (IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* readFile
|
||||
* <p>读取文件为字符串
|
||||
* @param path 文件路径
|
||||
* @author :Wind
|
||||
*/
|
||||
public static String readFile(String path) throws IOException {
|
||||
return new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
@ -1,151 +0,0 @@
|
||||
package org.dromara.sms4j.comm.utils;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.dromara.sms4j.comm.config.SmsSqlConfig;
|
||||
import org.dromara.sms4j.comm.exception.SmsSqlException;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.Hashtable;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* JDBCTool
|
||||
* <p> 数据库相关工具
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/4/4 22:34
|
||||
**/
|
||||
public class JDBCTool {
|
||||
|
||||
public static final String SELECT = "select {},{} from {} where {} = {}";
|
||||
private static final String ERR_CONFIG = "One configuration was expected, but {} was found. Please check your database configuration";
|
||||
private final SmsSqlConfig config;
|
||||
/**
|
||||
* 数据库链接
|
||||
* */
|
||||
private Connection connection;
|
||||
|
||||
|
||||
public JDBCTool(SmsSqlConfig config,Connection connection) {
|
||||
// if (config == null) {
|
||||
// throw new SmsSqlException("The configuration file failed to be loaded");
|
||||
// }
|
||||
this.config = config;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
public JDBCTool(SmsSqlConfig config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public void setConnection(Connection connection){
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
public static Map<String, String> selectConfig() {
|
||||
Map<String, String> select = BeanFactory.getJDBCTool().select();
|
||||
if (select.size() == 0) {
|
||||
throw new SmsSqlException("No valid configuration was scanned. Please check whether the configuration file or database link is normal");
|
||||
}
|
||||
return select;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取链接
|
||||
*/
|
||||
public Connection getConn() {
|
||||
Connection connection;
|
||||
String url;
|
||||
if (SmsUtil.isEmpty(config.getUrl())) {
|
||||
throw new SmsSqlException("The configuration file failed to be loaded");
|
||||
}
|
||||
try {
|
||||
if (config.getDatabaseName().isEmpty()){
|
||||
url = config.getUrl();
|
||||
}else{
|
||||
url = config.getUrl() + "/" + config.getDatabaseName();
|
||||
}
|
||||
connection = DriverManager.getConnection(url, config.getUsername(), config.getPassword());
|
||||
} catch (SQLException e) {
|
||||
throw new SmsSqlException(e.getMessage());
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* select
|
||||
* <p>查询封装
|
||||
* @param sql 要查询的sql语句
|
||||
* @author :Wind
|
||||
*/
|
||||
public Map<String, String> select(String sql) {
|
||||
// Connection conn = null;
|
||||
if (Objects.isNull(connection)){
|
||||
connection = getConn();
|
||||
}
|
||||
PreparedStatement preparedStatement = null;
|
||||
ResultSet resultSet = null;
|
||||
Map<String, String> map = new Hashtable<>();
|
||||
try {
|
||||
// conn = getConn();
|
||||
preparedStatement = connection.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
|
||||
preparedStatement.setFetchSize(1000);
|
||||
resultSet = preparedStatement.executeQuery();
|
||||
|
||||
while (resultSet.next()) {
|
||||
String data = resultSet.getString(config.getConfigName());
|
||||
String supplier = resultSet.getString(config.getSupplierFieldName());
|
||||
map.put(supplier, data);
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
throw new SmsSqlException(e.getMessage());
|
||||
} finally {
|
||||
close(connection, preparedStatement, resultSet);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* close
|
||||
* <p>关闭链接方法
|
||||
* @author :Wind
|
||||
*/
|
||||
private void close(Connection conn, PreparedStatement stmt, ResultSet rs) {
|
||||
// 关闭连接
|
||||
if (conn != null) {
|
||||
// try {
|
||||
// conn.close();
|
||||
// } catch (SQLException e) {
|
||||
// throw new SmsSqlException(e.getMessage());
|
||||
// }
|
||||
}
|
||||
// 关闭 statement
|
||||
if (stmt != null) {
|
||||
try {
|
||||
stmt.close();
|
||||
} catch (SQLException e) {
|
||||
throw new SmsSqlException(e.getMessage());
|
||||
}
|
||||
}
|
||||
// 关闭结果集
|
||||
if (rs != null) {
|
||||
try {
|
||||
rs.close();
|
||||
} catch (SQLException e) {
|
||||
throw new SmsSqlException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> select() {
|
||||
String format = StrUtil.format(SELECT,
|
||||
config.getConfigName(),
|
||||
config.getSupplierFieldName(),
|
||||
config.getTableName(),
|
||||
config.getStartName(),
|
||||
config.getIsStart());
|
||||
return select(format);
|
||||
}
|
||||
}
|
||||
@ -1,16 +0,0 @@
|
||||
package org.dromara.sms4j.comm.utils;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* REST API 函数式接口
|
||||
*
|
||||
* @param <P> 请求参数
|
||||
* @param <R> 响应
|
||||
* @author Charles7c
|
||||
* @since 2023/4/17 20:57
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface RestApiFunction<P, R> extends Serializable {
|
||||
R apply(P param);
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package org.dromara.sms4j.comm.utils;
|
||||
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SmsHttpUtil {
|
||||
|
||||
private SmsHttpUtil() {
|
||||
}
|
||||
|
||||
private static class SmsHttpHolder {
|
||||
private static final SmsHttpUtil INSTANCE = new SmsHttpUtil();
|
||||
}
|
||||
|
||||
public static SmsHttpUtil instance() {
|
||||
return SmsHttpHolder.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送post json请求
|
||||
* @param url 请求地址
|
||||
* @param headers 请求头
|
||||
* @param body 请求体(json格式字符串)
|
||||
* @return 返回体
|
||||
*/
|
||||
public JSONObject postJson(String url, Map<String, String> headers, String body){
|
||||
try(HttpResponse response = HttpRequest.post(url)
|
||||
.addHeaders(headers)
|
||||
.body(body)
|
||||
.execute()){
|
||||
return JSONUtil.parseObj(response.body());
|
||||
}catch (Exception e){
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送post json请求
|
||||
* @param url 请求地址
|
||||
* @param headers 请求头
|
||||
* @param body 请求体(map格式请求体)
|
||||
* @return 返回体
|
||||
*/
|
||||
public JSONObject postJson(String url, Map<String, String> headers, Map<String, Object> body){
|
||||
return postJson(url, headers, JSONUtil.toJsonStr(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送post form 请求
|
||||
* @param url 请求地址
|
||||
* @param headers 请求头
|
||||
* @param body 请求体(map格式请求体)
|
||||
* @return 返回体
|
||||
*/
|
||||
public JSONObject postFrom(String url, Map<String, String> headers, Map<String, Object> body){
|
||||
try(HttpResponse response = HttpRequest.post(url)
|
||||
.addHeaders(headers)
|
||||
.form(body)
|
||||
.execute()){
|
||||
return JSONUtil.parseObj(response.body());
|
||||
}catch (Exception e){
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 线程睡眠
|
||||
* @param retryInterval 秒
|
||||
*/
|
||||
public void safeSleep(int retryInterval){
|
||||
ThreadUtil.safeSleep(retryInterval * 1000L);
|
||||
}
|
||||
}
|
||||
@ -4,11 +4,13 @@ import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author wind
|
||||
@ -122,7 +124,7 @@ public class SmsUtil {
|
||||
* @return 结果字符串
|
||||
*/
|
||||
public static String arrayToString(List<String> list) {
|
||||
return CollUtil.join(list, ",", "+86", "");
|
||||
return CollUtil.join(list, ",", str -> StrUtil.addPrefixIfNot(str, "+86"));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -134,9 +136,31 @@ public class SmsUtil {
|
||||
public static String[] listToArray(List<String> list) {
|
||||
List<String> toStr = new ArrayList<>();
|
||||
for (String s : list) {
|
||||
toStr.add("+86" + s);
|
||||
toStr.add(StrUtil.addPrefixIfNot(s, "+86"));
|
||||
}
|
||||
return toStr.toArray(new String[list.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将Map中所有key的分隔符转换为新的分隔符
|
||||
* @param map map对象
|
||||
* @param seperator 旧分隔符
|
||||
* @param newSeperator 新分隔符
|
||||
*/
|
||||
public static void replaceKeysSeperator(Map<String, Object> map, String seperator, String newSeperator) {
|
||||
if(CollUtil.isEmpty(map)) {
|
||||
return;
|
||||
}
|
||||
List<String> keySet = new ArrayList<>(map.keySet());
|
||||
for(String key : keySet) {
|
||||
if(StrUtil.isEmpty(key) || !key.contains(seperator)) {
|
||||
continue;
|
||||
}
|
||||
String value = String.valueOf(map.get(key));
|
||||
String newKey = key.replaceAll(seperator, newSeperator);
|
||||
map.putIfAbsent(newKey, value);
|
||||
map.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,252 +0,0 @@
|
||||
package org.dromara.sms4j.comm.utils;
|
||||
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* <p>类名: TimeExpiredPoolCache
|
||||
* <p>说明: 一个自实现的内部缓存,可用于无法使用redis的场景
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/3/25 18:26
|
||||
**/
|
||||
@Slf4j
|
||||
public class TimeExpiredPoolCache {
|
||||
|
||||
/**
|
||||
* 持久化文件格式
|
||||
*/
|
||||
private static final String FILE_TYPE = "persistence.data";
|
||||
private static final long defaultCachedMillis = 24 * 60 * 60 * 1000L;//过期时间默认24小时
|
||||
private static final long timerMillis = 30 * 1000L;//定时清理默认1分钟
|
||||
/**
|
||||
* 对象池
|
||||
*/
|
||||
private static ConcurrentHashMap<String, DataWrapper<?>> dataPool = null;
|
||||
/**
|
||||
* 对象单例
|
||||
*/
|
||||
private static TimeExpiredPoolCache instance = null;
|
||||
/**
|
||||
* 定时器定时清理过期缓存
|
||||
*/
|
||||
private static final Timer timer = new Timer();
|
||||
|
||||
private TimeExpiredPoolCache() {
|
||||
}
|
||||
|
||||
private static synchronized void syncInit() {
|
||||
if (instance == null) {
|
||||
instance = new TimeExpiredPoolCache();
|
||||
dataPool = new ConcurrentHashMap<>();
|
||||
initTimer();
|
||||
}
|
||||
}
|
||||
|
||||
public static TimeExpiredPoolCache getInstance() {
|
||||
if (instance == null) {
|
||||
syncInit();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取持久化文件
|
||||
*/
|
||||
private static boolean persistenceInit() {
|
||||
String path = FileTool.getPath() + FILE_TYPE;
|
||||
try {
|
||||
DataWrapper d = JSONUtil.toBean(FileTool.readFile(path), DataWrapper.class);
|
||||
if (dataPool != null) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
log.error(e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void initTimer() {
|
||||
timer.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
clearExpiredCaches();
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
}
|
||||
}, timerMillis, timerMillis);
|
||||
}
|
||||
|
||||
/** 写入持久化文件*/
|
||||
private static void persistence() {
|
||||
String path = FileTool.getPath() + FILE_TYPE;
|
||||
FileTool.createFile(path);
|
||||
FileTool.writeFile(new File(path), JSONUtil.toJsonStr(dataPool), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除过期的缓存
|
||||
*/
|
||||
private static void clearExpiredCaches() {
|
||||
List<String> expiredKeyList = new LinkedList<>();
|
||||
|
||||
for (Entry<String, DataWrapper<?>> entry : dataPool.entrySet()) {
|
||||
if (entry.getValue().isExpired()) {
|
||||
expiredKeyList.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
for (String key : expiredKeyList) {
|
||||
dataPool.remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存数据
|
||||
*
|
||||
* @param key key值
|
||||
* @param data 缓存数据
|
||||
* @param cachedMillis 过期时间
|
||||
* @param dataRenewer 刷新数据
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T put(String key, T data, long cachedMillis, DataRenewer<T> dataRenewer) throws Exception {
|
||||
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
|
||||
if (data == null && dataRenewer != null) {
|
||||
data = dataRenewer.renewData();
|
||||
}
|
||||
//当重新获取数据为空,直接返回不做put
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
if (dataWrapper != null) {
|
||||
//更新
|
||||
dataWrapper.update(data, cachedMillis);
|
||||
} else {
|
||||
dataWrapper = new DataWrapper<>(data, cachedMillis);
|
||||
dataPool.put(key, dataWrapper);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接设置缓存值和时间
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T put(String key, T data, long cachedMillis) throws Exception {
|
||||
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
|
||||
if (dataWrapper != null) {
|
||||
//更新
|
||||
dataWrapper.update(data, cachedMillis);
|
||||
} else {
|
||||
dataWrapper = new DataWrapper<T>(data, cachedMillis);
|
||||
dataPool.put(key, dataWrapper);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认构造时间的缓存数据
|
||||
*/
|
||||
@Deprecated
|
||||
public <T> T put(String key, T data, DataRenewer<T> dataRenewer) throws Exception {
|
||||
return put(key, data, defaultCachedMillis, dataRenewer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String key, long cachedMillis, DataRenewer<T> dataRenewer) throws Exception {
|
||||
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
|
||||
if (dataWrapper != null && !dataWrapper.isExpired()) {
|
||||
return dataWrapper.data;
|
||||
}
|
||||
return put(key, null, cachedMillis, dataRenewer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(String key) {
|
||||
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
|
||||
if (dataWrapper != null && !dataWrapper.isExpired()) {
|
||||
return dataWrapper.data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存
|
||||
*/
|
||||
public void clear() {
|
||||
dataPool.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除指定key的value
|
||||
*/
|
||||
public void remove(String key) {
|
||||
dataPool.remove(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据构造
|
||||
*/
|
||||
public interface DataRenewer<T> {
|
||||
public T renewData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据封装
|
||||
*/
|
||||
private static class DataWrapper<T> {
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
private T data;
|
||||
/**
|
||||
* 到期时间
|
||||
*/
|
||||
private long expiredTime;
|
||||
/**
|
||||
* 缓存时间
|
||||
*/
|
||||
private long cachedMillis;
|
||||
|
||||
private DataWrapper(T data, long cachedMillis) {
|
||||
this.update(data, cachedMillis);
|
||||
}
|
||||
|
||||
public void update(T data, long cachedMillis) {
|
||||
this.data = data;
|
||||
this.cachedMillis = cachedMillis;
|
||||
this.updateExpiredTime();
|
||||
}
|
||||
|
||||
public void updateExpiredTime() {
|
||||
this.expiredTime = System.currentTimeMillis() + cachedMillis;
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据是否过期
|
||||
*/
|
||||
public boolean isExpired() {
|
||||
if (this.expiredTime > 0) {
|
||||
return System.currentTimeMillis() > this.expiredTime;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,163 +0,0 @@
|
||||
package org.dromara.sms4j.core;
|
||||
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenConfig;
|
||||
import org.dromara.sms4j.comm.utils.JDBCTool;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.core.config.SupplierFactory;
|
||||
import org.dromara.sms4j.ctyun.config.CtyunConfig;
|
||||
import org.dromara.sms4j.emay.config.EmayConfig;
|
||||
import org.dromara.sms4j.huawei.config.HuaweiConfig;
|
||||
import org.dromara.sms4j.jdcloud.config.JdCloudConfig;
|
||||
import org.dromara.sms4j.netease.config.NeteaseConfig;
|
||||
import org.dromara.sms4j.provider.enumerate.SupplierType;
|
||||
import org.dromara.sms4j.tencent.config.TencentConfig;
|
||||
import org.dromara.sms4j.unisms.config.UniConfig;
|
||||
import org.dromara.sms4j.yunpian.config.YunpianConfig;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* SupplierSqlConfig
|
||||
* <p> 处理sql配置源
|
||||
* @author :Wind
|
||||
* 2023/4/11 19:42
|
||||
**/
|
||||
public class SupplierSqlConfig {
|
||||
private static Map<String, String> select;
|
||||
|
||||
/**
|
||||
* readSqlConfig
|
||||
* <p>读取数据库配置信息
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void readSqlConfig(){
|
||||
select = JDBCTool.selectConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* refreshSqlConfig
|
||||
* <p>读取并刷新数据库配置
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void refreshSqlConfig(){
|
||||
readSqlConfig();
|
||||
alibaba();
|
||||
huawei();
|
||||
jingdong();
|
||||
tencent();
|
||||
uniSms();
|
||||
yunPian();
|
||||
cloopen();
|
||||
emay();
|
||||
ctyun();
|
||||
netease();
|
||||
}
|
||||
|
||||
public SupplierSqlConfig() {
|
||||
refreshSqlConfig();
|
||||
}
|
||||
|
||||
public static SupplierSqlConfig newSupplierSqlConfig(){
|
||||
return new SupplierSqlConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* alibaba
|
||||
* <p>数据库读取并设置阿里云短信
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void alibaba(){
|
||||
AlibabaConfig alibabaConfig = SmsUtil.jsonForObject(select.get(SupplierType.ALIBABA.getName()), AlibabaConfig.class);
|
||||
SupplierFactory.setAlibabaConfig(alibabaConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* huawei
|
||||
* <p>数据库读取并设置华为短信
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void huawei(){
|
||||
HuaweiConfig huaweiConfig = SmsUtil.jsonForObject(select.get(SupplierType.HUAWEI.getName()), HuaweiConfig.class);
|
||||
SupplierFactory.setHuaweiConfig(huaweiConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* jingdong
|
||||
* <p>数据库读取并设置京东短信
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void jingdong(){
|
||||
JdCloudConfig jdCloudConfig = SmsUtil.jsonForObject(select.get(SupplierType.JD_CLOUD.getName()), JdCloudConfig.class);
|
||||
SupplierFactory.setJdCloudConfig(jdCloudConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* tencent
|
||||
* <p>数据库读取并设置腾讯短信
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void tencent(){
|
||||
TencentConfig tencentConfig = SmsUtil.jsonForObject(select.get(SupplierType.TENCENT.getName()), TencentConfig.class);
|
||||
SupplierFactory.setTencentConfig(tencentConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* uniSms
|
||||
* <p>数据库读取并设置合一短信配置
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void uniSms(){
|
||||
UniConfig uniConfig = SmsUtil.jsonForObject(select.get(SupplierType.UNI_SMS.getName()), UniConfig.class);
|
||||
SupplierFactory.setUniConfig(uniConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* yunPian
|
||||
* <p>数据库读取并设置云片短信
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void yunPian(){
|
||||
YunpianConfig yunpianConfig = SmsUtil.jsonForObject(select.get(SupplierType.YUNPIAN.getName()), YunpianConfig.class);
|
||||
SupplierFactory.setYunpianConfig(yunpianConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* cloopen
|
||||
* <p>数据库读取并设置容联云短信
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void cloopen(){
|
||||
CloopenConfig cloopenConfig = SmsUtil.jsonForObject(select.get(SupplierType.CLOOPEN.getName()), CloopenConfig.class);
|
||||
SupplierFactory.setCloopenConfig(cloopenConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* emay
|
||||
* <p>数据库读取并设置亿美软通短信
|
||||
*/
|
||||
public static void emay() {
|
||||
EmayConfig emayConfig = SmsUtil.jsonForObject(select.get(SupplierType.EMAY.getName()), EmayConfig.class);
|
||||
SupplierFactory.setEmayConfig(emayConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* ctyun
|
||||
* <p>数据库读取并设置天翼云短信
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void ctyun(){
|
||||
CtyunConfig ctyunConfig = SmsUtil.jsonForObject(select.get(SupplierType.CTYUN.getName()), CtyunConfig.class);
|
||||
SupplierFactory.setCtyunConfig(ctyunConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* netease
|
||||
* <p>数据库读取并设置网易云短信
|
||||
* @author : Adam
|
||||
*/
|
||||
public static void netease(){
|
||||
NeteaseConfig neteaseConfig = SmsUtil.jsonForObject(select.get(SupplierType.NETEASE.getName()), NeteaseConfig.class);
|
||||
SupplierFactory.setNeteaseConfig(neteaseConfig);
|
||||
}
|
||||
}
|
||||
@ -1,247 +0,0 @@
|
||||
package org.dromara.sms4j.core.config;
|
||||
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaFactory;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenConfig;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenFactory;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.core.factory.SmsFactory;
|
||||
import org.dromara.sms4j.core.load.SmsLoad;
|
||||
import org.dromara.sms4j.ctyun.config.CtyunConfig;
|
||||
import org.dromara.sms4j.ctyun.config.CtyunFactory;
|
||||
import org.dromara.sms4j.emay.config.EmayConfig;
|
||||
import org.dromara.sms4j.emay.config.EmayFactory;
|
||||
import org.dromara.sms4j.huawei.config.HuaweiConfig;
|
||||
import org.dromara.sms4j.huawei.config.HuaweiFactory;
|
||||
import org.dromara.sms4j.jdcloud.config.JdCloudConfig;
|
||||
import org.dromara.sms4j.jdcloud.config.JdCloudFactory;
|
||||
import org.dromara.sms4j.netease.config.NeteaseConfig;
|
||||
import org.dromara.sms4j.netease.config.NeteaseFactory;
|
||||
import org.dromara.sms4j.provider.enumerate.SupplierType;
|
||||
import org.dromara.sms4j.tencent.config.TencentConfig;
|
||||
import org.dromara.sms4j.tencent.config.TencentFactory;
|
||||
import org.dromara.sms4j.unisms.config.UniConfig;
|
||||
import org.dromara.sms4j.unisms.config.UniFactory;
|
||||
import org.dromara.sms4j.yunpian.config.YunPianFactory;
|
||||
import org.dromara.sms4j.yunpian.config.YunpianConfig;
|
||||
import org.dromara.sms4j.zhutong.config.ZhutongConfig;
|
||||
import org.dromara.sms4j.zhutong.config.ZhutongFactory;
|
||||
|
||||
/**
|
||||
* SupplierFactory
|
||||
* <p> 差异化配置工厂
|
||||
* @author :Wind
|
||||
* 2023/4/8 15:02
|
||||
**/
|
||||
public class SupplierFactory {
|
||||
private SupplierFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 阿里云配置获取
|
||||
*/
|
||||
public static AlibabaConfig getAlibabaConfig() {
|
||||
return AlibabaFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 华为云配置获取
|
||||
*/
|
||||
public static HuaweiConfig getHuaweiConfig() {
|
||||
return HuaweiFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 合一短信配置获取
|
||||
*/
|
||||
public static UniConfig getUniConfig() {
|
||||
return UniFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 腾讯短信配置获取
|
||||
*/
|
||||
public static TencentConfig getTencentConfig() {
|
||||
return TencentFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 云片短信配置获取
|
||||
*/
|
||||
public static YunpianConfig getYunpianConfig() {
|
||||
return YunPianFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 京东云短信配置获取
|
||||
*/
|
||||
public static JdCloudConfig getJdCloudConfig() {
|
||||
return JdCloudFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 容联云短信配置获取
|
||||
*/
|
||||
public static CloopenConfig getCloopenConfig() {
|
||||
return CloopenFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 亿美软通配置获取
|
||||
*/
|
||||
public static EmayConfig getEmayConfig() {
|
||||
return EmayFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 天翼云配置获取
|
||||
*/
|
||||
public static CtyunConfig getCtyunConfig() {
|
||||
return CtyunFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 网易云信配置获取
|
||||
*/
|
||||
public static NeteaseConfig getNeteaseConfig() {
|
||||
return NeteaseFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 助通信配置获取
|
||||
*/
|
||||
public static ZhutongConfig getZhutongConfig() {
|
||||
return ZhutongFactory.instance().getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* setSupplierConfig
|
||||
* <p>通用化set,用于设置
|
||||
* @param t 配置对象
|
||||
* @author :Wind
|
||||
*/
|
||||
public static <T extends SupplierConfig> void setSupplierConfig(T t) {
|
||||
if (t instanceof AlibabaConfig) {
|
||||
setAlibabaConfig((AlibabaConfig) t);
|
||||
} else if (t instanceof HuaweiConfig) {
|
||||
setHuaweiConfig((HuaweiConfig) t);
|
||||
} else if (t instanceof UniConfig) {
|
||||
setUniConfig((UniConfig) t);
|
||||
} else if (t instanceof TencentConfig) {
|
||||
setTencentConfig((TencentConfig) t);
|
||||
} else if (t instanceof YunpianConfig) {
|
||||
setYunpianConfig((YunpianConfig) t);
|
||||
} else if (t instanceof JdCloudConfig) {
|
||||
setJdCloudConfig((JdCloudConfig) t);
|
||||
} else if (t instanceof CloopenConfig) {
|
||||
setCloopenConfig((CloopenConfig) t);
|
||||
} else if (t instanceof EmayConfig) {
|
||||
setEmayConfig((EmayConfig) t);
|
||||
} else if (t instanceof CtyunConfig) {
|
||||
setCtyunConfig((CtyunConfig) t);
|
||||
} else if (t instanceof NeteaseConfig) {
|
||||
setNeteaseConfig((NeteaseConfig) t);
|
||||
} else if (t instanceof ZhutongConfig) {
|
||||
setZhuTongConfig((ZhutongConfig) t);
|
||||
} else {
|
||||
throw new SmsBlendException("Loading failure! Please check the configuration type.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 alibabaConfig
|
||||
*/
|
||||
public static void setAlibabaConfig(AlibabaConfig alibabaConfig) {
|
||||
AlibabaFactory.instance().setConfig(alibabaConfig);
|
||||
SmsFactory.refresh(SupplierType.ALIBABA);
|
||||
SmsLoad.starConfig(alibabaConfig,SupplierType.ALIBABA);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 huaweiConfig
|
||||
*/
|
||||
public static void setHuaweiConfig(HuaweiConfig huaweiConfig) {
|
||||
HuaweiFactory.instance().setConfig(huaweiConfig);
|
||||
SmsFactory.refresh(SupplierType.HUAWEI);
|
||||
SmsLoad.starConfig(huaweiConfig,SupplierType.HUAWEI);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 uniConfig
|
||||
*/
|
||||
public static void setUniConfig(UniConfig uniConfig) {
|
||||
UniFactory.instance().setConfig(uniConfig);
|
||||
SmsFactory.refresh(SupplierType.UNI_SMS);
|
||||
SmsLoad.starConfig(uniConfig,SupplierType.UNI_SMS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 tencentConfig
|
||||
*/
|
||||
public static void setTencentConfig(TencentConfig tencentConfig) {
|
||||
TencentFactory.instance().setConfig(tencentConfig);
|
||||
SmsFactory.refresh(SupplierType.TENCENT);
|
||||
SmsLoad.starConfig(tencentConfig,SupplierType.TENCENT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 yunpianConfig
|
||||
*/
|
||||
public static void setYunpianConfig(YunpianConfig yunpianConfig) {
|
||||
YunPianFactory.instance().setConfig(yunpianConfig);
|
||||
SmsFactory.refresh(SupplierType.YUNPIAN);
|
||||
SmsLoad.starConfig(yunpianConfig,SupplierType.YUNPIAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 jdCloudConfig
|
||||
*/
|
||||
public static void setJdCloudConfig(JdCloudConfig jdCloudConfig) {
|
||||
JdCloudFactory.instance().setConfig(jdCloudConfig);
|
||||
SmsFactory.refresh(SupplierType.JD_CLOUD);
|
||||
SmsLoad.starConfig(jdCloudConfig,SupplierType.JD_CLOUD);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 cloopenConfig
|
||||
*/
|
||||
public static void setCloopenConfig(CloopenConfig cloopenConfig) {
|
||||
CloopenFactory.instance().setConfig(cloopenConfig);
|
||||
SmsFactory.refresh(SupplierType.CLOOPEN);
|
||||
SmsLoad.starConfig(cloopenConfig,SupplierType.CLOOPEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 emayConfig
|
||||
*/
|
||||
public static void setEmayConfig(EmayConfig emayConfig) {
|
||||
EmayFactory.instance().setConfig(emayConfig);
|
||||
SmsFactory.refresh(SupplierType.EMAY);
|
||||
SmsLoad.starConfig(emayConfig,SupplierType.EMAY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 ctyunConfig
|
||||
*/
|
||||
public static void setCtyunConfig(CtyunConfig ctyunConfig) {
|
||||
CtyunFactory.instance().setConfig(ctyunConfig);
|
||||
SmsFactory.refresh(SupplierType.CTYUN);
|
||||
SmsLoad.starConfig(ctyunConfig,SupplierType.CTYUN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 neteaseConfig
|
||||
*/
|
||||
public static void setNeteaseConfig(NeteaseConfig neteaseConfig) {
|
||||
NeteaseFactory.instance().setConfig(neteaseConfig);
|
||||
SmsFactory.refresh(SupplierType.NETEASE);
|
||||
SmsLoad.starConfig(neteaseConfig,SupplierType.NETEASE);
|
||||
}
|
||||
|
||||
public static void setZhuTongConfig(ZhutongConfig zhutongConfig){
|
||||
ZhutongFactory.instance().setConfig(zhutongConfig);
|
||||
SmsFactory.refresh(SupplierType.ZHUTONG);
|
||||
SmsLoad.starConfig(zhutongConfig,SupplierType.ZHUTONG);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package org.dromara.sms4j.core.datainterface;
|
||||
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SmsReadConfig
|
||||
* <p> 读取配置接口,实现该接口中的方法则可以按照自己的形式进行配置的读取
|
||||
* <p>这样只关注最终的配置数据而不关注配置的来源,用户可以自由的选择数据来源的方式</p>
|
||||
* <p>该种方式读取配置并非在启动阶段完成,而是在方法第一次调用期间完成</p>
|
||||
* @author :Wind
|
||||
* 2023/8/1 12:06
|
||||
**/
|
||||
public interface SmsReadConfig {
|
||||
|
||||
/**
|
||||
* getSupplierConfig
|
||||
* <p> 通过配置ID获取一个厂商的配置
|
||||
* @param configId 配置id
|
||||
* @author :Wind
|
||||
*/
|
||||
BaseConfig getSupplierConfig(String configId);
|
||||
|
||||
/**
|
||||
* getSupplierConfigList
|
||||
* <p> 获取多个厂商的配置,会同时加载进框架中
|
||||
* @author :Wind
|
||||
*/
|
||||
List<BaseConfig> getSupplierConfigList();
|
||||
|
||||
}
|
||||
@ -1,141 +1,299 @@
|
||||
package org.dromara.sms4j.core.factory;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.smsProxy.SmsInvocationHandler;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.core.SupplierSqlConfig;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.core.datainterface.SmsReadConfig;
|
||||
import org.dromara.sms4j.core.load.SmsLoad;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.enumerate.SupplierType;
|
||||
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.BeanFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* SmsFactory
|
||||
* <p>构造工厂,用于获取一个厂商的短信实现对象
|
||||
* 在调用对应厂商的短信发送方法前,请先确保你的配置已经实现,否则无法发送该厂商对应的短信,一般情况下厂商会回执因缺少的配置所造成的的异常,但不会
|
||||
* 以java异常的形式抛出
|
||||
* 在调用对应厂商的短信发送方法前,请先确保你的配置已经实现,否则无法发送该厂商对应的短信,一般情况下厂商会回执因缺少的配置所造成的的异常,但组件
|
||||
* 不会处理
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/4/8 15:55
|
||||
**/
|
||||
public abstract class SmsFactory {
|
||||
|
||||
private static final Map<SupplierType, SmsBlend> beans = new HashMap<>();
|
||||
/**
|
||||
* <p>框架维护的所有短信服务对象</p>
|
||||
* <p>key: configId,短信服务对象的唯一标识</p>
|
||||
* <p>value: 短信服务对象</p>
|
||||
*/
|
||||
private final static Map<String, SmsBlend> blends = new ConcurrentHashMap<>();
|
||||
|
||||
private SmsFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* createSmsBlend
|
||||
* <p>获取各个厂商的实现类
|
||||
* <p>创建各个厂商的实现类
|
||||
*
|
||||
* @param supplierType 厂商枚举
|
||||
* @author :Wind
|
||||
*/
|
||||
public static SmsBlend createSmsBlend(SupplierType supplierType) {
|
||||
BaseProviderFactory providerFactory = supplierType.getProviderFactory();
|
||||
return providerFactory.createSms(providerFactory.getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* createSmsBlend
|
||||
* <p>获取各个厂商的多例实现对象
|
||||
* @param supplierType 厂商枚举
|
||||
* @param config 短信配置
|
||||
* @author :Wind
|
||||
*/
|
||||
public static SmsBlend createSmsBlend(SupplierType supplierType,SupplierConfig config){
|
||||
BaseProviderFactory providerFactory = supplierType.getProviderFactory();
|
||||
return providerFactory.createMultitonSms(config);
|
||||
*/
|
||||
public static void createSmsBlend(SupplierConfig config) {
|
||||
SmsBlend smsBlend = create(config);
|
||||
register(smsBlend);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* createSmsBlend
|
||||
* <p>通过配置读取接口创建某个短信实例
|
||||
* <p>该方法创建的短信实例将会交给框架进行托管,后续可以通过getSmsBlend获取
|
||||
* <p>该方法会直接调用接口实现
|
||||
*
|
||||
* @param smsReadConfig 读取额外配置接口
|
||||
* @param configId 配置ID
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void createSmsBlend(SmsReadConfig smsReadConfig, String configId) {
|
||||
BaseConfig supplierConfig = smsReadConfig.getSupplierConfig(configId);
|
||||
SmsBlend smsBlend = create(supplierConfig);
|
||||
register(smsBlend);
|
||||
}
|
||||
|
||||
/**
|
||||
* createSmsBlend
|
||||
* <p>获取负载均衡器中的短信实例
|
||||
* createSmsBlend
|
||||
* <p>通过配置读取接口创建全部短信实例
|
||||
* <p>该方法创建的短信实例将会交给框架进行托管,后续可以通过getSmsBlend获取
|
||||
* <p>该方法会直接调用接口实现
|
||||
*
|
||||
* @param smsReadConfig 读取额外配置接口
|
||||
* @author :Wind
|
||||
*/
|
||||
public static SmsBlend createSmsBlend(){
|
||||
*/
|
||||
public static void createSmsBlend(SmsReadConfig smsReadConfig) {
|
||||
List<BaseConfig> supplierConfigList = smsReadConfig.getSupplierConfigList();
|
||||
supplierConfigList.forEach(supplierConfig -> {
|
||||
SmsBlend smsBlend = create(supplierConfig);
|
||||
register(smsBlend);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* createRestrictedSmsBlend
|
||||
* <p> 创建一个指定厂商开启短信拦截后的实例,拦截的参数取决于配置参数
|
||||
*
|
||||
* @param config 短信配置
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void createRestrictedSmsBlend(SupplierConfig config) {
|
||||
SmsBlend smsBlend = create(config);
|
||||
smsBlend = renderWithRestricted(smsBlend);
|
||||
register(smsBlend);
|
||||
}
|
||||
|
||||
/**
|
||||
* createRestrictedSmsBlend
|
||||
* <p>通过配置读取接口创建某个开启短信拦截后的短信实例
|
||||
* <p>该方法创建的短信实例将会交给框架进行托管,后续可以通过getSmsBlend获取
|
||||
* <p>该方法会直接调用接口实现
|
||||
*
|
||||
* @param smsReadConfig 读取额外配置接口
|
||||
* @param configId 配置ID
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void createRestrictedSmsBlend(SmsReadConfig smsReadConfig, String configId) {
|
||||
BaseConfig supplierConfig = smsReadConfig.getSupplierConfig(configId);
|
||||
SmsBlend smsBlend = create(supplierConfig);
|
||||
smsBlend = renderWithRestricted(smsBlend);
|
||||
register(smsBlend);
|
||||
}
|
||||
|
||||
/**
|
||||
* createRestrictedSmsBlend
|
||||
* <p>通过配置读取接口创建全部开启短信拦截后的短信实例
|
||||
* <p>该方法创建的短信实例将会交给框架进行托管,后续可以通过getSmsBlend获取
|
||||
* <p>该方法会直接调用接口实现
|
||||
*
|
||||
* @param smsReadConfig 读取额外配置接口
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void createRestrictedSmsBlend(SmsReadConfig smsReadConfig) {
|
||||
List<BaseConfig> supplierConfigList = smsReadConfig.getSupplierConfigList();
|
||||
supplierConfigList.forEach(supplierConfig -> {
|
||||
SmsBlend smsBlend = create(supplierConfig);
|
||||
smsBlend = renderWithRestricted(smsBlend);
|
||||
register(smsBlend);
|
||||
});
|
||||
}
|
||||
|
||||
private static SmsBlend create(SupplierConfig config) {
|
||||
BaseProviderFactory factory = ProviderFactoryHolder.requireForSupplier(config.getSupplier());
|
||||
if (factory == null) {
|
||||
throw new SmsBlendException("不支持当前供应商配置");
|
||||
}
|
||||
return factory.createSms(config);
|
||||
}
|
||||
|
||||
/**
|
||||
* renderWithRestricted
|
||||
* <p> 构建smsBlend对象的代理对象
|
||||
*
|
||||
* @author :Wind
|
||||
*/
|
||||
private static SmsBlend renderWithRestricted(SmsBlend sms) {
|
||||
SmsInvocationHandler smsInvocationHandler = SmsInvocationHandler.newSmsInvocationHandler(sms);
|
||||
return (SmsBlend) Proxy.newProxyInstance(sms.getClass().getClassLoader(), new Class[]{SmsBlend.class}, smsInvocationHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过负载均衡服务获取短信服务对象
|
||||
*
|
||||
* @return 返回短信服务列表
|
||||
*/
|
||||
public static SmsBlend getSmsBlend() {
|
||||
return SmsLoad.getBeanLoad().getLoadServer();
|
||||
}
|
||||
|
||||
/**
|
||||
* refresh
|
||||
* <p>刷新配置,用于切换配置后的刷新,防止因厂商sdk内部的保存导致配置更新不及时
|
||||
* 此方法会造成一定的性能损失,不建议经常性调用
|
||||
* 通过configId获取短信服务对象
|
||||
*
|
||||
* @author :Wind
|
||||
* @param configId 唯一标识
|
||||
* @return 返回短信服务对象。如果未找到则返回null
|
||||
*/
|
||||
public static void refresh() {
|
||||
for(SupplierType type : SupplierType.values()) {
|
||||
refresh(type);
|
||||
public static SmsBlend getSmsBlend(String configId) {
|
||||
return blends.get(configId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过供应商标识获取单个短信服务对象
|
||||
* <p>当供应商有多个短信服务对象时无法保证获取顺序</p>
|
||||
*
|
||||
* @param supplier 供应商标识
|
||||
* @return 返回短信服务对象。如果未找到则返回null
|
||||
*/
|
||||
public static SmsBlend getBySupplier(String supplier) {
|
||||
if (StrUtil.isEmpty(supplier)) {
|
||||
throw new SmsBlendException("供应商标识不能为空");
|
||||
}
|
||||
return blends.values().stream().filter(smsBlend -> supplier.equals(smsBlend.getSupplier())).findFirst().orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* refresh
|
||||
* <p>根据厂商类型枚举刷新对应厂商的配置,此方法不会刷新全部厂商的配置对象,只会重构所选厂商的对象,性能损失相对较小
|
||||
* 通过供应商标识获取短信服务对象列表
|
||||
*
|
||||
* @param supplierType 厂商类型枚举
|
||||
* @author :Wind
|
||||
* @param supplier 供应商标识
|
||||
* @return 返回短信服务对象列表。如果未找到则返回空列表
|
||||
*/
|
||||
public static void refresh(SupplierType supplierType) {
|
||||
BaseProviderFactory providerFactory = supplierType.getProviderFactory();
|
||||
providerFactory.refresh(providerFactory.getConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
* refreshSqlConfig
|
||||
* <p>重新读取sql配置
|
||||
*
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void refreshSqlConfig() {
|
||||
SupplierSqlConfig.refreshSqlConfig();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* getRestrictedSmsBlend
|
||||
* <p>获取某个厂商的带有短信拦截的实现
|
||||
*
|
||||
* @param supplierType 厂商枚举
|
||||
* @author :Wind
|
||||
*/
|
||||
public static SmsBlend getRestrictedSmsBlend(SupplierType supplierType) {
|
||||
SmsBlend smsBlend = beans.get(supplierType);
|
||||
if (Objects.isNull(smsBlend)) {
|
||||
smsBlend = getSmsBlend(supplierType);
|
||||
beans.put(supplierType, smsBlend);
|
||||
public static List<SmsBlend> getListBySupplier(String supplier) {
|
||||
List<SmsBlend> list = null;
|
||||
if (StrUtil.isEmpty(supplier)) {
|
||||
throw new SmsBlendException("供应商标识不能为空");
|
||||
}
|
||||
return smsBlend;
|
||||
list = blends.values().stream().filter(smsBlend -> supplier.equals(smsBlend.getSupplier())).collect(Collectors.toList());
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* refreshRestrictedSmsBlend
|
||||
* <p>刷新带有短信拦截的对象实现
|
||||
* @param supplierType 厂商枚举
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void refreshRestrictedSmsBlend(SupplierType supplierType) {
|
||||
refresh(supplierType);
|
||||
beans.put(supplierType,getSmsBlend(supplierType));
|
||||
* 获取全部短信服务对象
|
||||
*
|
||||
* @return 短信服务对象列表
|
||||
*/
|
||||
public static List<SmsBlend> getAll() {
|
||||
return new ArrayList<>(blends.values());
|
||||
}
|
||||
|
||||
private static SmsBlend getSmsBlend(SupplierType supplierType) {
|
||||
SmsBlend sms = createSmsBlend(supplierType);
|
||||
SmsInvocationHandler smsInvocationHandler = SmsInvocationHandler.newSmsInvocationHandler(
|
||||
sms,
|
||||
BeanFactory.getSmsConfig()
|
||||
);
|
||||
return (SmsBlend) Proxy.newProxyInstance(
|
||||
sms.getClass().getClassLoader(),
|
||||
new Class[]{SmsBlend.class},
|
||||
smsInvocationHandler
|
||||
);
|
||||
/**
|
||||
* 注册短信服务对象
|
||||
*
|
||||
* @param smsBlend 短信服务对象
|
||||
*/
|
||||
public static void register(SmsBlend smsBlend) {
|
||||
if (smsBlend == null) {
|
||||
throw new SmsBlendException("短信服务对象不能为空");
|
||||
}
|
||||
blends.put(smsBlend.getConfigId(), smsBlend);
|
||||
SmsLoad.starConfig(smsBlend, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册短信服务对象
|
||||
*
|
||||
* @param smsBlend 短信服务对象
|
||||
*/
|
||||
public static void register(SmsBlend smsBlend, Integer weight) {
|
||||
if (smsBlend == null) {
|
||||
throw new SmsBlendException("短信服务对象不能为空");
|
||||
}
|
||||
blends.put(smsBlend.getConfigId(), smsBlend);
|
||||
SmsLoad.starConfig(smsBlend, weight);
|
||||
}
|
||||
|
||||
/**
|
||||
* 以configId为标识,当短信服务对象不存在时,进行注册
|
||||
*
|
||||
* @param smsBlend 短信服务对象
|
||||
* @return 是否注册成功
|
||||
* <p>当对象不存在时,进行注册并返回true</p>
|
||||
* <p>当对象已存在时,返回false</p>
|
||||
*/
|
||||
public static boolean registerIfAbsent(SmsBlend smsBlend) {
|
||||
if (smsBlend == null) {
|
||||
throw new SmsBlendException("短信服务对象不能为空");
|
||||
}
|
||||
String configId = smsBlend.getConfigId();
|
||||
if (blends.containsKey(configId)) {
|
||||
return false;
|
||||
}
|
||||
blends.put(configId, smsBlend);
|
||||
SmsLoad.starConfig(smsBlend, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* registerIfAbsent
|
||||
* <p> 以configId为标识,当短信服务对象不存在时,进行注册。并添加至系统的负载均衡器
|
||||
*
|
||||
* @param smsBlend 短信服务对象
|
||||
* @param weight 权重
|
||||
* @return 是否注册成功
|
||||
* <p>当对象不存在时,进行注册并返回true</p>
|
||||
* <p>当对象已存在时,返回false</p>
|
||||
* @author :Wind
|
||||
*/
|
||||
public static boolean registerIfAbsent(SmsBlend smsBlend, Integer weight) {
|
||||
if (smsBlend == null) {
|
||||
throw new SmsBlendException("短信服务对象不能为空");
|
||||
}
|
||||
String configId = smsBlend.getConfigId();
|
||||
if (blends.containsKey(configId)) {
|
||||
return false;
|
||||
}
|
||||
blends.put(configId, smsBlend);
|
||||
SmsLoad.starConfig(smsBlend, weight);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注销短信服务对象
|
||||
* <p>与此同时会注销掉负载均衡器中已经存在的对象</p>
|
||||
*
|
||||
* @param configId 标识
|
||||
* @return 是否注销成功
|
||||
* <p>当configId存在时,进行注销并返回true</p>
|
||||
* <p>当configId不存在时,返回false</p>
|
||||
*/
|
||||
public static boolean unregister(String configId) {
|
||||
SmsBlend blend = blends.remove(configId);
|
||||
SmsLoad.getBeanLoad().removeLoadServer(blend);
|
||||
return blend != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -3,11 +3,8 @@ package org.dromara.sms4j.core.load;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.enumerate.SupplierType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -22,9 +19,6 @@ public class SmsLoad {
|
||||
// 服务器列表,每个服务器有一个权重和当前权重
|
||||
private final List<LoadServer> LoadServers = new ArrayList<>();
|
||||
|
||||
// 实例列表
|
||||
private static final Map<Object, SmsBlend> smsBlendMap = new HashMap<>();
|
||||
|
||||
private static final SmsLoad smsLoad = new SmsLoad();
|
||||
|
||||
private SmsLoad() {
|
||||
@ -96,18 +90,20 @@ public class SmsLoad {
|
||||
* starConfig
|
||||
* <p> 创建smsBlend并加入到负载均衡器
|
||||
*
|
||||
* @param smsBlend 短信服务
|
||||
* @param supplierConfig 厂商配置
|
||||
* @param supplierType 厂商枚举
|
||||
* @author :Wind
|
||||
*/
|
||||
public static void starConfig(SupplierConfig supplierConfig, SupplierType supplierType) {
|
||||
BaseProviderFactory providerFactory = supplierType.getProviderFactory();
|
||||
SmsBlend smsBlend = providerFactory.createMultitonSms(supplierConfig);
|
||||
public static void starConfig(SmsBlend smsBlend, SupplierConfig supplierConfig) {
|
||||
Map<String, Object> supplierConfigMap = BeanUtil.beanToMap(supplierConfig);
|
||||
Object weight = supplierConfigMap.getOrDefault("weight", 1);
|
||||
smsLoad.addLoadServer(smsBlend, Integer.parseInt(weight.toString()));
|
||||
}
|
||||
|
||||
public static void starConfig(SmsBlend smsBlend,Integer weight) {
|
||||
smsLoad.addLoadServer(smsBlend,weight);
|
||||
}
|
||||
|
||||
public static SmsLoad getBeanLoad() {
|
||||
return smsLoad;
|
||||
}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
package org.dromara.sms4j.core.proxy;
|
||||
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.dao.SmsDao;
|
||||
import org.dromara.sms4j.api.proxy.RestrictedProcess;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||
import org.dromara.sms4j.provider.factory.BeanFactory;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
public class RestrictedProcessDefaultImpl implements RestrictedProcess {
|
||||
static Long minTimer = 60 * 1000L;
|
||||
static Long accTimer = 24 * 60 * 60 * 1000L;
|
||||
|
||||
/**
|
||||
* 缓存实例
|
||||
*/
|
||||
@Setter
|
||||
private SmsDao smsDao;
|
||||
|
||||
public SmsBlendException process(String phone) throws Exception {
|
||||
if (Objects.isNull(smsDao)) {
|
||||
throw new SmsBlendException("The dao tool could not be found");
|
||||
}
|
||||
SmsConfig config = BeanFactory.getSmsConfig();
|
||||
Integer accountMax = config.getAccountMax(); // 每日最大发送量
|
||||
Integer minuteMax = config.getMinuteMax(); // 每分钟最大发送量
|
||||
if (SmsUtil.isNotEmpty(accountMax)) { // 是否配置了每日限制
|
||||
Integer i = (Integer) smsDao.get(phone + "max");
|
||||
if (SmsUtil.isEmpty(i)) {
|
||||
smsDao.set(phone + "max", 1, accTimer);
|
||||
} else if (i >= accountMax) {
|
||||
log.info("The phone:" + phone + ",number of short messages reached the maximum today");
|
||||
return new SmsBlendException("The phone:" + phone + ",number of short messages reached the maximum today");
|
||||
} else {
|
||||
smsDao.set(phone + "max", i + 1, accTimer);
|
||||
}
|
||||
}
|
||||
if (SmsUtil.isNotEmpty(minuteMax)) { // 是否配置了每分钟最大限制
|
||||
Integer o = (Integer) smsDao.get(phone);
|
||||
if (SmsUtil.isNotEmpty(o)) {
|
||||
if (o < minuteMax) {
|
||||
smsDao.set(phone, o + 1, minTimer);
|
||||
} else {
|
||||
log.info("The phone:" + phone + " Text messages are sent too often!");
|
||||
return new SmsBlendException("The phone:", phone + " Text messages are sent too often!");
|
||||
}
|
||||
} else {
|
||||
smsDao.set(phone, 1, minTimer);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
package org.dromara.sms4j.api.smsProxy;
|
||||
package org.dromara.sms4j.core.proxy;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.comm.config.SmsConfig;
|
||||
import org.dromara.sms4j.api.proxy.RestrictedProcess;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
@ -12,16 +12,14 @@ import java.util.Objects;
|
||||
@Slf4j
|
||||
public class SmsInvocationHandler implements InvocationHandler {
|
||||
private final SmsBlend smsBlend;
|
||||
private final SmsConfig config;
|
||||
private static RestrictedProcess restrictedProcess = new RestrictedProcess();
|
||||
private static RestrictedProcess restrictedProcess = new RestrictedProcessDefaultImpl();
|
||||
|
||||
private SmsInvocationHandler(SmsBlend smsBlend, SmsConfig config) {
|
||||
private SmsInvocationHandler(SmsBlend smsBlend) {
|
||||
this.smsBlend = smsBlend;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public static SmsInvocationHandler newSmsInvocationHandler(SmsBlend smsBlend, SmsConfig config){
|
||||
return new SmsInvocationHandler(smsBlend,config);
|
||||
public static SmsInvocationHandler newSmsInvocationHandler(SmsBlend smsBlend) {
|
||||
return new SmsInvocationHandler(smsBlend);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -30,7 +28,7 @@ public class SmsInvocationHandler implements InvocationHandler {
|
||||
if ("sendMessage".equals(method.getName()) || "massTexting".equals(method.getName())) {
|
||||
//取手机号作为参数
|
||||
String phone = (String) objects[0];
|
||||
SmsBlendException smsBlendException = restrictedProcess.process(config,phone);
|
||||
SmsBlendException smsBlendException = restrictedProcess.process(phone);
|
||||
if (!Objects.isNull(smsBlendException)) {
|
||||
throw smsBlendException;
|
||||
}
|
||||
@ -2,26 +2,40 @@ package org.dromara.sms4j.javase.config;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.io.resource.ClassPathResource;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenConfig;
|
||||
import org.dromara.sms4j.comm.config.SmsConfig;
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaFactory;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.dao.SmsDao;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenFactory;
|
||||
import org.dromara.sms4j.core.factory.SmsFactory;
|
||||
import org.dromara.sms4j.core.proxy.RestrictedProcessDefaultImpl;
|
||||
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
|
||||
import org.dromara.sms4j.ctyun.config.CtyunFactory;
|
||||
import org.dromara.sms4j.emay.config.EmayFactory;
|
||||
import org.dromara.sms4j.huawei.config.HuaweiFactory;
|
||||
import org.dromara.sms4j.jdcloud.config.JdCloudFactory;
|
||||
import org.dromara.sms4j.netease.config.NeteaseFactory;
|
||||
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.core.config.SupplierFactory;
|
||||
import org.dromara.sms4j.emay.config.EmayConfig;
|
||||
import org.dromara.sms4j.huawei.config.HuaweiConfig;
|
||||
import org.dromara.sms4j.provider.factory.BeanFactory;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.javase.util.YamlUtil;
|
||||
import org.dromara.sms4j.jdcloud.config.JdCloudConfig;
|
||||
import org.dromara.sms4j.netease.config.NeteaseConfig;
|
||||
import org.dromara.sms4j.tencent.config.TencentConfig;
|
||||
import org.dromara.sms4j.unisms.config.UniConfig;
|
||||
import org.dromara.sms4j.yunpian.config.YunpianConfig;
|
||||
import org.dromara.sms4j.zhutong.config.ZhutongConfig;
|
||||
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
import org.dromara.sms4j.tencent.config.TencentFactory;
|
||||
import org.dromara.sms4j.unisms.config.UniFactory;
|
||||
import org.dromara.sms4j.yunpian.config.YunPianFactory;
|
||||
import org.dromara.sms4j.zhutong.config.ZhutongFactory;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 初始化类
|
||||
@ -46,126 +60,15 @@ public class SEInitializer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化阿里配置
|
||||
*
|
||||
* @param alibabaConfig 阿里配置
|
||||
* @return 当前初始化类实例
|
||||
*/
|
||||
public SEInitializer initAlibaba(AlibabaConfig alibabaConfig) {
|
||||
BeanUtil.copyProperties(alibabaConfig, SupplierFactory.getAlibabaConfig());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化容连云配置
|
||||
*
|
||||
* @param cloopenConfig 容连云配置
|
||||
* @return 当前初始化类实例
|
||||
*/
|
||||
public SEInitializer initCloopen(CloopenConfig cloopenConfig) {
|
||||
BeanUtil.copyProperties(cloopenConfig, SupplierFactory.getCloopenConfig());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化亿美软通配置
|
||||
*
|
||||
* @param emayConfig 亿美软通配置
|
||||
* @return 当前初始化类实例
|
||||
*/
|
||||
public SEInitializer initEmay(EmayConfig emayConfig) {
|
||||
BeanUtil.copyProperties(emayConfig, SupplierFactory.getEmayConfig());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化华为配置
|
||||
*
|
||||
* @param huaweiConfig 华为配置
|
||||
* @return 当前初始化类实例
|
||||
*/
|
||||
public SEInitializer initHuawei(HuaweiConfig huaweiConfig) {
|
||||
BeanUtil.copyProperties(huaweiConfig, SupplierFactory.getHuaweiConfig());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化京东配置
|
||||
*
|
||||
* @param jdCloudConfig 京东配置
|
||||
* @return 当前初始化类实例
|
||||
*/
|
||||
public SEInitializer initJdCloud(JdCloudConfig jdCloudConfig) {
|
||||
BeanUtil.copyProperties(jdCloudConfig, SupplierFactory.getJdCloudConfig());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化腾讯配置
|
||||
*
|
||||
* @param tencentConfig 腾讯配置
|
||||
* @return 当前初始化类实例
|
||||
*/
|
||||
public SEInitializer initTencent(TencentConfig tencentConfig) {
|
||||
BeanUtil.copyProperties(tencentConfig, SupplierFactory.getTencentConfig());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化合一配置
|
||||
*
|
||||
* @param uniConfig 合一配置
|
||||
* @return 当前初始化类实例
|
||||
*/
|
||||
public SEInitializer initUniSms(UniConfig uniConfig) {
|
||||
BeanUtil.copyProperties(uniConfig, SupplierFactory.getUniConfig());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化云片配置
|
||||
*
|
||||
* @param yunpianConfig 云片配置
|
||||
* @return 当前初始化类实例
|
||||
*/
|
||||
public SEInitializer initYunpian(YunpianConfig yunpianConfig) {
|
||||
BeanUtil.copyProperties(yunpianConfig, SupplierFactory.getYunpianConfig());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* initializer
|
||||
* <p>初始化网易云短信配置
|
||||
*
|
||||
* @return 当前初始化类实例
|
||||
* @author :Wind
|
||||
*/
|
||||
public SEInitializer initNetase(NeteaseConfig neteaseConfig) {
|
||||
BeanUtil.copyProperties(neteaseConfig, SupplierFactory.getNeteaseConfig());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* initZhuTong
|
||||
* <p>初始化助通短信配置
|
||||
*
|
||||
* @return 当前初始化类实例
|
||||
* @author :Wind
|
||||
*/
|
||||
public SEInitializer initZhuTong(ZhutongConfig zhutongConfig) {
|
||||
BeanUtil.copyProperties(zhutongConfig, SupplierFactory.getZhutongConfig());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认从sms-aggregation.yml文件中读取配置
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public void fromYaml() {
|
||||
public SEInitializer fromYaml() {
|
||||
ClassPathResource yamlResouce = new ClassPathResource("sms4j.yml");
|
||||
this.fromYaml(yamlResouce.readUtf8Str());
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -173,9 +76,10 @@ public class SEInitializer {
|
||||
*
|
||||
* @param yaml yaml配置字符串
|
||||
*/
|
||||
public void fromYaml(String yaml) {
|
||||
public SEInitializer fromYaml(String yaml) {
|
||||
InitConfig config = YamlUtil.toBean(yaml, InitConfig.class);
|
||||
this.initConfig(config);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -183,9 +87,33 @@ public class SEInitializer {
|
||||
*
|
||||
* @param json json配置字符串
|
||||
*/
|
||||
public void fromJson(String json) {
|
||||
public SEInitializer fromJson(String json) {
|
||||
InitConfig config = JSONUtil.toBean(json, InitConfig.class);
|
||||
this.initConfig(config);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册供应商工厂
|
||||
* @param factory
|
||||
*/
|
||||
public SEInitializer registerFactory(BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig> factory) {
|
||||
ProviderFactoryHolder.registerFactory(factory);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册DAO实例
|
||||
* @param smsDao
|
||||
*/
|
||||
public SEInitializer registerSmsDao(SmsDao smsDao) {
|
||||
if(smsDao == null) {
|
||||
throw new SmsBlendException("注册DAO实例失败,实例不能为空");
|
||||
}
|
||||
RestrictedProcessDefaultImpl process = new RestrictedProcessDefaultImpl();
|
||||
process.setSmsDao(smsDao);
|
||||
SmsInvocationHandler.setRestrictedProcess(process);
|
||||
return this;
|
||||
}
|
||||
|
||||
private void initConfig(InitConfig config) {
|
||||
@ -199,49 +127,52 @@ public class SEInitializer {
|
||||
throw new SmsBlendException("初始化配置失败");
|
||||
}
|
||||
|
||||
//注册默认工厂
|
||||
registerDefaultFactory();
|
||||
|
||||
//初始化SmsConfig整体配置文件
|
||||
this.initSmsConfig(smsConfig);
|
||||
AlibabaConfig alibabaConfig = smsConfig.getAlibaba();
|
||||
if (alibabaConfig != null) {
|
||||
this.initAlibaba(alibabaConfig);
|
||||
}
|
||||
CloopenConfig cloopenConfig = smsConfig.getCloopen();
|
||||
if (cloopenConfig != null) {
|
||||
this.initCloopen(cloopenConfig);
|
||||
}
|
||||
EmayConfig emayConfig = smsConfig.getEmay();
|
||||
if (emayConfig != null) {
|
||||
this.initEmay(emayConfig);
|
||||
}
|
||||
HuaweiConfig huaweiConfig = smsConfig.getHuawei();
|
||||
if (huaweiConfig != null) {
|
||||
this.initHuawei(huaweiConfig);
|
||||
}
|
||||
JdCloudConfig jdCloudConfig = smsConfig.getJdCloud();
|
||||
if (jdCloudConfig != null) {
|
||||
this.initJdCloud(jdCloudConfig);
|
||||
}
|
||||
TencentConfig tencentConfig = smsConfig.getTencent();
|
||||
if (tencentConfig != null) {
|
||||
this.initTencent(tencentConfig);
|
||||
}
|
||||
UniConfig uniConfig = smsConfig.getUni();
|
||||
if (uniConfig != null) {
|
||||
this.initUniSms(uniConfig);
|
||||
}
|
||||
YunpianConfig yunpianConfig = smsConfig.getYunpian();
|
||||
if (yunpianConfig != null) {
|
||||
this.initYunpian(yunpianConfig);
|
||||
}
|
||||
NeteaseConfig neteaseConfig = smsConfig.getNeteaseConfig();
|
||||
if (neteaseConfig != null){
|
||||
this.initNetase(neteaseConfig);
|
||||
}
|
||||
ZhutongConfig zhutongConfig = smsConfig.getZhutongConfig();
|
||||
if (zhutongConfig != null){
|
||||
this.initZhuTong(zhutongConfig);
|
||||
// 解析供应商配置
|
||||
Map<String, Map<String, Object>> blends = smsConfig.getBlends();
|
||||
for(String configId : blends.keySet()) {
|
||||
Map<String, Object> configMap = blends.get(configId);
|
||||
Object supplierObj = configMap.get(Constant.SUPPLIER_KEY);
|
||||
String supplier = supplierObj == null ? "" : String.valueOf(supplierObj);
|
||||
supplier = StrUtil.isEmpty(supplier) ? configId : supplier;
|
||||
BaseProviderFactory<SmsBlend, SupplierConfig> providerFactory = (BaseProviderFactory<SmsBlend, SupplierConfig>) ProviderFactoryHolder.requireForSupplier(supplier);
|
||||
if(providerFactory == null) {
|
||||
log.warn("创建\"{}\"的短信服务失败,未找到供应商为\"{}\"的服务", configId, supplier);
|
||||
continue;
|
||||
}
|
||||
configMap.put("config-id", configId);
|
||||
SmsUtil.replaceKeysSeperator(configMap, "-", "_");
|
||||
JSONObject configJson = new JSONObject(configMap);
|
||||
SupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass());
|
||||
if(Boolean.TRUE.equals(smsConfig.getRestricted())) {
|
||||
SmsFactory.createRestrictedSmsBlend(supplierConfig);
|
||||
} else {
|
||||
SmsFactory.createSmsBlend(supplierConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册默认工厂实例
|
||||
*/
|
||||
private void registerDefaultFactory() {
|
||||
ProviderFactoryHolder.registerFactory(AlibabaFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(CloopenFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(CtyunFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(EmayFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(HuaweiFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(JdCloudFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(NeteaseFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(TencentFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(UniFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(YunPianFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(ZhutongFactory.instance());
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化配置bean
|
||||
*/
|
||||
@ -259,16 +190,7 @@ public class SEInitializer {
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public static class InitSmsConfig extends SmsConfig {
|
||||
private AlibabaConfig alibaba;
|
||||
private CloopenConfig cloopen;
|
||||
private EmayConfig emay;
|
||||
private HuaweiConfig huawei;
|
||||
private JdCloudConfig jdCloud;
|
||||
private TencentConfig tencent;
|
||||
private UniConfig uni;
|
||||
private YunpianConfig yunpian;
|
||||
private NeteaseConfig neteaseConfig;
|
||||
private ZhutongConfig zhutongConfig;
|
||||
private Map<String, Map<String, Object>> blends;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
<dependency>
|
||||
<groupId>com.jdcloud.sdk</groupId>
|
||||
<artifactId>sms</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
||||
@ -6,18 +6,16 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.aliyun.service.AlibabaSmsImpl;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
|
||||
/**
|
||||
* @author Wind
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
public class AlibabaConfig extends BaseConfig implements SupplierConfig {
|
||||
@Data
|
||||
public class AlibabaConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 模板变量名称
|
||||
@ -27,24 +25,31 @@ public class AlibabaConfig extends BaseConfig implements SupplierConfig {
|
||||
/**
|
||||
* 请求地址
|
||||
*/
|
||||
@Builder.Default
|
||||
private String requestUrl = "dysmsapi.aliyuncs.com";
|
||||
|
||||
/**
|
||||
* 接口名称
|
||||
*/
|
||||
@Builder.Default
|
||||
private String action = "SendSms";
|
||||
|
||||
/**
|
||||
* 接口版本号
|
||||
*/
|
||||
@Builder.Default
|
||||
private String version = "2017-05-25";
|
||||
|
||||
/**
|
||||
* 地域信息默认为 cn-hangzhou
|
||||
*/
|
||||
@Builder.Default
|
||||
private String regionId = "cn-hangzhou";
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return AlibabaSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
package org.dromara.sms4j.aliyun.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.aliyun.service.AlibabaSmsImpl;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
|
||||
/**
|
||||
* AlibabaSmsConfig
|
||||
@ -12,20 +13,11 @@ import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
* @author :Wind
|
||||
* 2023/4/8 14:54
|
||||
**/
|
||||
@Slf4j
|
||||
public class AlibabaFactory implements BaseProviderFactory<AlibabaSmsImpl, AlibabaConfig> {
|
||||
|
||||
private static AlibabaSmsImpl alibabaSms;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class AlibabaFactory extends AbstractProviderFactory<AlibabaSmsImpl, AlibabaConfig> {
|
||||
|
||||
private static final AlibabaFactory INSTANCE = new AlibabaFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static AlibabaConfig config = AlibabaConfig.builder().build();
|
||||
}
|
||||
|
||||
private AlibabaFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -41,45 +33,16 @@ public class AlibabaFactory implements BaseProviderFactory<AlibabaSmsImpl, Aliba
|
||||
*/
|
||||
@Override
|
||||
public AlibabaSmsImpl createSms(AlibabaConfig alibabaConfig) {
|
||||
if (alibabaSms == null) {
|
||||
alibabaSms = createMultitonSms(alibabaConfig);
|
||||
}
|
||||
return alibabaSms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlibabaSmsImpl createMultitonSms(AlibabaConfig alibabaConfig) {
|
||||
return new AlibabaSmsImpl(alibabaConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
return new AlibabaSmsImpl(alibabaConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新短信实现对象
|
||||
* @param alibabaConfig 短信配置对象
|
||||
* @return 刷新后的短信实现对象
|
||||
* 获取供应商
|
||||
* @return 供应商
|
||||
*/
|
||||
@Override
|
||||
public AlibabaSmsImpl refresh(AlibabaConfig alibabaConfig) {
|
||||
//重新构造一个实现对象
|
||||
alibabaSms = new AlibabaSmsImpl(alibabaConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
return alibabaSms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Override
|
||||
public AlibabaConfig getConfig() {
|
||||
return ConfigHolder.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(AlibabaConfig config) {
|
||||
ConfigHolder.config = config;
|
||||
public String getSupplier() {
|
||||
return AlibabaSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,21 +1,20 @@
|
||||
package org.dromara.sms4j.aliyun.service;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
|
||||
import org.dromara.sms4j.aliyun.utils.AliyunUtils;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@ -26,9 +25,10 @@ import java.util.concurrent.Executor;
|
||||
* 2023/3/26 17:16
|
||||
**/
|
||||
@Slf4j
|
||||
public class AlibabaSmsImpl extends AbstractSmsBlend {
|
||||
public class AlibabaSmsImpl extends AbstractSmsBlend<AlibabaConfig> {
|
||||
|
||||
private final AlibabaConfig config;
|
||||
public static final String SUPPLIER = "alibaba";
|
||||
private int retry = 0;
|
||||
|
||||
/**
|
||||
* AlibabaSmsImpl
|
||||
@ -37,35 +37,43 @@ public class AlibabaSmsImpl extends AbstractSmsBlend {
|
||||
* @author :Wind
|
||||
*/
|
||||
public AlibabaSmsImpl(AlibabaConfig config, Executor pool, DelayedTime delayedTime) {
|
||||
super(pool, delayedTime);
|
||||
this.config = config;
|
||||
super(config, pool, delayedTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* AlibabaSmsImpl
|
||||
* <p>构造器,用于构造短信实现模块
|
||||
*/
|
||||
public AlibabaSmsImpl(AlibabaConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
map.put(config.getTemplateName(), message);
|
||||
return sendMessage(phone, config.getTemplateId(), map);
|
||||
map.put(getConfig().getTemplateName(), message);
|
||||
return sendMessage(phone, getConfig().getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
String messageStr = JSONUtil.toJsonStr(messages);
|
||||
return getSmsResponse(phone, messageStr, templateId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
map.put(config.getTemplateName(), message);
|
||||
return massTexting(phones, config.getTemplateId(), map);
|
||||
map.put(getConfig().getTemplateName(), message);
|
||||
return massTexting(phones, getConfig().getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
String messageStr = JSONUtil.toJsonStr(messages);
|
||||
return getSmsResponse(SmsUtil.arrayToString(phones), messageStr, templateId);
|
||||
@ -75,27 +83,39 @@ public class AlibabaSmsImpl extends AbstractSmsBlend {
|
||||
String requestUrl;
|
||||
String paramStr;
|
||||
try {
|
||||
requestUrl = AliyunUtils.generateSendSmsRequestUrl(this.config, message, phone, templateId);
|
||||
paramStr = AliyunUtils.generateParamBody(config, phone, message, templateId);
|
||||
requestUrl = AliyunUtils.generateSendSmsRequestUrl(getConfig(), message, phone, templateId);
|
||||
paramStr = AliyunUtils.generateParamBody(getConfig(), phone, message, templateId);
|
||||
} catch (Exception e) {
|
||||
log.error("aliyun send message error", e);
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
log.debug("requestUrl {}", requestUrl);
|
||||
try(HttpResponse response = HttpRequest.post(requestUrl)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.body(paramStr)
|
||||
.execute()){
|
||||
JSONObject body = JSONUtil.parseObj(response.body());
|
||||
return this.getResponse(body);
|
||||
try {
|
||||
Map<String, String> headers = new LinkedHashMap<>(1);
|
||||
headers.put("Content-Type", Constant.FROM_URLENCODED);
|
||||
SmsResponse smsResponse = getResponse(http.postJson(requestUrl, headers, paramStr));
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(phone, message, templateId);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(phone, message, templateId);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(String phone, String message, String templateId) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return getSmsResponse(phone, message, templateId);
|
||||
}
|
||||
|
||||
private SmsResponse getResponse(JSONObject resJson) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setSuccess("OK".equals(resJson.getStr("Code")));
|
||||
smsResponse.setData(resJson);
|
||||
smsResponse.setConfigId(this.config.getConfigId());
|
||||
smsResponse.setConfigId(getConfigId());
|
||||
return smsResponse;
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
import org.dromara.sms4j.cloopen.service.CloopenSmsImpl;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
|
||||
/**
|
||||
* 容联云短信配置属性
|
||||
@ -16,11 +16,8 @@ import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
* @since 2023/4/10 22:10
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
public class CloopenConfig extends BaseConfig implements SupplierConfig {
|
||||
public class CloopenConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 应用 ID
|
||||
@ -30,7 +27,6 @@ public class CloopenConfig extends BaseConfig implements SupplierConfig {
|
||||
/**
|
||||
* REST API Base URL
|
||||
*/
|
||||
@Builder.Default
|
||||
private String baseUrl = "https://app.cloopen.com:8883/2013-12-26";
|
||||
|
||||
/**
|
||||
@ -50,4 +46,14 @@ public class CloopenConfig extends BaseConfig implements SupplierConfig {
|
||||
*/
|
||||
@Deprecated
|
||||
private String serverPort;
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return CloopenSmsImpl.SUPPLIER;
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,8 +3,8 @@ package org.dromara.sms4j.cloopen.config;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.cloopen.service.CloopenSmsImpl;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
|
||||
/**
|
||||
* 容联云短信配置
|
||||
@ -13,16 +13,10 @@ import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
* @since 2023/4/10 22:10
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class CloopenFactory implements BaseProviderFactory<CloopenSmsImpl, CloopenConfig> {
|
||||
|
||||
private static CloopenSmsImpl cloopenSms;
|
||||
public class CloopenFactory extends AbstractProviderFactory<CloopenSmsImpl, CloopenConfig> {
|
||||
|
||||
private static final CloopenFactory INSTANCE = new CloopenFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static CloopenConfig config = CloopenConfig.builder().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -38,45 +32,16 @@ public class CloopenFactory implements BaseProviderFactory<CloopenSmsImpl, Cloop
|
||||
*/
|
||||
@Override
|
||||
public CloopenSmsImpl createSms(CloopenConfig cloopenConfig) {
|
||||
if (cloopenSms == null) {
|
||||
cloopenSms = createMultitonSms(cloopenConfig);
|
||||
}
|
||||
return cloopenSms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CloopenSmsImpl createMultitonSms(CloopenConfig cloopenConfig) {
|
||||
return new CloopenSmsImpl(cloopenConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
return new CloopenSmsImpl(cloopenConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新容连云短信实现对象
|
||||
* @param cloopenConfig 短信配置对象
|
||||
* @return 刷新后的短信实现对象
|
||||
* 获取供应商
|
||||
* @return 供应商
|
||||
*/
|
||||
@Override
|
||||
public CloopenSmsImpl refresh(CloopenConfig cloopenConfig) {
|
||||
//重新构造一个实现对象
|
||||
cloopenSms = new CloopenSmsImpl(cloopenConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
return cloopenSms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Override
|
||||
public CloopenConfig getConfig() {
|
||||
return ConfigHolder.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(CloopenConfig config) {
|
||||
ConfigHolder.config = config;
|
||||
public String getSupplier() {
|
||||
return CloopenSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
package org.dromara.sms4j.cloopen.service;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenConfig;
|
||||
import org.dromara.sms4j.cloopen.util.CloopenHelper;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -23,42 +21,46 @@ import java.util.concurrent.Executor;
|
||||
* @since 2023/4/10 22:10
|
||||
*/
|
||||
@Slf4j
|
||||
public class CloopenSmsImpl extends AbstractSmsBlend {
|
||||
public class CloopenSmsImpl extends AbstractSmsBlend<CloopenConfig> {
|
||||
|
||||
private final CloopenConfig config;
|
||||
public static final String SUPPLIER = "cloopen";
|
||||
|
||||
public CloopenSmsImpl(CloopenConfig config, Executor pool, DelayedTime delayed) {
|
||||
super(pool,delayed);
|
||||
this.config = config;
|
||||
super(config, pool, delayed);
|
||||
}
|
||||
|
||||
public CloopenSmsImpl(CloopenConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
return massTexting(Collections.singletonList(phone), message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
return massTexting(Collections.singletonList(phone), templateId, messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
map.put(IdUtil.fastSimpleUUID(), message);
|
||||
return massTexting(phones, config.getTemplateId(), map);
|
||||
return massTexting(phones, getConfig().getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
CloopenHelper helper = new CloopenHelper(config);
|
||||
Map<String, Object> paramMap = MapUtil.newHashMap(4);
|
||||
CloopenHelper helper = new CloopenHelper(getConfig(), http);
|
||||
Map<String, Object> paramMap = new LinkedHashMap<>(4);
|
||||
paramMap.put("to", String.join(",", phones));
|
||||
paramMap.put("appId", config.getAppId());
|
||||
paramMap.put("appId", getConfig().getAppId());
|
||||
paramMap.put("templateId", templateId);
|
||||
paramMap.put("datas", messages.keySet().stream().map(messages::get).toArray(String[]::new));
|
||||
return helper.smsResponse(paramMap);
|
||||
|
||||
@ -4,14 +4,16 @@ import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.date.DatePattern;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenConfig;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsHttpUtil;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -20,33 +22,48 @@ import java.util.Map;
|
||||
* @author Charles7c
|
||||
* @since 2023/4/17 20:57
|
||||
*/
|
||||
@Slf4j
|
||||
public class CloopenHelper {
|
||||
|
||||
private final CloopenConfig config;
|
||||
private final SmsHttpUtil http;
|
||||
private int retry = 0;
|
||||
|
||||
public CloopenHelper(CloopenConfig config) {
|
||||
public CloopenHelper(CloopenConfig config, SmsHttpUtil http) {
|
||||
this.config = config;
|
||||
this.http = http;
|
||||
}
|
||||
|
||||
public SmsResponse smsResponse(Map<String, Object> paramMap){
|
||||
String timestamp = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
|
||||
try {
|
||||
String timestamp = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
|
||||
|
||||
String url = String.format("%s/Accounts/%s/SMS/TemplateSMS?sig=%s",
|
||||
config.getBaseUrl(),
|
||||
config.getAccessKeyId(),
|
||||
this.generateSign(config.getAccessKeyId(), config.getAccessKeySecret(), timestamp));
|
||||
|
||||
try(HttpResponse response = HttpRequest.post(url)
|
||||
.header("Accept", "application/json")
|
||||
.header("Content-Type", "application/json;charset=utf-8")
|
||||
.header("Authorization", this.generateAuthorization(config.getAccessKeyId(), timestamp))
|
||||
.body(JSONUtil.toJsonStr(paramMap))
|
||||
.execute()){
|
||||
JSONObject body = JSONUtil.parseObj(response.body());
|
||||
return this.getResponse(body);
|
||||
String url = String.format("%s/Accounts/%s/SMS/TemplateSMS?sig=%s",
|
||||
config.getBaseUrl(),
|
||||
config.getAccessKeyId(),
|
||||
this.generateSign(config.getAccessKeyId(), config.getAccessKeySecret(), timestamp));
|
||||
Map<String, String> headers = new LinkedHashMap<>(3);
|
||||
headers.put("Accept", Constant.ACCEPT);
|
||||
headers.put("Content-Type", Constant.APPLICATION_JSON_UTF8);
|
||||
headers.put("Authorization", this.generateAuthorization(config.getAccessKeyId(), timestamp));
|
||||
SmsResponse smsResponse = getResponse(http.postJson(url, headers, paramMap));
|
||||
if(smsResponse.isSuccess() || retry == config.getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(paramMap);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(paramMap);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(Map<String, Object> paramMap) {
|
||||
http.safeSleep(config.getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return smsResponse(paramMap);
|
||||
}
|
||||
|
||||
private SmsResponse getResponse(JSONObject resJson) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setSuccess("000000".equals(resJson.getStr("statusCode")));
|
||||
|
||||
@ -6,8 +6,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
import org.dromara.sms4j.ctyun.service.CtyunSmsImpl;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
|
||||
/**
|
||||
* <p>类名: CtyunConfig
|
||||
@ -17,11 +17,8 @@ import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
* 2023/5/12 15:06
|
||||
**/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
public class CtyunConfig extends BaseConfig implements SupplierConfig {
|
||||
public class CtyunConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 模板变量名称
|
||||
@ -31,12 +28,20 @@ public class CtyunConfig extends BaseConfig implements SupplierConfig {
|
||||
/**
|
||||
* 请求地址
|
||||
*/
|
||||
@Builder.Default
|
||||
private String requestUrl = "https://sms-global.ctapi.ctyun.cn/sms/api/v1";
|
||||
|
||||
/**
|
||||
* 接口名称
|
||||
*/
|
||||
@Builder.Default
|
||||
private String action = "SendSms";
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return CtyunSmsImpl.SUPPLIER;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
package org.dromara.sms4j.ctyun.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.ctyun.service.CtyunSmsImpl;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
|
||||
/**
|
||||
* <p>类名: CtyunSmsConfig
|
||||
@ -12,20 +13,11 @@ import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
* @author :bleachhtred
|
||||
* 2023/5/12 15:06
|
||||
**/
|
||||
@Slf4j
|
||||
public class CtyunFactory implements BaseProviderFactory<CtyunSmsImpl, CtyunConfig> {
|
||||
|
||||
private static CtyunSmsImpl ctyunSms;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class CtyunFactory extends AbstractProviderFactory<CtyunSmsImpl, CtyunConfig> {
|
||||
|
||||
private static final CtyunFactory INSTANCE = new CtyunFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static CtyunConfig config = CtyunConfig.builder().build();
|
||||
}
|
||||
|
||||
private CtyunFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -42,45 +34,16 @@ public class CtyunFactory implements BaseProviderFactory<CtyunSmsImpl, CtyunConf
|
||||
*/
|
||||
@Override
|
||||
public CtyunSmsImpl createSms(CtyunConfig ctyunConfig) {
|
||||
if (ctyunSms == null) {
|
||||
ctyunSms = createMultitonSms(ctyunConfig);
|
||||
}
|
||||
return ctyunSms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CtyunSmsImpl createMultitonSms(CtyunConfig ctyunConfig) {
|
||||
return new CtyunSmsImpl(ctyunConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
return new CtyunSmsImpl(ctyunConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* refresh
|
||||
* <p> 刷新对象
|
||||
*
|
||||
* @author :bleachhtred
|
||||
* 获取供应商
|
||||
* @return 供应商
|
||||
*/
|
||||
@Override
|
||||
public CtyunSmsImpl refresh(CtyunConfig ctyunConfig) {
|
||||
//重新构造一个实现对象
|
||||
ctyunSms = new CtyunSmsImpl( ctyunConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
return ctyunSms;
|
||||
public String getSupplier() {
|
||||
return CtyunSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Override
|
||||
public CtyunConfig getConfig() {
|
||||
return ConfigHolder.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(CtyunConfig config) {
|
||||
ConfigHolder.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,18 +1,15 @@
|
||||
package org.dromara.sms4j.ctyun.service;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.ctyun.config.CtyunConfig;
|
||||
import org.dromara.sms4j.ctyun.utils.CtyunUtils;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@ -26,40 +23,46 @@ import java.util.concurrent.Executor;
|
||||
* 2023/5/12 15:06
|
||||
**/
|
||||
@Slf4j
|
||||
public class CtyunSmsImpl extends AbstractSmsBlend {
|
||||
public class CtyunSmsImpl extends AbstractSmsBlend<CtyunConfig> {
|
||||
|
||||
private final CtyunConfig config;
|
||||
public static final String SUPPLIER = "ctyun";
|
||||
|
||||
private int retry = 0;
|
||||
|
||||
public CtyunSmsImpl(CtyunConfig config, Executor pool, DelayedTime delayedTime) {
|
||||
super(pool, delayedTime);
|
||||
this.config = config;
|
||||
super(config, pool, delayedTime);
|
||||
}
|
||||
|
||||
public CtyunSmsImpl(CtyunConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
map.put(config.getTemplateName(), message);
|
||||
return sendMessage(phone, config.getTemplateId(), map);
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
map.put(getConfig().getTemplateName(), message);
|
||||
return sendMessage(phone, getConfig().getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
String messageStr = JSONUtil.toJsonStr(messages);
|
||||
return getSmsResponse(phone, messageStr, templateId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
map.put(config.getTemplateName(), message);
|
||||
return massTexting(phones, config.getTemplateId(), map);
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
map.put(getConfig().getTemplateName(), message);
|
||||
return massTexting(phones, getConfig().getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
String messageStr = JSONUtil.toJsonStr(messages);
|
||||
return getSmsResponse(SmsUtil.arrayToString(phones), messageStr, templateId);
|
||||
@ -69,27 +72,39 @@ public class CtyunSmsImpl extends AbstractSmsBlend {
|
||||
String requestUrl;
|
||||
String paramStr;
|
||||
try {
|
||||
requestUrl = config.getRequestUrl();
|
||||
paramStr = CtyunUtils.generateParamJsonStr(config, phone, message, templateId);
|
||||
requestUrl = getConfig().getRequestUrl();
|
||||
paramStr = CtyunUtils.generateParamJsonStr(getConfig(), phone, message, templateId);
|
||||
} catch (Exception e) {
|
||||
log.error("ctyun send message error", e);
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
log.debug("requestUrl {}", requestUrl);
|
||||
try(HttpResponse response = HttpRequest.post(requestUrl)
|
||||
.addHeaders(CtyunUtils.signHeader(paramStr, config.getAccessKeyId(), config.getAccessKeySecret()))
|
||||
.body(paramStr)
|
||||
.execute()){
|
||||
JSONObject body = JSONUtil.parseObj(response.body());
|
||||
return this.getResponse(body);
|
||||
try {
|
||||
SmsResponse smsResponse = getResponse(http.postJson(requestUrl,
|
||||
CtyunUtils.signHeader(paramStr, getConfig().getAccessKeyId(), getConfig().getAccessKeySecret()),
|
||||
paramStr));
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(phone, message, templateId);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(phone, message, templateId);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(String phone, String message, String templateId) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry ++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return getSmsResponse(phone, message, templateId);
|
||||
}
|
||||
|
||||
private SmsResponse getResponse(JSONObject resJson) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setSuccess("OK".equals(resJson.getStr("code")));
|
||||
smsResponse.setData(resJson);
|
||||
smsResponse.setConfigId(this.config.getConfigId());
|
||||
smsResponse.setConfigId(getConfigId());
|
||||
return smsResponse;
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import cn.hutool.crypto.digest.HmacAlgorithm;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.ctyun.config.CtyunConfig;
|
||||
|
||||
@ -35,7 +36,7 @@ public class CtyunUtils {
|
||||
* 获取签名请求头
|
||||
*/
|
||||
public static Map<String, String> signHeader(String body, String key, String secret){
|
||||
Map<String, String> map = new ConcurrentHashMap<>();
|
||||
Map<String, String> map = new ConcurrentHashMap<>(4);
|
||||
|
||||
// 构造时间戳
|
||||
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
|
||||
@ -58,7 +59,7 @@ public class CtyunUtils {
|
||||
// 构造签名
|
||||
String signature = Base64.encode(hmacSHA256(signatureStr.getBytes(StandardCharsets.UTF_8), kDate));
|
||||
String signHeader = String.format("%s Headers=ctyun-eop-request-id;eop-date Signature=%s", key, signature);
|
||||
map.put("Content-Type", "application/json;charset=UTF-8");
|
||||
map.put("Content-Type", Constant.APPLICATION_JSON_UTF8);
|
||||
map.put("ctyun-eop-request-id", uuid);
|
||||
map.put("Eop-date", signatureTime);
|
||||
map.put("Eop-Authorization", signHeader);
|
||||
@ -74,7 +75,7 @@ public class CtyunUtils {
|
||||
* @param templateId 模板id
|
||||
*/
|
||||
public static String generateParamJsonStr(CtyunConfig ctyunConfig, String phone, String message, String templateId) {
|
||||
Map<String, String> paramMap = new HashMap<>();
|
||||
Map<String, String> paramMap = new HashMap<>(5);
|
||||
paramMap.put("action", ctyunConfig.getAction());
|
||||
paramMap.put("phoneNumber", phone);
|
||||
paramMap.put("signName", ctyunConfig.getSignature());
|
||||
|
||||
@ -6,28 +6,28 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.emay.service.EmaySmsImpl;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
|
||||
/**
|
||||
* @author Richard
|
||||
* @date 2023-04-11 12:00
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@NoArgsConstructor
|
||||
public class EmayConfig implements SupplierConfig {
|
||||
/** appKey*/
|
||||
private String appId ;
|
||||
/** appSecret */
|
||||
private String secretKey ;
|
||||
public class EmayConfig extends BaseConfig {
|
||||
|
||||
/** APP接入地址*/
|
||||
private String requestUrl;
|
||||
|
||||
/**
|
||||
* 配置标识名 如未配置取对应渠道名例如 Alibaba
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private String configId;
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return EmaySmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
package org.dromara.sms4j.emay.config;
|
||||
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.emay.service.EmaySmsImpl;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
|
||||
/**
|
||||
* EmaySmsConfig
|
||||
@ -11,17 +13,11 @@ import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
* @author Richard
|
||||
* @date 2023/04/11 12:00
|
||||
* */
|
||||
public class EmayFactory implements BaseProviderFactory<EmaySmsImpl, EmayConfig> {
|
||||
private static EmaySmsImpl emaySms;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class EmayFactory extends AbstractProviderFactory<EmaySmsImpl, EmayConfig> {
|
||||
|
||||
private static final EmayFactory INSTANCE = new EmayFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static EmayConfig config = EmayConfig.builder().build();
|
||||
}
|
||||
|
||||
private EmayFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -37,44 +33,16 @@ public class EmayFactory implements BaseProviderFactory<EmaySmsImpl, EmayConfig>
|
||||
*/
|
||||
@Override
|
||||
public EmaySmsImpl createSms(EmayConfig emayConfig) {
|
||||
if (emaySms == null){
|
||||
emaySms = createMultitonSms(emayConfig);
|
||||
}
|
||||
return emaySms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EmaySmsImpl createMultitonSms(EmayConfig emayConfig) {
|
||||
return new EmaySmsImpl(emayConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
|
||||
return new EmaySmsImpl(emayConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新短信实现对象
|
||||
* @param emayConfig 短信配置对象
|
||||
* @return 刷新后的短信实现对象
|
||||
* 获取供应商
|
||||
* @return 供应商
|
||||
*/
|
||||
@Override
|
||||
public EmaySmsImpl refresh(EmayConfig emayConfig){
|
||||
emaySms = new EmaySmsImpl(emayConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
|
||||
return emaySms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Override
|
||||
public EmayConfig getConfig() {
|
||||
return ConfigHolder.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(EmayConfig config) {
|
||||
ConfigHolder.config = config;
|
||||
public String getSupplier() {
|
||||
return EmaySmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,18 +1,15 @@
|
||||
package org.dromara.sms4j.emay.service;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.emay.config.EmayConfig;
|
||||
import org.dromara.sms4j.emay.util.EmayBuilder;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -25,31 +22,51 @@ import java.util.concurrent.Executor;
|
||||
* @date 2023-04-11 12:00
|
||||
*/
|
||||
@Slf4j
|
||||
public class EmaySmsImpl extends AbstractSmsBlend {
|
||||
public class EmaySmsImpl extends AbstractSmsBlend<EmayConfig> {
|
||||
|
||||
public static final String SUPPLIER = "emay";
|
||||
private int retry = 0;
|
||||
|
||||
public EmaySmsImpl(EmayConfig config, Executor pool, DelayedTime delayed) {
|
||||
super(pool, delayed);
|
||||
this.config = config;
|
||||
super(config, pool, delayed);
|
||||
}
|
||||
|
||||
private final EmayConfig config;
|
||||
public EmaySmsImpl(EmayConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
String url = config.getRequestUrl();
|
||||
Map<String, Object> params;
|
||||
String url = getConfig().getRequestUrl();
|
||||
Map<String, Object> params = EmayBuilder.buildRequestBody(getConfig().getAccessKeyId(), getConfig().getAccessKeySecret(), phone, message);
|
||||
try {
|
||||
params = EmayBuilder.buildRequestBody(config.getAppId(), config.getSecretKey(), phone, message);
|
||||
} catch (SmsBlendException e) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setSuccess(false);
|
||||
return smsResponse;
|
||||
Map<String, String> headers = new LinkedHashMap<>(1);
|
||||
headers.put("Content-Type", Constant.FROM_URLENCODED);
|
||||
SmsResponse smsResponse = getResponse(http.postFrom(url, headers, params));
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(phone, message);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(phone, message);
|
||||
}
|
||||
return getSendResponse(params, url);
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(String phone, String message) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return sendMessage(phone, message);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
@ -59,7 +76,6 @@ public class EmaySmsImpl extends AbstractSmsBlend {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
if (phones.size() > 500) {
|
||||
throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于500");
|
||||
@ -68,7 +84,6 @@ public class EmaySmsImpl extends AbstractSmsBlend {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
if (phones.size() > 500) {
|
||||
throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于500");
|
||||
@ -80,21 +95,11 @@ public class EmaySmsImpl extends AbstractSmsBlend {
|
||||
return sendMessage(SmsUtil.listToString(phones), EmayBuilder.listToString(list));
|
||||
}
|
||||
|
||||
private SmsResponse getSendResponse(Map<String, Object> body, String requestUrl) {
|
||||
try(HttpResponse response = HttpRequest.post(requestUrl)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.body(JSONUtil.toJsonStr(body))
|
||||
.execute()){
|
||||
JSONObject res = JSONUtil.parseObj(response.body());
|
||||
return this.getResponse(res);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse getResponse(JSONObject resJson) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setSuccess("success".equalsIgnoreCase(resJson.getStr("code")));
|
||||
smsResponse.setData(resJson);
|
||||
smsResponse.setConfigId(this.config.getConfigId());
|
||||
smsResponse.setConfigId(getConfigId());
|
||||
return smsResponse;
|
||||
}
|
||||
|
||||
|
||||
@ -6,33 +6,27 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.huawei.service.HuaweiSmsImpl;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
@NoArgsConstructor
|
||||
public class HuaweiConfig implements SupplierConfig {
|
||||
|
||||
/** appKey*/
|
||||
private String appKey ;
|
||||
/** appSecret */
|
||||
private String appSecret ;
|
||||
/** 短信签名*/
|
||||
private String signature;
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class HuaweiConfig extends BaseConfig {
|
||||
/** 国内短信签名通道号*/
|
||||
private String sender;
|
||||
/** 模板Id*/
|
||||
private String templateId;
|
||||
/** 短信状态报告接收地*/
|
||||
private String statusCallBack;
|
||||
/** APP接入地址*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 配置标识名 如未配置取对应渠道名例如 Alibaba
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private String configId;
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return HuaweiSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
package org.dromara.sms4j.huawei.config;
|
||||
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.aliyun.service.AlibabaSmsImpl;
|
||||
import org.dromara.sms4j.huawei.service.HuaweiSmsImpl;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
|
||||
/**
|
||||
* HuaweiSmsConfig
|
||||
@ -11,17 +14,11 @@ import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
* @author :Wind
|
||||
* 2023/4/8 15:27
|
||||
**/
|
||||
public class HuaweiFactory implements BaseProviderFactory<HuaweiSmsImpl, HuaweiConfig> {
|
||||
private static HuaweiSmsImpl huaweiSms;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class HuaweiFactory extends AbstractProviderFactory<HuaweiSmsImpl, HuaweiConfig> {
|
||||
|
||||
private static final HuaweiFactory INSTANCE = new HuaweiFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static HuaweiConfig config = HuaweiConfig.builder().build();
|
||||
}
|
||||
|
||||
private HuaweiFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -33,40 +30,16 @@ public class HuaweiFactory implements BaseProviderFactory<HuaweiSmsImpl, HuaweiC
|
||||
/** 建造一个华为短信实现*/
|
||||
@Override
|
||||
public HuaweiSmsImpl createSms(HuaweiConfig huaweiConfig) {
|
||||
if (huaweiSms == null){
|
||||
huaweiSms = createMultitonSms(huaweiConfig);
|
||||
}
|
||||
return huaweiSms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HuaweiSmsImpl createMultitonSms(HuaweiConfig huaweiConfig) {
|
||||
return new HuaweiSmsImpl(huaweiConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
|
||||
}
|
||||
|
||||
/** 刷新对象*/
|
||||
@Override
|
||||
public HuaweiSmsImpl refresh(HuaweiConfig huaweiConfig){
|
||||
huaweiSms = new HuaweiSmsImpl(huaweiConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
|
||||
return huaweiSms;
|
||||
return new HuaweiSmsImpl(huaweiConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
* 获取供应商
|
||||
* @return 供应商
|
||||
*/
|
||||
@Override
|
||||
public HuaweiConfig getConfig() {
|
||||
return ConfigHolder.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(HuaweiConfig config) {
|
||||
ConfigHolder.config = config;
|
||||
public String getSupplier() {
|
||||
return HuaweiSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,75 +1,84 @@
|
||||
package org.dromara.sms4j.huawei.service;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.huawei.config.HuaweiConfig;
|
||||
import org.dromara.sms4j.huawei.utils.HuaweiBuilder;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static org.dromara.sms4j.huawei.utils.HuaweiBuilder.listToString;
|
||||
|
||||
@Slf4j
|
||||
public class HuaweiSmsImpl extends AbstractSmsBlend {
|
||||
public class HuaweiSmsImpl extends AbstractSmsBlend<HuaweiConfig> {
|
||||
|
||||
public static final String SUPPLIER = "huawei";
|
||||
private int retry = 0;
|
||||
|
||||
public HuaweiSmsImpl(HuaweiConfig config, Executor pool, DelayedTime delayed) {
|
||||
super(pool, delayed);
|
||||
this.config = config;
|
||||
super(config, pool, delayed);
|
||||
}
|
||||
|
||||
private final HuaweiConfig config;
|
||||
public HuaweiSmsImpl(HuaweiConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
LinkedHashMap<String, String> mes = new LinkedHashMap<>();
|
||||
mes.put(UUID.randomUUID().toString().replaceAll("-", ""), message);
|
||||
return sendMessage(phone, config.getTemplateId(), mes);
|
||||
return sendMessage(phone, getConfig().getTemplateId(), mes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
String url = config.getUrl() + Constant.HUAWEI_REQUEST_URL;
|
||||
String url = getConfig().getUrl() + Constant.HUAWEI_REQUEST_URL;
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
list.add(entry.getValue());
|
||||
}
|
||||
String mess = listToString(list);
|
||||
String requestBody = HuaweiBuilder.buildRequestBody(config.getSender(), phone, templateId, mess, config.getStatusCallBack(), config.getSignature());
|
||||
Map<String, String> headers = new LinkedHashMap<>();
|
||||
headers.put("Authorization", Constant.HUAWEI_AUTH_HEADER_VALUE);
|
||||
headers.put("X-WSSE", HuaweiBuilder.buildWsseHeader(config.getAppKey(), config.getAppSecret()));
|
||||
headers.put("Content-Type", Constant.FROM_URLENCODED);
|
||||
try(HttpResponse response = HttpRequest.post(url)
|
||||
.addHeaders(headers)
|
||||
.body(requestBody)
|
||||
.execute()){
|
||||
JSONObject body = JSONUtil.parseObj(response.body());
|
||||
return this.getResponse(body);
|
||||
String requestBody = HuaweiBuilder.buildRequestBody(getConfig().getSender(), phone, templateId, mess, getConfig().getStatusCallBack(), getConfig().getSignature());
|
||||
try {
|
||||
Map<String, String> headers = new LinkedHashMap<>(3);
|
||||
headers.put("Authorization", Constant.HUAWEI_AUTH_HEADER_VALUE);
|
||||
headers.put("X-WSSE", HuaweiBuilder.buildWsseHeader(getConfig().getAccessKeyId(), getConfig().getAccessKeySecret()));
|
||||
headers.put("Content-Type", Constant.FROM_URLENCODED);
|
||||
SmsResponse smsResponse = getResponse(http.postJson(url, headers, requestBody));
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(phone, templateId, messages);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(phone, templateId, messages);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return sendMessage(phone, templateId, messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
return sendMessage(listToString(phones), message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
return sendMessage(listToString(phones), templateId, messages);
|
||||
}
|
||||
@ -78,7 +87,7 @@ public class HuaweiSmsImpl extends AbstractSmsBlend {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setSuccess("000000".equals(resJson.getStr("Code")));
|
||||
smsResponse.setData(resJson);
|
||||
smsResponse.setConfigId(this.config.getConfigId());
|
||||
smsResponse.setConfigId(getConfigId());
|
||||
return smsResponse;
|
||||
}
|
||||
|
||||
|
||||
@ -6,8 +6,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
import org.dromara.sms4j.jdcloud.service.JdCloudSmsImpl;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
|
||||
/**
|
||||
* 京东云短信配置属性
|
||||
@ -16,15 +16,22 @@ import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
* @since 2023/4/10 20:01
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
public class JdCloudConfig extends BaseConfig implements SupplierConfig {
|
||||
public class JdCloudConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 地域信息
|
||||
*/
|
||||
@Builder.Default
|
||||
private String region = "cn-north-1";
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return JdCloudSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,9 +5,10 @@ import com.jdcloud.sdk.auth.StaticCredentialsProvider;
|
||||
import com.jdcloud.sdk.http.HttpRequestConfig;
|
||||
import com.jdcloud.sdk.http.Protocol;
|
||||
import com.jdcloud.sdk.service.sms.client.SmsClient;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.jdcloud.service.JdCloudSmsImpl;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
|
||||
/**
|
||||
* 京东云短信配置
|
||||
@ -15,19 +16,11 @@ import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
* @author Charles7c
|
||||
* @since 2023/4/10 20:01
|
||||
*/
|
||||
public class JdCloudFactory implements BaseProviderFactory<JdCloudSmsImpl, JdCloudConfig> {
|
||||
|
||||
private static JdCloudSmsImpl jdCloudSms;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class JdCloudFactory extends AbstractProviderFactory<JdCloudSmsImpl, JdCloudConfig> {
|
||||
|
||||
private static final JdCloudFactory INSTANCE = new JdCloudFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static JdCloudConfig config = JdCloudConfig.builder().build();
|
||||
}
|
||||
|
||||
private JdCloudFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -54,52 +47,19 @@ public class JdCloudFactory implements BaseProviderFactory<JdCloudSmsImpl, JdClo
|
||||
*/
|
||||
@Override
|
||||
public JdCloudSmsImpl createSms(JdCloudConfig jdCloudConfig) {
|
||||
if (jdCloudSms == null) {
|
||||
jdCloudSms = createMultitonSms(jdCloudConfig);
|
||||
}
|
||||
return jdCloudSms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdCloudSmsImpl createMultitonSms(JdCloudConfig jdCloudConfig) {
|
||||
return new JdCloudSmsImpl(
|
||||
this.client(jdCloudConfig),
|
||||
jdCloudConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime()
|
||||
jdCloudConfig
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新对象
|
||||
* 获取供应商
|
||||
* @return 供应商
|
||||
*/
|
||||
@Override
|
||||
public JdCloudSmsImpl refresh(JdCloudConfig jdCloudConfig) {
|
||||
jdCloudSms = new JdCloudSmsImpl(
|
||||
this.client(jdCloudConfig),
|
||||
jdCloudConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime()
|
||||
);
|
||||
return jdCloudSms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Override
|
||||
public JdCloudConfig getConfig() {
|
||||
return ConfigHolder.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(JdCloudConfig config) {
|
||||
ConfigHolder.config = config;
|
||||
public String getSupplier() {
|
||||
return JdCloudSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,12 +5,11 @@ import com.jdcloud.sdk.service.sms.client.SmsClient;
|
||||
import com.jdcloud.sdk.service.sms.model.BatchSendRequest;
|
||||
import com.jdcloud.sdk.service.sms.model.BatchSendResult;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.jdcloud.config.JdCloudConfig;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
@ -25,56 +24,80 @@ import java.util.stream.Collectors;
|
||||
* @since 2023/4/10 20:01
|
||||
*/
|
||||
@Slf4j
|
||||
public class JdCloudSmsImpl extends AbstractSmsBlend {
|
||||
public class JdCloudSmsImpl extends AbstractSmsBlend<JdCloudConfig> {
|
||||
|
||||
public static final String SUPPLIER = "jdcloud";
|
||||
|
||||
private final SmsClient client;
|
||||
|
||||
private final JdCloudConfig config;
|
||||
private int retry = 0;
|
||||
|
||||
public JdCloudSmsImpl(SmsClient client, JdCloudConfig config, Executor pool, DelayedTime delayed) {
|
||||
super(pool, delayed);
|
||||
super(config, pool, delayed);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public JdCloudSmsImpl(SmsClient client, JdCloudConfig config) {
|
||||
super(config);
|
||||
this.client = client;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
return massTexting(Collections.singletonList(phone), message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
return massTexting(Collections.singletonList(phone), templateId, messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
map.put(IdUtil.fastSimpleUUID(), message);
|
||||
return massTexting(phones, config.getTemplateId(), map);
|
||||
return massTexting(phones, getConfig().getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
BatchSendRequest request;
|
||||
try {
|
||||
BatchSendRequest request = new BatchSendRequest();
|
||||
request = new BatchSendRequest();
|
||||
request.setPhoneList(phones);
|
||||
request.setRegionId(config.getRegion());
|
||||
request.setRegionId(getConfig().getRegion());
|
||||
request.setTemplateId(templateId);
|
||||
request.setSignId(config.getSignature());
|
||||
request.setSignId(getConfig().getSignature());
|
||||
List<String> params = messages.keySet().stream().map(messages::get)
|
||||
.collect(Collectors.toList());
|
||||
request.setParams(params);
|
||||
|
||||
BatchSendResult result = client.batchSend(request).getResult();
|
||||
return getSmsResponse(result);
|
||||
} catch (Exception e) {
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
BatchSendResult result = client.batchSend(request).getResult();
|
||||
SmsResponse smsResponse = getSmsResponse(result);
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(phones, templateId, messages);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(phones, templateId, messages);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return massTexting(phones, templateId, messages);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,7 +110,7 @@ public class JdCloudSmsImpl extends AbstractSmsBlend {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setSuccess(res.getStatus() != null && res.getStatus());
|
||||
smsResponse.setData(res);
|
||||
smsResponse.setConfigId(this.config.getConfigId());
|
||||
smsResponse.setConfigId(getConfigId());
|
||||
return smsResponse;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,17 +5,15 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
import org.dromara.sms4j.netease.service.NeteaseSmsImpl;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
|
||||
/**
|
||||
* @author adam
|
||||
*/
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
public class NeteaseConfig extends BaseConfig implements SupplierConfig {
|
||||
public class NeteaseConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 模板变量名称
|
||||
@ -25,20 +23,17 @@ public class NeteaseConfig extends BaseConfig implements SupplierConfig {
|
||||
/**
|
||||
* 模板短信请求地址
|
||||
*/
|
||||
@Builder.Default
|
||||
private String templateUrl = "https://api.netease.im/sms/sendtemplate.action";
|
||||
|
||||
|
||||
/**
|
||||
* 验证码短信请求地址
|
||||
*/
|
||||
@Builder.Default
|
||||
private String codeUrl = "https://api.netease.im/sms/sendcode.action";
|
||||
|
||||
/**
|
||||
* 验证码验证请求地址
|
||||
*/
|
||||
@Builder.Default
|
||||
private String verifyUrl = "https://api.netease.im/sms/verifycode.action";
|
||||
|
||||
/**
|
||||
@ -47,4 +42,14 @@ public class NeteaseConfig extends BaseConfig implements SupplierConfig {
|
||||
*/
|
||||
private Boolean needUp;
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return NeteaseSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
package org.dromara.sms4j.netease.config;
|
||||
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.netease.service.NeteaseSmsImpl;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
|
||||
/**
|
||||
* NeteaseSmsConfig
|
||||
@ -11,19 +13,11 @@ import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
* @author :adam
|
||||
* 2023-05-30
|
||||
**/
|
||||
public class NeteaseFactory implements BaseProviderFactory<NeteaseSmsImpl, NeteaseConfig> {
|
||||
|
||||
private static NeteaseSmsImpl neteaseSms;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class NeteaseFactory extends AbstractProviderFactory<NeteaseSmsImpl, NeteaseConfig> {
|
||||
|
||||
private static final NeteaseFactory INSTANCE = new NeteaseFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static NeteaseConfig config = NeteaseConfig.builder().build();
|
||||
}
|
||||
|
||||
private NeteaseFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -37,50 +31,17 @@ public class NeteaseFactory implements BaseProviderFactory<NeteaseSmsImpl, Netea
|
||||
*/
|
||||
@Override
|
||||
public NeteaseSmsImpl createSms(NeteaseConfig neteaseConfig) {
|
||||
if (neteaseSms == null) {
|
||||
neteaseSms = new NeteaseSmsImpl(
|
||||
neteaseConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime()
|
||||
);
|
||||
}
|
||||
return neteaseSms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NeteaseSmsImpl createMultitonSms(NeteaseConfig neteaseConfig) {
|
||||
return new NeteaseSmsImpl(neteaseConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
return new NeteaseSmsImpl(neteaseConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新对象
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public NeteaseSmsImpl refresh(NeteaseConfig neteaseConfig) {
|
||||
neteaseSms = new NeteaseSmsImpl(
|
||||
neteaseConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime()
|
||||
);
|
||||
return neteaseSms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Override
|
||||
public NeteaseConfig getConfig() {
|
||||
return ConfigHolder.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(NeteaseConfig config) {
|
||||
ConfigHolder.config = config;
|
||||
public String getSupplier() {
|
||||
return NeteaseSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -2,25 +2,18 @@ package org.dromara.sms4j.netease.service;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.netease.config.NeteaseConfig;
|
||||
import org.dromara.sms4j.netease.utils.NeteaseUtils;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@ -31,13 +24,22 @@ import java.util.concurrent.Executor;
|
||||
*/
|
||||
|
||||
@Slf4j
|
||||
public class NeteaseSmsImpl extends AbstractSmsBlend {
|
||||
public class NeteaseSmsImpl extends AbstractSmsBlend<NeteaseConfig> {
|
||||
|
||||
private NeteaseConfig config;
|
||||
public static final String SUPPLIER = "netease";
|
||||
private int retry = 0;
|
||||
|
||||
public NeteaseSmsImpl(NeteaseConfig config, Executor pool, DelayedTime delayed) {
|
||||
super(pool, delayed);
|
||||
this.config = config;
|
||||
super(config, pool, delayed);
|
||||
}
|
||||
|
||||
public NeteaseSmsImpl(NeteaseConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -46,11 +48,10 @@ public class NeteaseSmsImpl extends AbstractSmsBlend {
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
Optional.ofNullable(phone).orElseThrow(() -> new SmsBlendException("手机号不能为空"));
|
||||
Optional.ofNullable(config.getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
|
||||
return getSmsResponse(config.getTemplateUrl(), Collections.singletonList(phone), message, config.getTemplateId());
|
||||
Optional.ofNullable(getConfig().getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
|
||||
return getSmsResponse(getConfig().getTemplateUrl(), Collections.singletonList(phone), message, getConfig().getTemplateId());
|
||||
}
|
||||
|
||||
|
||||
@ -61,16 +62,14 @@ public class NeteaseSmsImpl extends AbstractSmsBlend {
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
Optional.ofNullable(phone).orElseThrow(() -> new SmsBlendException("手机号不能为空"));
|
||||
Optional.ofNullable(config.getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
|
||||
Optional.ofNullable(getConfig().getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
|
||||
String messageStr = messages.get("params");
|
||||
return getSmsResponse(config.getTemplateUrl(), Collections.singletonList(phone), messageStr, templateId);
|
||||
return getSmsResponse(getConfig().getTemplateUrl(), Collections.singletonList(phone), messageStr, templateId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
if (phones.size() < 1) {
|
||||
throw new SmsBlendException("手机号不能为空");
|
||||
@ -78,45 +77,62 @@ public class NeteaseSmsImpl extends AbstractSmsBlend {
|
||||
if (phones.size() > 100) {
|
||||
throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于100");
|
||||
}
|
||||
Optional.ofNullable(config.getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
|
||||
return getSmsResponse(config.getTemplateUrl(), phones, config.getTemplateId(), message);
|
||||
Optional.ofNullable(getConfig().getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
|
||||
return getSmsResponse(getConfig().getTemplateUrl(), phones, getConfig().getTemplateId(), message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
if (phones.size() > 100) {
|
||||
throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于100");
|
||||
}
|
||||
Optional.ofNullable(config.getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
|
||||
Optional.ofNullable(getConfig().getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
|
||||
String messageStr = messages.get("message");
|
||||
return getSmsResponse(config.getTemplateUrl(), phones, messageStr, templateId);
|
||||
return getSmsResponse(getConfig().getTemplateUrl(), phones, messageStr, templateId);
|
||||
}
|
||||
|
||||
private SmsResponse getSmsResponse(String requestUrl, List<String> phones, String message, String templateId) {
|
||||
private SmsResponse getSmsResponse(String requestUrl, List<String> phones, String message, String templateId) {
|
||||
String nonce = IdUtil.fastSimpleUUID();
|
||||
String curTime = String.valueOf(DateUtil.currentSeconds());
|
||||
String checkSum = NeteaseUtils.getCheckSum(config.getAccessKeySecret(), nonce, curTime);
|
||||
Map<String, String> body = NeteaseUtils.generateParamMap(config, phones, message, templateId);
|
||||
String paramStr = NeteaseUtils.generateParamBody(body);
|
||||
try(HttpResponse response = HttpRequest.post(requestUrl)
|
||||
.header("Content-Type", "application/x-www-form-urlencoded")
|
||||
.header("AppKey", config.getAccessKeyId())
|
||||
.header("Nonce", nonce)
|
||||
.header("CurTime", curTime)
|
||||
.header("CheckSum", checkSum)
|
||||
.body(paramStr)
|
||||
.execute()){
|
||||
JSONObject res = JSONUtil.parseObj(response.body());
|
||||
return this.getResponse(res);
|
||||
String checkSum = NeteaseUtils.getCheckSum(getConfig().getAccessKeySecret(), nonce, curTime);
|
||||
Map<String, Object> body = new LinkedHashMap<>(4);
|
||||
body.put("templateid", templateId);
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
jsonArray.addAll(phones);
|
||||
body.put("mobiles", jsonArray.toString());
|
||||
body.put("params", message);
|
||||
body.put("needUp", getConfig().getNeedUp());
|
||||
|
||||
try {
|
||||
Map<String, String> headers = new LinkedHashMap<>(5);
|
||||
headers.put("Content-Type", Constant.FROM_URLENCODED);
|
||||
headers.put("AppKey", getConfig().getAccessKeyId());
|
||||
headers.put("Nonce", nonce);
|
||||
headers.put("CurTime", curTime);
|
||||
headers.put("CheckSum", checkSum);
|
||||
SmsResponse smsResponse = getResponse(http.postJson(requestUrl, headers, body));
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(requestUrl, phones, message, templateId);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(requestUrl, phones, message, templateId);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(String requestUrl, List<String> phones, String message, String templateId) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return getSmsResponse(requestUrl, phones, message, templateId);
|
||||
}
|
||||
|
||||
private SmsResponse getResponse(JSONObject jsonObject) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setSuccess(jsonObject.getInt("code") <= 200);
|
||||
smsResponse.setData(jsonObject);
|
||||
smsResponse.setConfigId(this.config.getConfigId());
|
||||
smsResponse.setConfigId(getConfigId());
|
||||
return smsResponse;
|
||||
}
|
||||
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
package org.dromara.sms4j.provider.base;
|
||||
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
|
||||
/**
|
||||
* AlibabaSmsConfig
|
||||
* <p>短信对象建造者</p>
|
||||
* @param <S> 短信对象
|
||||
* @param <C> 短信配置对象
|
||||
*/
|
||||
public interface BaseProviderFactory<S extends SmsBlend, C extends SupplierConfig> {
|
||||
|
||||
/**
|
||||
* 创建短信实现对象
|
||||
* @param c 短信配置对象
|
||||
* @return 短信实现对象
|
||||
*/
|
||||
S createSms(C c);
|
||||
|
||||
/**
|
||||
* createMultitonSms
|
||||
* <p> 创建多例的短信实现对象
|
||||
* 在此方法中创建的短信实现对象框架将不再持有单例,故而JVM中可以同时存在多个,如果更改配置后需要重新调用该方法
|
||||
* @param c 短信配置对象
|
||||
* @return 短信实现对象
|
||||
* @author :Wind
|
||||
*/
|
||||
S createMultitonSms(C c);
|
||||
|
||||
/**
|
||||
* 刷新短信实现对象
|
||||
* @param c 短信配置对象
|
||||
* @return 刷新后的短信实现对象
|
||||
*/
|
||||
S refresh(C c);
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
*/
|
||||
C getConfig();
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
void setConfig(C config);
|
||||
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package org.dromara.sms4j.provider.config;
|
||||
|
||||
import lombok.Data;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
|
||||
/**
|
||||
* 短信配置属性基类
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/4/20 23:03
|
||||
*/
|
||||
@Data
|
||||
public abstract class BaseConfig implements SupplierConfig {
|
||||
|
||||
/**
|
||||
* Access Key
|
||||
*/
|
||||
private String accessKeyId;
|
||||
|
||||
/**
|
||||
* Access Key Secret
|
||||
*/
|
||||
private String accessKeySecret;
|
||||
|
||||
/**
|
||||
* 短信签名
|
||||
*/
|
||||
private String signature;
|
||||
|
||||
/**
|
||||
* 模板 ID
|
||||
*/
|
||||
private String templateId;
|
||||
|
||||
/**
|
||||
* 权重
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private Integer weight = 1;
|
||||
|
||||
/**
|
||||
* 配置标识名 如未配置取对应渠道名例如 Alibaba
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
private String configId;
|
||||
|
||||
/**
|
||||
* 重试间隔(单位:秒),默认为5秒
|
||||
*/
|
||||
private int retryInterval = 5;
|
||||
|
||||
public void setRetryInterval(int retryInterval) {
|
||||
if (retryInterval <= 0){
|
||||
throw new SmsBlendException("重试间隔必须大于0秒");
|
||||
}
|
||||
this.retryInterval = retryInterval;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重试次数,默认为0次
|
||||
*/
|
||||
private int maxRetries = 0;
|
||||
|
||||
public void setMaxRetries(int maxRetries) {
|
||||
if (maxRetries < 0){
|
||||
throw new SmsBlendException("重试次数不能小于0次");
|
||||
}
|
||||
this.maxRetries = maxRetries;
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package org.dromara.sms4j.comm.config;
|
||||
package org.dromara.sms4j.provider.config;
|
||||
|
||||
public class SmsBanner {
|
||||
private static final String banner =
|
||||
@ -1,4 +1,4 @@
|
||||
package org.dromara.sms4j.comm.config;
|
||||
package org.dromara.sms4j.provider.config;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
@ -8,7 +8,7 @@ import org.dromara.sms4j.comm.enumerate.ConfigType;
|
||||
public class SmsConfig {
|
||||
|
||||
/** 配置源类型*/
|
||||
private ConfigType configType = ConfigType.CONFIG_FILE;
|
||||
private ConfigType configType = ConfigType.YAML;
|
||||
|
||||
/**
|
||||
* 打印banner
|
||||
@ -1,72 +0,0 @@
|
||||
package org.dromara.sms4j.provider.enumerate;
|
||||
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaFactory;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenFactory;
|
||||
import org.dromara.sms4j.ctyun.config.CtyunFactory;
|
||||
import org.dromara.sms4j.emay.config.EmayFactory;
|
||||
import org.dromara.sms4j.huawei.config.HuaweiFactory;
|
||||
import org.dromara.sms4j.jdcloud.config.JdCloudFactory;
|
||||
import org.dromara.sms4j.netease.config.NeteaseFactory;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.tencent.config.TencentFactory;
|
||||
import org.dromara.sms4j.unisms.config.UniFactory;
|
||||
import org.dromara.sms4j.yunpian.config.YunPianFactory;
|
||||
import org.dromara.sms4j.zhutong.config.ZhutongFactory;
|
||||
|
||||
/**
|
||||
* SupplierType
|
||||
* <p> 短信供应商枚举
|
||||
* @author :Wind
|
||||
* 2023/4/7 20:55
|
||||
**/
|
||||
public enum SupplierType {
|
||||
/** 阿里云*/
|
||||
ALIBABA("阿里云短信", AlibabaFactory.instance()),
|
||||
/** 华为云*/
|
||||
HUAWEI("华为云短信", HuaweiFactory.instance()),
|
||||
/** 云片*/
|
||||
YUNPIAN("云片短信", YunPianFactory.instance()),
|
||||
/** 腾讯云*/
|
||||
TENCENT("腾讯云短信", TencentFactory.instance()),
|
||||
/** 合一短信*/
|
||||
UNI_SMS("合一短信", UniFactory.instance()),
|
||||
/** 京东云 */
|
||||
JD_CLOUD("京东云短信", JdCloudFactory.instance()),
|
||||
/** 容联云 */
|
||||
CLOOPEN("容联云短信", CloopenFactory.instance()),
|
||||
/** 亿美软通*/
|
||||
EMAY("亿美软通", EmayFactory.instance()),
|
||||
/** 天翼云 */
|
||||
CTYUN("天翼云短信", CtyunFactory.instance()),
|
||||
/** 网易云信 */
|
||||
NETEASE("网易云短信", NeteaseFactory.instance()),
|
||||
/** 助通短信 */
|
||||
ZHUTONG("助通短信", ZhutongFactory.instance())
|
||||
;
|
||||
|
||||
|
||||
/**
|
||||
* 渠道名称
|
||||
*/
|
||||
private final String name;
|
||||
/**
|
||||
* 短信对象配置
|
||||
*/
|
||||
private final BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig> providerFactory;
|
||||
|
||||
|
||||
SupplierType(String name, BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig> providerFactory) {
|
||||
this.name = name;
|
||||
this.providerFactory = providerFactory;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig> getProviderFactory() {
|
||||
return providerFactory;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package org.dromara.sms4j.provider.factory;
|
||||
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public abstract class AbstractProviderFactory<S extends SmsBlend, C extends SupplierConfig> implements BaseProviderFactory<S, C> {
|
||||
|
||||
private Class<C> configClass;
|
||||
|
||||
public AbstractProviderFactory() {
|
||||
Type genericSuperclass = getClass().getGenericSuperclass();
|
||||
if(genericSuperclass instanceof ParameterizedType) {
|
||||
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
|
||||
Type[] typeArguments = paramType.getActualTypeArguments();
|
||||
if(typeArguments.length > 1 && typeArguments[1] instanceof Class) {
|
||||
configClass = (Class<C>) typeArguments[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置类
|
||||
* @return 配置类
|
||||
*/
|
||||
public Class<C> getConfigClass() {
|
||||
return configClass;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package org.dromara.sms4j.provider.factory;
|
||||
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
|
||||
/**
|
||||
* AlibabaSmsConfig
|
||||
* <p>短信对象建造者</p>
|
||||
* @param <S> 短信对象
|
||||
* @param <C> 短信配置对象
|
||||
*/
|
||||
public interface BaseProviderFactory<S extends SmsBlend, C extends SupplierConfig> {
|
||||
|
||||
/**
|
||||
* 创建短信实现对象
|
||||
* @param c 短信配置对象
|
||||
* @return 短信实现对象
|
||||
*/
|
||||
S createSms(C c);
|
||||
|
||||
/**
|
||||
* 获取配置类
|
||||
* @return 配置类
|
||||
*/
|
||||
Class<C> getConfigClass();
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
* @return 供应商
|
||||
*/
|
||||
String getSupplier();
|
||||
|
||||
}
|
||||
@ -1,9 +1,7 @@
|
||||
package org.dromara.sms4j.comm.factory;
|
||||
package org.dromara.sms4j.provider.factory;
|
||||
|
||||
import org.dromara.sms4j.comm.config.SmsConfig;
|
||||
import org.dromara.sms4j.comm.config.SmsSqlConfig;
|
||||
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.utils.JDBCTool;
|
||||
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
@ -28,15 +26,6 @@ public class BeanFactory {
|
||||
/** 核心配置信息*/
|
||||
private static SmsConfig smsConfig;
|
||||
|
||||
/** jdbc工具*/
|
||||
private static JDBCTool jdbcTool;
|
||||
|
||||
/** 数据库配置*/
|
||||
private static SmsSqlConfig smsSqlConfig;
|
||||
|
||||
/** 实例化自身对象防止被GC*/
|
||||
private static final BeanFactory beanFactory = new BeanFactory();
|
||||
|
||||
private BeanFactory() {
|
||||
}
|
||||
|
||||
@ -75,17 +64,4 @@ public class BeanFactory {
|
||||
return smsConfig;
|
||||
}
|
||||
|
||||
public static SmsSqlConfig getSmsSqlConfig(){
|
||||
if (smsSqlConfig == null){
|
||||
smsSqlConfig = new SmsSqlConfig();
|
||||
}
|
||||
return smsSqlConfig;
|
||||
}
|
||||
|
||||
public static JDBCTool getJDBCTool(){
|
||||
if (jdbcTool == null){
|
||||
jdbcTool = new JDBCTool(getSmsSqlConfig());
|
||||
}
|
||||
return jdbcTool;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package org.dromara.sms4j.provider.factory;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 供应商工厂持有者
|
||||
*
|
||||
* @author xiaoyan
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class ProviderFactoryHolder {
|
||||
|
||||
private static final Map<String, BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig>> factories = new ConcurrentHashMap<>();
|
||||
|
||||
public static void registerFactory(BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig> factory) {
|
||||
if(factory == null) {
|
||||
throw new SmsBlendException("注册供应商工厂失败,工厂实例不能为空");
|
||||
}
|
||||
factories.put(factory.getSupplier(), factory);
|
||||
}
|
||||
|
||||
public static void registerFactory(List<BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig>> factoryList) {
|
||||
if(CollUtil.isEmpty(factoryList)) {
|
||||
return;
|
||||
}
|
||||
for(BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig> factory : factoryList) {
|
||||
if(factory == null) {
|
||||
continue;
|
||||
}
|
||||
registerFactory(factory);
|
||||
}
|
||||
}
|
||||
|
||||
public static BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig> requireForSupplier(String supplier) {
|
||||
return factories.getOrDefault(supplier, null);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,10 +1,14 @@
|
||||
package org.dromara.sms4j.api;
|
||||
package org.dromara.sms4j.provider.service;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.Getter;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.callback.CallBack;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.provider.factory.BeanFactory;
|
||||
import org.dromara.sms4j.comm.utils.SmsHttpUtil;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@ -12,21 +16,37 @@ import java.util.TimerTask;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public abstract class AbstractSmsBlend implements SmsBlend{
|
||||
public abstract class AbstractSmsBlend<C extends SupplierConfig> implements SmsBlend {
|
||||
|
||||
@Getter
|
||||
private final String configId;
|
||||
|
||||
private final C config;
|
||||
|
||||
protected final Executor pool;
|
||||
|
||||
protected final DelayedTime delayed;
|
||||
|
||||
protected AbstractSmsBlend(Executor pool, DelayedTime delayed) {
|
||||
protected final SmsHttpUtil http = SmsHttpUtil.instance();
|
||||
|
||||
protected AbstractSmsBlend(C config, Executor pool, DelayedTime delayed) {
|
||||
this.configId = StrUtil.isEmpty(config.getConfigId()) ? getSupplier() : config.getConfigId();
|
||||
this.config = config;
|
||||
this.pool = pool;
|
||||
this.delayed = delayed;
|
||||
}
|
||||
|
||||
protected AbstractSmsBlend() {
|
||||
protected AbstractSmsBlend(C config) {
|
||||
this.configId = StrUtil.isEmpty(config.getConfigId()) ? getSupplier() : config.getConfigId();
|
||||
this.config = config;
|
||||
this.pool = BeanFactory.getExecutor();
|
||||
this.delayed = BeanFactory.getDelayedTime();
|
||||
}
|
||||
|
||||
protected C getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:发送固定消息模板短信
|
||||
* <p>此方法将使用配置文件中预设的短信模板进行短信发送
|
||||
@ -79,7 +99,6 @@ public abstract class AbstractSmsBlend implements SmsBlend{
|
||||
* @param callBack 回调
|
||||
* @author :Wind
|
||||
*/
|
||||
@Restricted
|
||||
public final void sendMessageAsync(String phone, String message, CallBack callBack){
|
||||
CompletableFuture<SmsResponse> smsResponseCompletableFuture = CompletableFuture.supplyAsync(() -> sendMessage(phone, message), pool);
|
||||
smsResponseCompletableFuture.thenAcceptAsync(callBack::callBack);
|
||||
@ -93,7 +112,6 @@ public abstract class AbstractSmsBlend implements SmsBlend{
|
||||
* @param message 发送内容
|
||||
* @author :Wind
|
||||
*/
|
||||
@Restricted
|
||||
public final void sendMessageAsync(String phone, String message){
|
||||
pool.execute(() -> {
|
||||
sendMessage(phone, message);
|
||||
@ -110,7 +128,6 @@ public abstract class AbstractSmsBlend implements SmsBlend{
|
||||
* @author :Wind
|
||||
*/
|
||||
|
||||
@Restricted
|
||||
public final void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages, CallBack callBack){
|
||||
CompletableFuture<SmsResponse> smsResponseCompletableFuture = CompletableFuture.supplyAsync(() -> sendMessage(phone,templateId, messages), pool);
|
||||
smsResponseCompletableFuture.thenAcceptAsync(callBack::callBack);
|
||||
@ -124,7 +141,6 @@ public abstract class AbstractSmsBlend implements SmsBlend{
|
||||
* @param messages key为模板变量名称 value为模板变量值
|
||||
* @author :Wind
|
||||
*/
|
||||
@Restricted
|
||||
public final void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages){
|
||||
pool.execute(() -> {
|
||||
sendMessage(phone, templateId, messages);
|
||||
@ -140,7 +156,6 @@ public abstract class AbstractSmsBlend implements SmsBlend{
|
||||
* @param delayedTime 延迟时间
|
||||
* @author :Wind
|
||||
*/
|
||||
@Restricted
|
||||
public final void delayedMessage(String phone, String message, Long delayedTime){
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
@ -160,7 +175,6 @@ public abstract class AbstractSmsBlend implements SmsBlend{
|
||||
* @param delayedTime 延迟的时间
|
||||
* @author :Wind
|
||||
*/
|
||||
@Restricted
|
||||
public final void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime){
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
@ -177,7 +191,6 @@ public abstract class AbstractSmsBlend implements SmsBlend{
|
||||
* @param phones 要群体发送的手机号码
|
||||
* @author :Wind
|
||||
*/
|
||||
@Restricted
|
||||
public final void delayMassTexting(List<String> phones, String message, Long delayedTime){
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
@ -197,7 +210,6 @@ public abstract class AbstractSmsBlend implements SmsBlend{
|
||||
* @param delayedTime 延迟的时间
|
||||
* @author :Wind
|
||||
*/
|
||||
@Restricted
|
||||
public final void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime){
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
@ -6,15 +6,12 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
import org.dromara.sms4j.tencent.service.TencentSmsImpl;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
public class TencentConfig extends BaseConfig implements SupplierConfig {
|
||||
public class TencentConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 短信sdkAppId
|
||||
@ -24,32 +21,37 @@ public class TencentConfig extends BaseConfig implements SupplierConfig {
|
||||
/**
|
||||
* 地域信息默认为 ap-guangzhou
|
||||
*/
|
||||
@Builder.Default
|
||||
private String territory = "ap-guangzhou";
|
||||
|
||||
/**
|
||||
* 请求超时时间
|
||||
*/
|
||||
@Builder.Default
|
||||
private Integer connTimeout = 60;
|
||||
/** 请求地址*/
|
||||
@Builder.Default
|
||||
private String requestUrl = "sms.tencentcloudapi.com";
|
||||
/**
|
||||
* 接口名称
|
||||
*/
|
||||
@Builder.Default
|
||||
private String action = "SendSms";
|
||||
|
||||
/**
|
||||
* 接口版本
|
||||
*/
|
||||
@Builder.Default
|
||||
private String version = "2021-01-11";
|
||||
|
||||
/**
|
||||
* 服务名
|
||||
*/
|
||||
@Builder.Default
|
||||
private String service = "sms";
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return TencentSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
package org.dromara.sms4j.tencent.config;
|
||||
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
import org.dromara.sms4j.tencent.service.TencentSmsImpl;
|
||||
|
||||
/**
|
||||
@ -11,19 +11,10 @@ import org.dromara.sms4j.tencent.service.TencentSmsImpl;
|
||||
* @author :Wind
|
||||
* 2023/4/8 16:05
|
||||
**/
|
||||
public class TencentFactory implements BaseProviderFactory<TencentSmsImpl, TencentConfig> {
|
||||
|
||||
private static TencentSmsImpl tencentSms;
|
||||
public class TencentFactory extends AbstractProviderFactory<TencentSmsImpl, TencentConfig> {
|
||||
|
||||
private static final TencentFactory INSTANCE = new TencentFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static TencentConfig config = TencentConfig.builder().build();
|
||||
}
|
||||
|
||||
private TencentFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -37,44 +28,17 @@ public class TencentFactory implements BaseProviderFactory<TencentSmsImpl, Tence
|
||||
*/
|
||||
@Override
|
||||
public TencentSmsImpl createSms(TencentConfig tencentConfig) {
|
||||
if (tencentSms == null) {
|
||||
tencentSms = createMultitonSms(tencentConfig);
|
||||
}
|
||||
return tencentSms;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TencentSmsImpl createMultitonSms(TencentConfig tencentConfig) {
|
||||
return new TencentSmsImpl(tencentConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime()
|
||||
);
|
||||
return new TencentSmsImpl(tencentConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新对象
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public TencentSmsImpl refresh(TencentConfig tencentConfig) {
|
||||
tencentSms = new TencentSmsImpl(tencentConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime()
|
||||
);
|
||||
return tencentSms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
*/
|
||||
@Override
|
||||
public TencentConfig getConfig() {
|
||||
return ConfigHolder.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(TencentConfig config) {
|
||||
ConfigHolder.config = config;
|
||||
public String getSupplier() {
|
||||
return TencentSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,18 +1,15 @@
|
||||
package org.dromara.sms4j.tencent.service;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.jdcloud.sdk.utils.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.tencent.config.TencentConfig;
|
||||
import org.dromara.sms4j.tencent.utils.TencentUtils;
|
||||
|
||||
@ -26,50 +23,55 @@ import java.util.concurrent.Executor;
|
||||
* @author wind
|
||||
*/
|
||||
@Slf4j
|
||||
public class TencentSmsImpl extends AbstractSmsBlend {
|
||||
public class TencentSmsImpl extends AbstractSmsBlend<TencentConfig> {
|
||||
|
||||
private final TencentConfig config;
|
||||
public static final String SUPPLIER = "tencent";
|
||||
private int retry = 0;
|
||||
|
||||
public TencentSmsImpl(TencentConfig tencentSmsConfig, Executor pool, DelayedTime delayed) {
|
||||
super(pool, delayed);
|
||||
this.config = tencentSmsConfig;
|
||||
super(tencentSmsConfig, pool, delayed);
|
||||
}
|
||||
|
||||
public TencentSmsImpl(TencentConfig tencentSmsConfig) {
|
||||
super(tencentSmsConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
String[] split = message.split("&");
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
map.put(String.valueOf(i), split[i]);
|
||||
}
|
||||
return sendMessage(phone, config.getTemplateId(), map);
|
||||
return sendMessage(phone, getConfig().getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
list.add(entry.getValue());
|
||||
}
|
||||
String[] s = new String[list.size()];
|
||||
return getSmsResponse(new String[]{"+86" + phone}, list.toArray(s), templateId);
|
||||
return getSmsResponse(new String[]{StrUtil.addPrefixIfNot(phone, "+86")}, list.toArray(s), templateId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
String[] split = message.split("&");
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
map.put(String.valueOf(i), split[i]);
|
||||
}
|
||||
return massTexting(phones, config.getTemplateId(), map);
|
||||
return massTexting(phones, getConfig().getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
@ -83,32 +85,43 @@ public class TencentSmsImpl extends AbstractSmsBlend {
|
||||
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
|
||||
String signature;
|
||||
try {
|
||||
signature = TencentUtils.generateSignature(this.config, templateId, messages, phones, timestamp);
|
||||
signature = TencentUtils.generateSignature(this.getConfig(), templateId, messages, phones, timestamp);
|
||||
} catch (Exception e) {
|
||||
log.error("tencent send message error", e);
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
Map<String, String> headsMap = TencentUtils.generateHeadsMap(signature, timestamp, config.getAction(),
|
||||
config.getVersion(), config.getTerritory(), config.getRequestUrl());
|
||||
Map<String, Object> requestBody = TencentUtils.generateRequestBody(phones, config.getSdkAppId(),
|
||||
config.getSignature(), templateId, messages);
|
||||
String url = Constant.HTTPS_PREFIX + config.getRequestUrl();
|
||||
try(HttpResponse response = HttpRequest.post(url)
|
||||
.addHeaders(headsMap)
|
||||
.body(JSONUtil.toJsonStr(requestBody))
|
||||
.execute()){
|
||||
JSONObject body = JSONUtil.parseObj(response.body());
|
||||
return this.getResponse(body);
|
||||
Map<String, String> headsMap = TencentUtils.generateHeadsMap(signature, timestamp, getConfig().getAction(),
|
||||
getConfig().getVersion(), getConfig().getTerritory(), getConfig().getRequestUrl());
|
||||
Map<String, Object> requestBody = TencentUtils.generateRequestBody(phones, getConfig().getSdkAppId(),
|
||||
getConfig().getSignature(), templateId, messages);
|
||||
String url = Constant.HTTPS_PREFIX + getConfig().getRequestUrl();
|
||||
|
||||
try {
|
||||
SmsResponse smsResponse = getResponse(http.postJson(url, headsMap, requestBody));
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(phones, messages, templateId);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(phones, messages, templateId);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(String[] phones, String[] messages, String templateId) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return getSmsResponse(phones, messages, templateId);
|
||||
}
|
||||
|
||||
private SmsResponse getResponse(JSONObject resJson) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
JSONObject response = resJson.getJSONObject("Response");
|
||||
String error = response.getStr("Error");
|
||||
smsResponse.setSuccess(StringUtils.isBlank(error));
|
||||
smsResponse.setData(resJson);
|
||||
smsResponse.setConfigId(this.config.getConfigId());
|
||||
smsResponse.setConfigId(getConfigId());
|
||||
return smsResponse;
|
||||
}
|
||||
}
|
||||
@ -6,24 +6,31 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
import org.dromara.sms4j.unisms.service.UniSmsImpl;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
public class UniConfig extends BaseConfig implements SupplierConfig {
|
||||
public class UniConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 是否为简易模式
|
||||
*/
|
||||
@Builder.Default
|
||||
private Boolean isSimple = true;
|
||||
|
||||
/**
|
||||
* 模板变量名称
|
||||
*/
|
||||
private String templateName;
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return UniSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
package org.dromara.sms4j.unisms.config;
|
||||
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
import org.dromara.sms4j.unisms.core.Uni;
|
||||
import org.dromara.sms4j.unisms.service.UniSmsImpl;
|
||||
|
||||
@ -11,19 +13,11 @@ import org.dromara.sms4j.unisms.service.UniSmsImpl;
|
||||
* @author :Wind
|
||||
* 2023/4/8 15:46
|
||||
**/
|
||||
public class UniFactory implements BaseProviderFactory<UniSmsImpl, UniConfig> {
|
||||
|
||||
private static UniSmsImpl uniSmsImpl;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class UniFactory extends AbstractProviderFactory<UniSmsImpl, UniConfig> {
|
||||
|
||||
private static final UniFactory INSTANCE = new UniFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static UniConfig config = UniConfig.builder().build();
|
||||
}
|
||||
|
||||
private UniFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -49,46 +43,18 @@ public class UniFactory implements BaseProviderFactory<UniSmsImpl, UniConfig> {
|
||||
*/
|
||||
@Override
|
||||
public UniSmsImpl createSms(UniConfig uniConfig){
|
||||
if (uniSmsImpl == null){
|
||||
this.buildSms(uniConfig);
|
||||
uniSmsImpl = createMultitonSms(uniConfig);
|
||||
}
|
||||
return uniSmsImpl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniSmsImpl createMultitonSms(UniConfig config) {
|
||||
return new UniSmsImpl(config, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
}
|
||||
|
||||
/**
|
||||
* refresh
|
||||
* <p>刷新对象
|
||||
* @author :Wind
|
||||
*/
|
||||
@Override
|
||||
public UniSmsImpl refresh(UniConfig uniConfig){
|
||||
this.buildSms(uniConfig);
|
||||
uniSmsImpl = new UniSmsImpl(uniConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
return uniSmsImpl;
|
||||
return new UniSmsImpl(uniConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public UniConfig getConfig() {
|
||||
return ConfigHolder.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(UniConfig config) {
|
||||
ConfigHolder.config = config;
|
||||
public String getSupplier() {
|
||||
return UniSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -61,11 +61,11 @@ public class Uni {
|
||||
* @return the Uni Client
|
||||
* @author :Wind
|
||||
*/
|
||||
public static UniClient getClient() {
|
||||
public static UniClient getClient(int retryInterval, int maxRetries) {
|
||||
if (Uni.client == null) {
|
||||
synchronized (Uni.class) {
|
||||
if (Uni.client == null) {
|
||||
Uni.client = buildClient();
|
||||
Uni.client = buildClient(retryInterval, maxRetries);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -78,7 +78,7 @@ public class Uni {
|
||||
}
|
||||
}
|
||||
|
||||
private static UniClient buildClient() {
|
||||
private static UniClient buildClient(int retryInterval, int maxRetries) {
|
||||
UniClient.Builder builder = new UniClient.Builder(Uni.accessKeyId);
|
||||
|
||||
if (Uni.accessKeySecret != null) {
|
||||
@ -87,7 +87,8 @@ public class Uni {
|
||||
|
||||
builder.endpoint(Uni.endpoint);
|
||||
builder.signingAlgorithm(Uni.signingAlgorithm);
|
||||
|
||||
builder.setRetryInterval(retryInterval);
|
||||
builder.setMaxRetries(maxRetries);
|
||||
return builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,10 +3,10 @@ package org.dromara.sms4j.unisms.core;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.crypto.digest.HMac;
|
||||
import cn.hutool.crypto.digest.HmacAlgorithm;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsHttpUtil;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
@ -17,6 +17,7 @@ import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.UUID;
|
||||
|
||||
@Slf4j
|
||||
public class UniClient {
|
||||
|
||||
public static final String USER_AGENT = "uni-java-sdk" + "/" + Uni.VERSION;
|
||||
@ -24,12 +25,18 @@ public class UniClient {
|
||||
private final String accessKeySecret;
|
||||
private final String endpoint;
|
||||
private final String signingAlgorithm;
|
||||
private final int retryInterval;
|
||||
private final int maxRetries;
|
||||
private int retry = 0;
|
||||
private final SmsHttpUtil http = SmsHttpUtil.instance();
|
||||
|
||||
protected UniClient(Builder b) {
|
||||
this.accessKeyId = b.accessKeyId;
|
||||
this.accessKeySecret = b.accessKeySecret;
|
||||
this.endpoint = b.endpoint;
|
||||
this.signingAlgorithm = b.signingAlgorithm;
|
||||
this.retryInterval = b.retryInterval;
|
||||
this.maxRetries = b.maxRetries;
|
||||
}
|
||||
|
||||
private static String getSignature(final String message, final String secretKey) {
|
||||
@ -81,23 +88,37 @@ public class UniClient {
|
||||
public UniResponse request(final String action, final Map<String, Object> data) throws SmsBlendException {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("User-Agent", USER_AGENT);
|
||||
headers.put("Content-Type", "application/json;charset=utf-8");
|
||||
headers.put("Accept", "application/json");
|
||||
headers.put("Content-Type", Constant.APPLICATION_JSON_UTF8);
|
||||
headers.put("Accept", Constant.ACCEPT);
|
||||
|
||||
String url = this.endpoint + "?action=" + action + "&accessKeyId=" + this.accessKeyId;
|
||||
try(HttpResponse response = HttpRequest.post(url)
|
||||
.addHeaders(headers)
|
||||
.body(JSONUtil.toJsonStr(data))
|
||||
.execute()){
|
||||
return new UniResponse(JSONUtil.parseObj(response.body()));
|
||||
|
||||
try {
|
||||
UniResponse smsResponse = new UniResponse(http.postJson(url, headers, data));
|
||||
if("Success".equals(smsResponse.message) || retry == maxRetries){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(action, data);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(action, data);
|
||||
}
|
||||
}
|
||||
|
||||
private UniResponse requestRetry(String action, Map<String, Object> data) {
|
||||
http.safeSleep(retryInterval);
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return request(action, data);
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
private String accessKeyId;
|
||||
private String accessKeySecret;
|
||||
private String endpoint;
|
||||
private String signingAlgorithm;
|
||||
private int retryInterval;
|
||||
private int maxRetries;
|
||||
|
||||
public Builder(final String accessKeyId) {
|
||||
this.accessKeyId = accessKeyId;
|
||||
@ -123,6 +144,16 @@ public class UniClient {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setRetryInterval(int retryInterval) {
|
||||
this.retryInterval = retryInterval;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMaxRetries(int maxRetries) {
|
||||
this.maxRetries = maxRetries;
|
||||
return this;
|
||||
}
|
||||
|
||||
public UniClient build() {
|
||||
return new UniClient(this);
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ public class UniResponse {
|
||||
this.raw = body;
|
||||
this.data = body;
|
||||
}
|
||||
if (this.status != "400") {
|
||||
if (!"400".equals(this.status)) {
|
||||
String code = response.getStr("code");
|
||||
if (!"0".equals(code)) {
|
||||
this.message = response.getStr("message");
|
||||
|
||||
@ -1,20 +1,15 @@
|
||||
package org.dromara.sms4j.unisms.service;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.unisms.config.UniConfig;
|
||||
import org.dromara.sms4j.unisms.core.Uni;
|
||||
import org.dromara.sms4j.unisms.core.UniResponse;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
@ -25,57 +20,61 @@ import java.util.concurrent.Executor;
|
||||
* 2023/3/26 17:10
|
||||
**/
|
||||
@Slf4j
|
||||
public class UniSmsImpl extends AbstractSmsBlend {
|
||||
public class UniSmsImpl extends AbstractSmsBlend<UniConfig> {
|
||||
|
||||
private final UniConfig config;
|
||||
public static final String SUPPLIER = "unisms";
|
||||
|
||||
public UniSmsImpl(UniConfig config, Executor pool, DelayedTime delayed) {
|
||||
super(pool, delayed);
|
||||
this.config = config;
|
||||
super(config, pool, delayed);
|
||||
}
|
||||
|
||||
public UniSmsImpl(UniConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
if ("".equals(config.getTemplateId()) && "".equals(config.getTemplateName())) {
|
||||
if ("".equals(getConfig().getTemplateId()) && "".equals(getConfig().getTemplateName())) {
|
||||
throw new SmsBlendException("配置文件模板id和模板变量不能为空!");
|
||||
}
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
map.put(config.getTemplateName(), message);
|
||||
return sendMessage(phone, config.getTemplateId(), map);
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
map.put(getConfig().getTemplateName(), message);
|
||||
return sendMessage(phone, getConfig().getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
Map<String, Object> data = new LinkedHashMap<>(4);
|
||||
data.put("to", Collections.singletonList(phone));
|
||||
data.put("signature", config.getSignature());
|
||||
data.put("signature", getConfig().getSignature());
|
||||
data.put("templateId", templateId);
|
||||
data.put("templateData", messages);
|
||||
return getSmsResponse(data);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
if ("".equals(config.getTemplateId()) && "".equals(config.getTemplateName())) {
|
||||
if ("".equals(getConfig().getTemplateId()) && "".equals(getConfig().getTemplateName())) {
|
||||
throw new SmsBlendException("配置文件模板id和模板变量不能为空!");
|
||||
}
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
map.put(config.getTemplateName(), message);
|
||||
return massTexting(phones, config.getTemplateId(), map);
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
map.put(getConfig().getTemplateName(), message);
|
||||
return massTexting(phones, getConfig().getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
if (phones.size() > 1000) {
|
||||
throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于1000");
|
||||
}
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
Map<String, Object> data = new LinkedHashMap<>(4);
|
||||
data.put("to", phones);
|
||||
data.put("signature", config.getSignature());
|
||||
data.put("signature", getConfig().getSignature());
|
||||
data.put("templateId", templateId);
|
||||
data.put("templateData", messages);
|
||||
return getSmsResponse(data);
|
||||
@ -84,10 +83,10 @@ public class UniSmsImpl extends AbstractSmsBlend {
|
||||
private SmsResponse getSmsResponse(Map<String, Object> data) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
try {
|
||||
UniResponse send = Uni.getClient().request("sms.message.send", data);
|
||||
UniResponse send = Uni.getClient(getConfig().getRetryInterval(), getConfig().getMaxRetries()).request("sms.message.send", data);
|
||||
smsResponse.setSuccess("Success".equals(send.message));
|
||||
smsResponse.setData(send);
|
||||
smsResponse.setConfigId(this.config.getConfigId());
|
||||
smsResponse.setConfigId(getConfigId());
|
||||
} catch (Exception e) {
|
||||
smsResponse.setSuccess(false);
|
||||
}
|
||||
|
||||
@ -1,22 +1,16 @@
|
||||
package org.dromara.sms4j.yunpian.config;
|
||||
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
import org.dromara.sms4j.yunpian.service.YunPianSmsImpl;
|
||||
|
||||
public class YunPianFactory implements BaseProviderFactory<YunPianSmsImpl, YunpianConfig> {
|
||||
|
||||
private static YunPianSmsImpl yunpianSmsImpl;
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class YunPianFactory extends AbstractProviderFactory<YunPianSmsImpl, YunpianConfig> {
|
||||
|
||||
private static final YunPianFactory INSTANCE = new YunPianFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static YunpianConfig config = YunpianConfig.builder().build();
|
||||
}
|
||||
|
||||
private YunPianFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -30,40 +24,17 @@ public class YunPianFactory implements BaseProviderFactory<YunPianSmsImpl, Yunpi
|
||||
*/
|
||||
@Override
|
||||
public YunPianSmsImpl createSms(YunpianConfig yunpianConfig){
|
||||
if (yunpianSmsImpl == null){
|
||||
yunpianSmsImpl = createMultitonSms(yunpianConfig);
|
||||
}
|
||||
return yunpianSmsImpl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public YunPianSmsImpl createMultitonSms(YunpianConfig yunpianConfig) {
|
||||
return new YunPianSmsImpl(yunpianConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
}
|
||||
|
||||
/** 刷新对象*/
|
||||
@Override
|
||||
public YunPianSmsImpl refresh(YunpianConfig yunpianConfig){
|
||||
yunpianSmsImpl = new YunPianSmsImpl(yunpianConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
return yunpianSmsImpl;
|
||||
return new YunPianSmsImpl(yunpianConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public YunpianConfig getConfig() {
|
||||
return ConfigHolder.config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(YunpianConfig config) {
|
||||
ConfigHolder.config = config;
|
||||
public String getSupplier() {
|
||||
return YunPianSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,14 +5,12 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
import org.dromara.sms4j.yunpian.service.YunPianSmsImpl;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
public class YunpianConfig extends BaseConfig implements SupplierConfig {
|
||||
public class YunpianConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 短信发送后将向这个地址推送(运营商返回的)发送报告
|
||||
@ -24,4 +22,14 @@ public class YunpianConfig extends BaseConfig implements SupplierConfig {
|
||||
*/
|
||||
private String templateName;
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return YunPianSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
package org.dromara.sms4j.yunpian.service;
|
||||
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.yunpian.config.YunpianConfig;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -22,14 +19,24 @@ import java.util.concurrent.Executor;
|
||||
/**
|
||||
* @author wind
|
||||
*/
|
||||
public class YunPianSmsImpl extends AbstractSmsBlend {
|
||||
@Slf4j
|
||||
public class YunPianSmsImpl extends AbstractSmsBlend<YunpianConfig> {
|
||||
|
||||
public static final String SUPPLIER = "yunpian";
|
||||
private int retry = 0;
|
||||
|
||||
public YunPianSmsImpl(YunpianConfig config, Executor pool, DelayedTime delayed) {
|
||||
super(pool, delayed);
|
||||
this.config = config;
|
||||
super(config, pool, delayed);
|
||||
}
|
||||
|
||||
private final YunpianConfig config;
|
||||
public YunPianSmsImpl(YunpianConfig config) {
|
||||
super(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
private SmsResponse getResponse(JSONObject execute) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
@ -39,26 +46,59 @@ public class YunPianSmsImpl extends AbstractSmsBlend {
|
||||
}
|
||||
smsResponse.setSuccess(execute.getInt("code") == 0);
|
||||
smsResponse.setData(execute);
|
||||
smsResponse.setConfigId(this.config.getConfigId());
|
||||
smsResponse.setConfigId(getConfigId());
|
||||
return smsResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
Map<String, String> body = setBody(phone, message, null, config.getTemplateId());
|
||||
return getSendResponse(body);
|
||||
Map<String, Object> body = setBody(phone, message, null, getConfig().getTemplateId());
|
||||
Map<String, String> headers = getHeaders();
|
||||
|
||||
try {
|
||||
SmsResponse smsResponse = getResponse(http.postFrom(Constant.YUNPIAN_URL + "/sms/tpl_single_send.json", headers, body));
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(phone, message);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(phone, message);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(String phone, String message) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return sendMessage(phone, message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
Map<String, String> body = setBody(phone, "", messages, templateId);
|
||||
return getSendResponse(body);
|
||||
Map<String, Object> body = setBody(phone, "", messages, templateId);
|
||||
Map<String, String> headers = getHeaders();
|
||||
|
||||
try {
|
||||
SmsResponse smsResponse = getResponse(http.postFrom(Constant.YUNPIAN_URL + "/sms/tpl_single_send.json", headers, body));
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(phone, templateId, messages);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(phone, templateId, messages);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return sendMessage(phone, templateId, messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
if (phones.size() > 1000) {
|
||||
throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于1000");
|
||||
@ -67,7 +107,6 @@ public class YunPianSmsImpl extends AbstractSmsBlend {
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
if (phones.size() > 1000) {
|
||||
throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于1000");
|
||||
@ -88,38 +127,27 @@ public class YunPianSmsImpl extends AbstractSmsBlend {
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
private Map<String, String> setBody(String phone, String mes, LinkedHashMap<String, String> messages, String tplId) {
|
||||
private Map<String, Object> setBody(String phone, String mes, LinkedHashMap<String, String> messages, String tplId) {
|
||||
LinkedHashMap<String, String> message = new LinkedHashMap<>();
|
||||
if (mes.isEmpty()) {
|
||||
message = messages;
|
||||
} else {
|
||||
message.put(config.getTemplateName(), mes);
|
||||
message.put(getConfig().getTemplateName(), mes);
|
||||
}
|
||||
Map<String, String> body = new HashMap<>();
|
||||
body.put("apikey", config.getAccessKeyId());
|
||||
Map<String, Object> body = new HashMap<>();
|
||||
body.put("apikey", getConfig().getAccessKeyId());
|
||||
body.put("mobile", phone);
|
||||
body.put("tpl_id", tplId);
|
||||
body.put("tpl_value", formattingMap(message));
|
||||
if (config.getCallbackUrl() != null && !config.getCallbackUrl().isEmpty())
|
||||
body.put("callback_url", config.getCallbackUrl());
|
||||
if (getConfig().getCallbackUrl() != null && !getConfig().getCallbackUrl().isEmpty())
|
||||
body.put("callback_url", getConfig().getCallbackUrl());
|
||||
return body;
|
||||
}
|
||||
|
||||
private Map<String, String> getHeaders() {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Accept", "application/json;charset=utf-8");
|
||||
headers.put("Accept", Constant.APPLICATION_JSON_UTF8);
|
||||
headers.put("Content-Type", Constant.FROM_URLENCODED);
|
||||
return headers;
|
||||
}
|
||||
|
||||
private SmsResponse getSendResponse(Map<String, String> body) {
|
||||
Map<String, String> headers = getHeaders();
|
||||
try(HttpResponse response = HttpRequest.post(Constant.YUNPIAN_URL + "/sms/tpl_single_send.json")
|
||||
.addHeaders(headers)
|
||||
.body(JSONUtil.toJsonStr(body))
|
||||
.execute()){
|
||||
JSONObject res = JSONUtil.parseObj(response.body());
|
||||
return getResponse(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,9 @@
|
||||
package org.dromara.sms4j.zhutong.config;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.*;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||
import org.dromara.sms4j.zhutong.service.ZhutongSmsImpl;
|
||||
|
||||
/**
|
||||
* 助通-自定义短信发送-配置
|
||||
@ -19,12 +15,9 @@ import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
* 说明4:templateId ====> 模板id可以为空,为空发送【自定义短信】无需要提前创建短信模板; 不为空发送:【模板短信】
|
||||
* 说明4:templateName ====> 模板变量名称可以为空,为空发送【自定义短信】无需要提前创建短信模板; 不为空发送:【模板短信】
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@NoArgsConstructor
|
||||
public class ZhutongConfig extends BaseConfig implements SupplierConfig {
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@Data
|
||||
public class ZhutongConfig extends BaseConfig {
|
||||
/**
|
||||
* 模板变量名称
|
||||
* 查看地址:https://mix2.zthysms.com/index.html#/TemplateManagement
|
||||
@ -35,6 +28,16 @@ public class ZhutongConfig extends BaseConfig implements SupplierConfig {
|
||||
* 默认请求地址
|
||||
* 不同区域,可切换请求地址,也可以不修改,请参考官方文档:https://doc.zthysms.com/web/#/1/236
|
||||
*/
|
||||
@Builder.Default
|
||||
private String requestUrl = "https://api.mix2.zthysms.com/";
|
||||
|
||||
/**
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return ZhutongSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,21 +1,15 @@
|
||||
package org.dromara.sms4j.zhutong.config;
|
||||
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.provider.base.BaseProviderFactory;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
import org.dromara.sms4j.zhutong.service.ZhutongSmsImpl;
|
||||
|
||||
public class ZhutongFactory implements BaseProviderFactory<ZhutongSmsImpl, ZhutongConfig> {
|
||||
private static ZhutongSmsImpl ZhutongSmsImpl;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class ZhutongFactory extends AbstractProviderFactory<ZhutongSmsImpl, ZhutongConfig> {
|
||||
private static final ZhutongFactory INSTANCE = new ZhutongFactory();
|
||||
|
||||
private static final class ConfigHolder {
|
||||
private static ZhutongConfig config = ZhutongConfig.builder().build();
|
||||
}
|
||||
|
||||
private ZhutongFactory() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取建造者实例
|
||||
* @return 建造者实例
|
||||
@ -28,40 +22,18 @@ public class ZhutongFactory implements BaseProviderFactory<ZhutongSmsImpl, Zhuto
|
||||
* 建造一个助通短信实现
|
||||
*/
|
||||
@Override
|
||||
public ZhutongSmsImpl createSms(ZhutongConfig ZhutongConfig){
|
||||
if (ZhutongSmsImpl == null){
|
||||
ZhutongSmsImpl = createMultitonSms(ZhutongConfig);
|
||||
}
|
||||
return ZhutongSmsImpl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ZhutongSmsImpl createMultitonSms(ZhutongConfig zhutongConfig) {
|
||||
return new ZhutongSmsImpl(zhutongConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
}
|
||||
|
||||
/** 刷新对象*/
|
||||
@Override
|
||||
public ZhutongSmsImpl refresh(ZhutongConfig zhutongConfig){
|
||||
ZhutongSmsImpl = new ZhutongSmsImpl(zhutongConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
|
||||
return ZhutongSmsImpl;
|
||||
public ZhutongSmsImpl createSms(ZhutongConfig zhutongConfig){
|
||||
return new ZhutongSmsImpl(zhutongConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
* @return 配置对象
|
||||
* 获取供应商
|
||||
*
|
||||
* @since 3.0.0
|
||||
*/
|
||||
@Override
|
||||
public ZhutongConfig getConfig() {
|
||||
return ZhutongFactory.ConfigHolder.config;
|
||||
public String getSupplier() {
|
||||
return ZhutongSmsImpl.SUPPLIER;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置
|
||||
* @param config 配置对象
|
||||
*/
|
||||
@Override
|
||||
public void setConfig(ZhutongConfig config) {
|
||||
ZhutongFactory.ConfigHolder.config = config;
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,18 +5,14 @@ import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.text.StrPool;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.http.HttpRequest;
|
||||
import cn.hutool.http.HttpResponse;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||
import org.dromara.sms4j.zhutong.config.ZhutongConfig;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -31,53 +27,64 @@ import java.util.concurrent.Executor;
|
||||
* <p>2. 模板短信发送 需定义模板 https://doc.zthysms.com/web/#/1/13
|
||||
*/
|
||||
@Slf4j
|
||||
public class ZhutongSmsImpl extends AbstractSmsBlend {
|
||||
public class ZhutongSmsImpl extends AbstractSmsBlend<ZhutongConfig> {
|
||||
|
||||
private final ZhutongConfig config;
|
||||
public static final String SUPPLIER = "zhutong";
|
||||
private int retry = 0;
|
||||
|
||||
/**
|
||||
* ZhutongSmsImpl
|
||||
* <p>构造器,用于构造短信实现模块
|
||||
*/
|
||||
public ZhutongSmsImpl(ZhutongConfig zhutongConfig, Executor pool, DelayedTime delayedTime) {
|
||||
super(pool, delayedTime);
|
||||
this.config = zhutongConfig;
|
||||
super(zhutongConfig, pool, delayedTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* ZhutongSmsImpl
|
||||
* <p>构造器,用于构造短信实现模块
|
||||
*/
|
||||
public ZhutongSmsImpl(ZhutongConfig zhutongConfig) {
|
||||
super(zhutongConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSupplier() {
|
||||
return SUPPLIER;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
ZhutongConfig config = getConfig();
|
||||
//如果模板id为空 or 模板变量名称为空,使用无模板的自定义短信发送
|
||||
if (StrUtil.hasBlank(config.getSignature(), config.getTemplateId(), config.getTemplateName())) {
|
||||
return getSmsResponse(phone, message);
|
||||
}
|
||||
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
map.put(config.getTemplateName(), message);
|
||||
return sendMessage(phone, config.getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
return getSmsResponseTemplate(templateId, phone, messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
ZhutongConfig config = getConfig();
|
||||
//如果模板id为空 or 模板变量名称为空,使用无模板的自定义短信发送
|
||||
if (StrUtil.hasBlank(config.getSignature(), config.getTemplateId(), config.getTemplateName())) {
|
||||
return getSmsResponse(phones, message);
|
||||
}
|
||||
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
|
||||
map.put(config.getTemplateName(), message);
|
||||
return massTexting(phones, config.getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
return getSmsResponseTemplate(templateId, phones, messages);
|
||||
}
|
||||
@ -86,6 +93,7 @@ public class ZhutongSmsImpl extends AbstractSmsBlend {
|
||||
* 发送 自定义短信:https://doc.zthysms.com/web/#/1/14
|
||||
*/
|
||||
protected SmsResponse getSmsResponse(List<String> phones, String content) {
|
||||
ZhutongConfig config = getConfig();
|
||||
String requestUrl = config.getRequestUrl();
|
||||
String username = config.getAccessKeyId();
|
||||
String password = config.getAccessKeySecret();
|
||||
@ -110,7 +118,7 @@ public class ZhutongSmsImpl extends AbstractSmsBlend {
|
||||
|
||||
String url = requestUrl + "v2/sendSms";
|
||||
long tKey = System.currentTimeMillis() / 1000;
|
||||
Map<String, String> json = new HashMap<>(5);
|
||||
Map<String, Object> json = new HashMap<>(5);
|
||||
//账号
|
||||
json.put("username", username);
|
||||
//密码
|
||||
@ -122,15 +130,27 @@ public class ZhutongSmsImpl extends AbstractSmsBlend {
|
||||
//内容
|
||||
json.put("content", content);
|
||||
|
||||
try(HttpResponse response = HttpRequest.post(url)
|
||||
.header("Content-Type", Constant.APPLICATION_JSON_UTF8)
|
||||
.body(JSONUtil.toJsonStr(json))
|
||||
.execute()){
|
||||
JSONObject body = JSONUtil.parseObj(response.body());
|
||||
return this.getResponse(body);
|
||||
try {
|
||||
Map<String, String> headers = new LinkedHashMap<>(1);
|
||||
headers.put("Content-Type", Constant.APPLICATION_JSON_UTF8);
|
||||
SmsResponse smsResponse = getResponse(http.postJson(requestUrl, headers, json));
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(phones, content);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(phones, content);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(List<String> phones, String content) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return getSmsResponse(phones, content);
|
||||
}
|
||||
|
||||
protected SmsResponse getSmsResponse(String mobile, String content) {
|
||||
return getSmsResponse(ListUtil.of(mobile), content);
|
||||
}
|
||||
@ -139,6 +159,7 @@ public class ZhutongSmsImpl extends AbstractSmsBlend {
|
||||
* 发送 模板短信:https://doc.zthysms.com/web/#/1/13
|
||||
*/
|
||||
protected SmsResponse getSmsResponseTemplate(String templateId, List<String> phones, LinkedHashMap<String, String> messages) {
|
||||
ZhutongConfig config = getConfig();
|
||||
String requestUrl = config.getRequestUrl();
|
||||
String username = config.getAccessKeyId();
|
||||
String password = config.getAccessKeySecret();
|
||||
@ -194,15 +215,27 @@ public class ZhutongSmsImpl extends AbstractSmsBlend {
|
||||
}
|
||||
requestJson.set("records", records);
|
||||
|
||||
try(HttpResponse response = HttpRequest.post(url)
|
||||
.header("Content-Type", Constant.APPLICATION_JSON_UTF8)
|
||||
.body(requestJson.toString())
|
||||
.execute()){
|
||||
JSONObject body = JSONUtil.parseObj(response.body());
|
||||
return this.getResponse(body);
|
||||
try {
|
||||
Map<String, String> headers = new LinkedHashMap<>(1);
|
||||
headers.put("Content-Type", Constant.APPLICATION_JSON_UTF8);
|
||||
SmsResponse smsResponse = getResponse(http.postJson(requestUrl, headers, requestJson.toString()));
|
||||
if(smsResponse.isSuccess() || retry == getConfig().getMaxRetries()){
|
||||
retry = 0;
|
||||
return smsResponse;
|
||||
}
|
||||
return requestRetry(templateId, phones, messages);
|
||||
}catch (SmsBlendException e){
|
||||
return requestRetry(templateId, phones, messages);
|
||||
}
|
||||
}
|
||||
|
||||
private SmsResponse requestRetry(String templateId, List<String> phones, LinkedHashMap<String, String> messages) {
|
||||
http.safeSleep(getConfig().getRetryInterval());
|
||||
retry++;
|
||||
log.warn("短信第 {" + retry + "} 次重新发送");
|
||||
return getSmsResponseTemplate(templateId, phones, messages);
|
||||
}
|
||||
|
||||
protected SmsResponse getSmsResponseTemplate(String templateId, String mobile, LinkedHashMap<String, String> content) {
|
||||
return getSmsResponseTemplate(templateId, ListUtil.of(mobile), content);
|
||||
}
|
||||
@ -211,7 +244,7 @@ public class ZhutongSmsImpl extends AbstractSmsBlend {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setSuccess(jsonObject.getInt("code", -1) <= 200);
|
||||
smsResponse.setData(jsonObject);
|
||||
smsResponse.setConfigId(this.config.getConfigId());
|
||||
smsResponse.setConfigId(getConfigId());
|
||||
return smsResponse;
|
||||
}
|
||||
|
||||
|
||||
@ -1,53 +1,55 @@
|
||||
package org.dromara.sms4j.solon.aop;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.smsProxy.RestrictedProcess;
|
||||
import org.dromara.sms4j.api.universal.SmsRedisUtil;
|
||||
import org.dromara.sms4j.comm.config.SmsConfig;
|
||||
import org.dromara.sms4j.api.dao.SmsDao;
|
||||
import org.dromara.sms4j.api.proxy.RestrictedProcess;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||
import org.dromara.sms4j.provider.factory.BeanFactory;
|
||||
import org.noear.solon.core.AopContext;
|
||||
|
||||
@Slf4j
|
||||
public class SolonRestrictedProcess extends RestrictedProcess {
|
||||
public class SolonRestrictedProcess implements RestrictedProcess {
|
||||
|
||||
private SmsRedisUtil redis;
|
||||
private static final Long minTimer = 60 * 1000L;
|
||||
private static final Long accTimer = 24 * 60 * 60 * 1000L;
|
||||
private static final String REDIS_KEY = "sms:restricted:";
|
||||
private SmsDao smsDao;
|
||||
|
||||
public SolonRestrictedProcess(AopContext context){
|
||||
context.getBeanAsync(SmsRedisUtil.class, bean->{
|
||||
redis = bean;
|
||||
public SolonRestrictedProcess(AopContext context) {
|
||||
context.getBeanAsync(SmsDao.class, bean -> {
|
||||
smsDao = bean;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public SmsBlendException process(SmsConfig config, String args) {
|
||||
Integer accountMax = config.getAccountMax();//每日最大发送量
|
||||
Integer minuteMax = config.getMinuteMax();//每分钟最大发送量
|
||||
if (SmsUtil.isNotEmpty(accountMax)) { //是否配置了每日限制
|
||||
Integer i = (Integer) redis.getByKey(REDIS_KEY+args + "max");
|
||||
public SmsBlendException process(String phone) {
|
||||
SmsConfig config = BeanFactory.getSmsConfig();
|
||||
Integer accountMax = config.getAccountMax(); // 每日最大发送量
|
||||
Integer minuteMax = config.getMinuteMax(); // 每分钟最大发送量
|
||||
if (SmsUtil.isNotEmpty(accountMax)) { // 是否配置了每日限制
|
||||
Integer i = (Integer) smsDao.get(REDIS_KEY + phone + "max");
|
||||
if (SmsUtil.isEmpty(i)) {
|
||||
redis.setOrTime(REDIS_KEY+args + "max", 1,accTimer/1000);
|
||||
} else if (i > accountMax) {
|
||||
log.info("The phone:"+args +",number of short messages reached the maximum today");
|
||||
return new SmsBlendException("The phone:"+args +",number of short messages reached the maximum today");
|
||||
smsDao.set(REDIS_KEY + phone + "max", 1, accTimer / 1000);
|
||||
} else if (i >= accountMax) {
|
||||
log.info("The phone:" + phone + ",number of short messages reached the maximum today");
|
||||
return new SmsBlendException("The phone:" + phone + ",number of short messages reached the maximum today");
|
||||
} else {
|
||||
redis.setOrTime(REDIS_KEY+args + "max", i + 1,accTimer/1000);
|
||||
smsDao.set(REDIS_KEY + phone + "max", i + 1, accTimer / 1000);
|
||||
}
|
||||
}
|
||||
if (SmsUtil.isNotEmpty(minuteMax)) { //是否配置了每分钟最大限制
|
||||
Integer o = (Integer) redis.getByKey(REDIS_KEY+args);
|
||||
if (SmsUtil.isNotEmpty(minuteMax)) { // 是否配置了每分钟最大限制
|
||||
Integer o = (Integer) smsDao.get(REDIS_KEY + phone);
|
||||
if (SmsUtil.isNotEmpty(o)) {
|
||||
if (o < minuteMax) {
|
||||
redis.setOrTime(REDIS_KEY+args, o + 1,minTimer/1000);
|
||||
smsDao.set(REDIS_KEY + phone, o + 1, minTimer / 1000);
|
||||
} else {
|
||||
log.info("The phone:"+args +",number of short messages reached the maximum today");
|
||||
return new SmsBlendException("The phone:", args + " Text messages are sent too often!");
|
||||
log.info("The phone:" + phone + ",number of short messages reached the maximum today");
|
||||
return new SmsBlendException("The phone:", phone + " Text messages are sent too often!");
|
||||
}
|
||||
} else {
|
||||
redis.setOrTime(REDIS_KEY+args, 1,minTimer/1000);
|
||||
smsDao.set(REDIS_KEY + phone, 1, minTimer / 1000);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
@ -1,20 +1,19 @@
|
||||
package org.dromara.sms4j.solon.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.smsProxy.SmsInvocationHandler;
|
||||
import org.dromara.sms4j.api.universal.SmsRedisUtil;
|
||||
import org.dromara.sms4j.comm.config.SmsBanner;
|
||||
import org.dromara.sms4j.comm.config.SmsConfig;
|
||||
import org.dromara.sms4j.comm.config.SmsSqlConfig;
|
||||
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
|
||||
import org.dromara.sms4j.provider.config.SmsBanner;
|
||||
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.core.SupplierSqlConfig;
|
||||
import org.dromara.sms4j.provider.factory.BeanFactory;
|
||||
import org.dromara.sms4j.solon.aop.SolonRestrictedProcess;
|
||||
import org.dromara.sms4j.solon.utils.SmsRedisUtils;
|
||||
import org.noear.solon.Solon;
|
||||
import org.noear.solon.Utils;
|
||||
import org.noear.solon.annotation.*;
|
||||
import org.noear.solon.annotation.Bean;
|
||||
import org.noear.solon.annotation.Condition;
|
||||
import org.noear.solon.annotation.Configuration;
|
||||
import org.noear.solon.annotation.Inject;
|
||||
import org.noear.solon.core.AopContext;
|
||||
import org.noear.solon.core.Props;
|
||||
import org.noear.solon.core.bean.LifecycleBean;
|
||||
@ -34,11 +33,6 @@ public class SmsAutowiredConfig implements LifecycleBean {
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SmsSqlConfig smsSqlConfig() {
|
||||
return injectObj("sms.sql", BeanFactory.getSmsSqlConfig());
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SmsConfig smsConfig() {
|
||||
return injectObj("sms", BeanFactory.getSmsConfig());
|
||||
@ -70,21 +64,17 @@ public class SmsAutowiredConfig implements LifecycleBean {
|
||||
return new SupplierConfig();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Condition(onProperty = "${sms.config-type}=sql_config")
|
||||
public SupplierSqlConfig supplierSqlConfig(@Inject SmsSqlConfig smsSqlConfig) {
|
||||
return new SupplierSqlConfig();
|
||||
}
|
||||
// @Bean
|
||||
// @Condition(onProperty = "${sms.config-type}=sql_config")
|
||||
// public SupplierSqlConfig supplierSqlConfig(@Inject SmsSqlConfig smsSqlConfig) {
|
||||
// return new SupplierSqlConfig();
|
||||
// }
|
||||
|
||||
//是在 solon 容器扫描完成之后执行的
|
||||
@Override
|
||||
public void start() throws Throwable {
|
||||
/* 如果配置中启用了redis,则注入redis工具*/
|
||||
if (BeanFactory.getSmsConfig().getRedisCache()) {
|
||||
//如果容器中不存在一个已经实现的redisUtil则自己注入一个
|
||||
if (!Solon.context().hasWrap(SmsRedisUtil.class)) {
|
||||
Solon.context().wrapAndPut(SmsRedisUtils.class);
|
||||
}
|
||||
SmsInvocationHandler.setRestrictedProcess(new SolonRestrictedProcess(aopContext));
|
||||
log.debug("The redis cache is enabled for sms4j");
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package org.dromara.sms4j.solon.config;
|
||||
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenConfig;
|
||||
import org.dromara.sms4j.core.config.SupplierFactory;
|
||||
import org.dromara.sms4j.ctyun.config.CtyunConfig;
|
||||
import org.dromara.sms4j.emay.config.EmayConfig;
|
||||
import org.dromara.sms4j.huawei.config.HuaweiConfig;
|
||||
@ -29,43 +28,43 @@ public class SupplierConfig {
|
||||
/** 阿里差异化配置*/
|
||||
@Bean
|
||||
public AlibabaConfig alibabaConfig(){
|
||||
return injectObj("sms.alibaba", SupplierFactory.getAlibabaConfig());
|
||||
return injectObj("sms.alibaba", new AlibabaConfig());
|
||||
}
|
||||
|
||||
/** 华为差异化配置*/
|
||||
@Bean
|
||||
public HuaweiConfig huaweiConfig(){
|
||||
return injectObj("sms.huawei", SupplierFactory.getHuaweiConfig());
|
||||
return injectObj("sms.huawei", new HuaweiConfig());
|
||||
}
|
||||
|
||||
/** 云片短信差异化配置*/
|
||||
@Bean
|
||||
public YunpianConfig yunpianConfig(){
|
||||
return injectObj("sms.yunpian", SupplierFactory.getYunpianConfig());
|
||||
return injectObj("sms.yunpian", new YunpianConfig());
|
||||
}
|
||||
|
||||
/** 合一短信差异化配置*/
|
||||
@Bean
|
||||
public UniConfig uniConfig(){
|
||||
return injectObj("sms.uni", SupplierFactory.getUniConfig());
|
||||
return injectObj("sms.uni", new UniConfig());
|
||||
}
|
||||
|
||||
/** 腾讯短信差异化配置*/
|
||||
@Bean
|
||||
public TencentConfig tencentConfig(){
|
||||
return injectObj("sms.tencent", SupplierFactory.getTencentConfig());
|
||||
return injectObj("sms.tencent", new TencentConfig());
|
||||
}
|
||||
|
||||
/** 京东云短信差异化配置 */
|
||||
@Bean
|
||||
public JdCloudConfig jdCloudConfig(){
|
||||
return injectObj("sms.jdcloud", SupplierFactory.getJdCloudConfig());
|
||||
return injectObj("sms.jdcloud", new JdCloudConfig());
|
||||
}
|
||||
|
||||
/** 容联云短信差异化配置 */
|
||||
@Bean
|
||||
public CloopenConfig cloopenConfig(){
|
||||
return injectObj("sms.cloopen", SupplierFactory.getCloopenConfig());
|
||||
return injectObj("sms.cloopen", new CloopenConfig());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -73,12 +72,12 @@ public class SupplierConfig {
|
||||
*/
|
||||
@Bean
|
||||
public EmayConfig emayConfig(){
|
||||
return injectObj("sms.emay", SupplierFactory.getEmayConfig());
|
||||
return injectObj("sms.emay", new EmayConfig());
|
||||
}
|
||||
|
||||
/** 天翼云短信差异化配置 */
|
||||
@Bean
|
||||
public CtyunConfig ctyunConfig(){
|
||||
return injectObj("sms.ctyun", SupplierFactory.getCtyunConfig());
|
||||
return injectObj("sms.ctyun", new CtyunConfig());
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
package org.dromara.sms4j.solon.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.dao.SmsDao;
|
||||
import org.noear.solon.Solon;
|
||||
import org.redisson.api.RedissonClient;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
public class SmsRedisDaoImpl implements SmsDao {
|
||||
|
||||
private RedissonClient redisTemplate;
|
||||
|
||||
public SmsRedisDaoImpl() {
|
||||
Thread t = new Thread(()->{
|
||||
//如果获取到的bean为null则等待后重试,最多重试五次
|
||||
for(int i = 0; i < 5 ;i++){
|
||||
RedissonClient bean = Solon.context().getBean(RedissonClient.class);
|
||||
if (Objects.isNull(bean)){
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}else{
|
||||
redisTemplate = bean;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
public SmsRedisDaoImpl(RedissonClient redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, Object value, long cacheTime) {
|
||||
redisTemplate.getBucket(key).set(value, cacheTime, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(String key, Object value) {
|
||||
redisTemplate.getBucket(key).set(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object get(String key) {
|
||||
return redisTemplate.getBucket(key).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:将Map中的数据批量放置到redis中
|
||||
* <p>
|
||||
*
|
||||
* @param valueMap 要放入的数据
|
||||
* @name: multiSet
|
||||
* @author :Wind
|
||||
*/
|
||||
public boolean multiSet(Map valueMap) {
|
||||
try {
|
||||
valueMap.forEach((key, val) -> redisTemplate.getBucket((String) key).set(val));
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clean() throws RuntimeException {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
@ -1,466 +0,0 @@
|
||||
package org.dromara.sms4j.solon.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.universal.SmsRedisUtil;
|
||||
import org.noear.solon.Solon;
|
||||
import org.redisson.api.RedissonClient;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
public class SmsRedisUtils implements SmsRedisUtil {
|
||||
|
||||
private RedissonClient redisTemplate;
|
||||
|
||||
public SmsRedisUtils() {
|
||||
Thread t = new Thread(()->{
|
||||
//如果获取到的bean为null则等待后重试,最多重试五次
|
||||
for(int i = 0; i < 5 ;i++){
|
||||
RedissonClient bean = Solon.context().getBean(RedissonClient.class);
|
||||
if (Objects.isNull(bean)){
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}else{
|
||||
redisTemplate = bean;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
public SmsRedisUtils(RedissonClient redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:设置redis的key的到期时间
|
||||
*
|
||||
* @param key redis的key
|
||||
* @param time 到期时间
|
||||
* @name: setTimeByKey
|
||||
* @author :Wind
|
||||
*/
|
||||
public boolean setTimeByKey(String key, Long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.getBucket(key).expire(time, TimeUnit.SECONDS);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:放入redis
|
||||
*
|
||||
* @param key 要放入的key
|
||||
* @param value 要放入的value
|
||||
* @name: set
|
||||
* @author :Wind
|
||||
*/
|
||||
public boolean set(String key, Object value) {
|
||||
redisTemplate.getBucket(key).set(value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:放入带过期时间的缓存
|
||||
*
|
||||
* @param time 到期时间(秒)
|
||||
* @name: setOrTime
|
||||
* @author :Wind
|
||||
*/
|
||||
public boolean setOrTime(String key, Object value, Long time) {
|
||||
try {
|
||||
redisTemplate.getBucket(key).set(value, time, TimeUnit.SECONDS);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:将Map中的数据批量放置到redis中
|
||||
* <p>
|
||||
*
|
||||
* @param valueMap 要放入的数据
|
||||
* @name: multiSet
|
||||
* @author :Wind
|
||||
*/
|
||||
public boolean multiSet(Map valueMap) {
|
||||
try {
|
||||
valueMap.forEach((key, val) -> redisTemplate.getBucket((String) key).set(val));
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:获取key对应的值
|
||||
*
|
||||
* @param key 要查询的key
|
||||
* @name: getByKey
|
||||
* @author :Wind
|
||||
*/
|
||||
public Object getByKey(String key) {
|
||||
return redisTemplate.getBucket(key).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:获取字符串型值
|
||||
*
|
||||
* @param
|
||||
* @name: getKyeString
|
||||
* @author :Wind
|
||||
*/
|
||||
public String getKyeString(String key) {
|
||||
return (String) getByKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:判断key是否存在
|
||||
*
|
||||
* @param key 要判断的key
|
||||
* @name: hasKey
|
||||
* @author :Wind
|
||||
*/
|
||||
public Boolean hasKey(String key) {
|
||||
return redisTemplate.getBucket(key).isExists();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 说明:根据key删除redis缓存可以批量删除
|
||||
// *
|
||||
// * @param key 要删除的key
|
||||
// * @name: deleteKey
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Boolean deleteKey(String... key) {
|
||||
// if (key != null && key.length > 0) {
|
||||
// if (key.length == 1) {
|
||||
// return redisTemplate.getBucket(key[0]).delete();
|
||||
// } else {
|
||||
// Long delete = redisTemplate.(Arrays.asList(key));
|
||||
// return delete >= 1L;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// public Boolean delete(String key) {
|
||||
// Set<String> keys = redisTemplate.keys(key + "*");
|
||||
// redisTemplate.removeByKeys(keys);
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * 根据key 获取key的过期时间
|
||||
// *
|
||||
// * @param key 键 不能为null
|
||||
// * @return 时间(秒) 返回-1, 代表为永久有效
|
||||
// */
|
||||
// public Long getKeyExpire(String key) {
|
||||
// return redisTemplate.ttl(key);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 修改redis中key的名称
|
||||
// *
|
||||
// * @param oldKey 旧的key值
|
||||
// * @param newKey 新的key值
|
||||
// */
|
||||
// public void renameKey(String oldKey, String newKey) {
|
||||
// redisTemplate..ren(oldKey, newKey);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * <p>说明:将map对象存入redis
|
||||
// * <p>
|
||||
// *
|
||||
// * @param map 要存入redis中的map
|
||||
// * @name: setMap
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public <T, M> void MapSetMap(String key, Map<T, M> map) {
|
||||
// redisClient.getHash(key).putAll(map);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * <p>说明:获取所有hash表中字段
|
||||
// * <p>
|
||||
// *
|
||||
// * @param
|
||||
// * @name: getMapByKey
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Set<Object> MapGetHashByKey(String key) {
|
||||
// return redisTemplate.opsForHash().keys(key);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * <p>说明:根据key和fieId获取对应的值
|
||||
// * <p>
|
||||
// *
|
||||
// * @param fieId hash中的fieId也是Map的Key
|
||||
// * @name: getValueByFieID
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Object MapGetValueByFieID(String key, String fieId) {
|
||||
// return redisTemplate.opsForHash().get(key, fieId);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * <p>说明:根据key获取所有的键值对
|
||||
// * <p>
|
||||
// *
|
||||
// * @param key redis中的key
|
||||
// * @name: getMapByKey
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Map<Object, Object> MapGetMapByKey(String key) {
|
||||
// return redisTemplate.opsForHash().entries(key);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * <p>说明:向key中添加一对新的键值对
|
||||
// * <p>
|
||||
// *
|
||||
// * @param hashKey 键值对的key
|
||||
// * @param value 键值对的value
|
||||
// * @name: setNewMapValue
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public void MapSetNewMapValue(String key, String hashKey, Object value) {
|
||||
// redisTemplate.opsForHash().put(key, hashKey, value);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * <p>说明:根据key和field删除数据
|
||||
// * <p>
|
||||
// *
|
||||
// * @param fields 要删除的fields
|
||||
// * @return Long 影响的条数
|
||||
// * @name: hashDelete
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Long MapHashDelete(String key, Object... fields) {
|
||||
// return redisTemplate.opsForHash().delete(key, fields);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * <p>说明:查看key下存了多少条键值对
|
||||
// * <p>
|
||||
// *
|
||||
// * @param key redis的key
|
||||
// * @name: getMapValueSize
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Long MapGetMapValueSize(String key) {
|
||||
// return redisTemplate.opsForHash().size(key);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 设置值到List中的头部
|
||||
// *
|
||||
// * @param key
|
||||
// * @param value
|
||||
// * @return
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Boolean listAddInHead(String key, Object value) {
|
||||
// try {
|
||||
// redisTemplate.opsForList().leftPush(key, value);
|
||||
// return true;
|
||||
// } catch (Exception e) {
|
||||
// log.error(e.getMessage());
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 批量设置值到List中的头部
|
||||
// *
|
||||
// * @param key List名字
|
||||
// * @param values
|
||||
// * @return
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Boolean listAddAllInHead(String key, Collection<?> values) {
|
||||
// try {
|
||||
// redisTemplate.opsForList().leftPushAll(key, values);
|
||||
// return true;
|
||||
// } catch (Exception e) {
|
||||
// log.error(e.getMessage());
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 如果存在List->key, 则设置值到List中的头部
|
||||
// *
|
||||
// * @param key List名字
|
||||
// * @param value
|
||||
// * @return
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Boolean listAddIfPresent(String key, Object value) {
|
||||
// try {
|
||||
// redisTemplate.opsForList().leftPushIfPresent(key, value);
|
||||
// return true;
|
||||
// } catch (Exception e) {
|
||||
// log.error(e.getMessage());
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 设置值到List中的尾部
|
||||
// *
|
||||
// * @param key List名字
|
||||
// * @param value 值
|
||||
// * @return
|
||||
// */
|
||||
// public Boolean listAddInEnd(String key, Object value) {
|
||||
// try {
|
||||
// redisTemplate.opsForList().rightPush(key, value);
|
||||
// return true;
|
||||
// } catch (Exception e) {
|
||||
// log.error(e.getMessage());
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 批量设置值到List中的尾部
|
||||
// *
|
||||
// * @param key List名字
|
||||
// * @param values 要设置的集合
|
||||
// * @return
|
||||
// */
|
||||
// public Boolean listAddAllInEnd(String key, Collection<?> values) {
|
||||
// try {
|
||||
// redisTemplate.opsForList().rightPushAll(key, values);
|
||||
// return true;
|
||||
// } catch (Exception e) {
|
||||
// log.error(e.getMessage());
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 通过索引去设置List->key中的值
|
||||
// *
|
||||
// * @param key redis的key
|
||||
// * @param index 索引
|
||||
// * @param value 值
|
||||
// * @return
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Boolean listAddByIndex(String key, long index, Object value) {
|
||||
// try {
|
||||
// redisTemplate.opsForList().set(key, index, value);
|
||||
// return true;
|
||||
// } catch (Exception e) {
|
||||
// log.error(e.getMessage());
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * 根据索引获取list中的值
|
||||
// *
|
||||
// * @param key list名字
|
||||
// * @param index
|
||||
// * @return
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Object listGetByIndex(String key, long index) {
|
||||
// return redisTemplate.opsForList().index(key, index);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 根据索引范围获取list中的值
|
||||
// *
|
||||
// * @param key list名字
|
||||
// * @param start
|
||||
// * @param end
|
||||
// * @return
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public List<Object> listGetByRange(String key, long start, long end) {
|
||||
// return redisTemplate.opsForList().range(key, start, end);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 移除并获取列表中第一个元素(如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止)
|
||||
// *
|
||||
// * @param key list名字
|
||||
// * @return
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Object listLeftPop(String key) {
|
||||
// return redisTemplate.opsForList().leftPop(key);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 移除并获取列表中最后一个元素(如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止)
|
||||
// *
|
||||
// * @param key list名字
|
||||
// * @return
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Object listRightPop(String key) {
|
||||
// return redisTemplate.opsForList().rightPop(key);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * <p>说明:获取列表元素的大小
|
||||
// * <p>
|
||||
// *
|
||||
// * @param
|
||||
// * @name: listGetSize
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Long listGetSize(String key) {
|
||||
// return redisTemplate.opsForList().size(key);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * 删除集合中值等于value的元素(
|
||||
// * index=0, 删除所有值等于value的元素;
|
||||
// * index>0, 从头部开始删除第一个值等于value的元素;
|
||||
// * index<0, 从尾部开始删除第一个值等于value的元素)
|
||||
// *
|
||||
// * @param key
|
||||
// * @param index
|
||||
// * @param value
|
||||
// * @return
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public Long listRemove(String key, long index, Object value) {
|
||||
// return redisTemplate.opsForList().remove(key, index, value);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * <p>说明:清除所有缓存
|
||||
// * <p><b>该方法会清理掉redis中所有的缓存,谨慎使用</b>
|
||||
// * <p>
|
||||
// *
|
||||
// * @name: empty
|
||||
// * @author :Wind
|
||||
// */
|
||||
// public void empty() {
|
||||
// redisTemplate.getConnectionFactory().getConnection().flushAll();
|
||||
// }
|
||||
}
|
||||
@ -1,9 +1,7 @@
|
||||
package org.dromara.sms4j.test;
|
||||
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.core.config.SupplierFactory;
|
||||
import org.dromara.sms4j.core.factory.SmsFactory;
|
||||
import org.dromara.sms4j.provider.enumerate.SupplierType;
|
||||
import org.dromara.sms4j.unisms.config.UniConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@ -16,15 +14,15 @@ public class Sms4jTest {
|
||||
|
||||
@Test
|
||||
public void uniSmsTest() {
|
||||
UniConfig build = UniConfig.builder()
|
||||
.signature("***")
|
||||
.accessKeyId("7Cr1***VJQ11Ap4***Mo7xmFg")
|
||||
.templateId("2001")
|
||||
.templateName("message")
|
||||
.isSimple(true)
|
||||
.build();
|
||||
SupplierFactory.setUniConfig(build);
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.UNI_SMS).sendMessage("175***65952", "123123");
|
||||
System.out.println(smsResponse);
|
||||
// UniConfig build = UniConfig.builder()
|
||||
// .signature("***")
|
||||
// .accessKeyId("7Cr1***VJQ11Ap4***Mo7xmFg")
|
||||
// .templateId("2001")
|
||||
// .templateName("message")
|
||||
// .isSimple(true)
|
||||
// .build();
|
||||
// SupplierFactory.setUniConfig(build);
|
||||
// SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.UNI_SMS).sendMessage("175***65952", "123123");
|
||||
// System.out.println(smsResponse);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,10 +5,20 @@ import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.aliyun.service.AlibabaSmsImpl;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.cloopen.service.CloopenSmsImpl;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.core.factory.SmsFactory;
|
||||
import org.dromara.sms4j.provider.enumerate.SupplierType;
|
||||
import org.dromara.sms4j.ctyun.service.CtyunSmsImpl;
|
||||
import org.dromara.sms4j.emay.service.EmaySmsImpl;
|
||||
import org.dromara.sms4j.huawei.service.HuaweiSmsImpl;
|
||||
import org.dromara.sms4j.jdcloud.service.JdCloudSmsImpl;
|
||||
import org.dromara.sms4j.netease.service.NeteaseSmsImpl;
|
||||
import org.dromara.sms4j.tencent.service.TencentSmsImpl;
|
||||
import org.dromara.sms4j.unisms.service.UniSmsImpl;
|
||||
import org.dromara.sms4j.yunpian.service.YunPianSmsImpl;
|
||||
import org.dromara.sms4j.zhutong.service.ZhutongSmsImpl;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@ -24,13 +34,19 @@ class Sms4jTest {
|
||||
private static final String PHONE = "";
|
||||
|
||||
@Test
|
||||
public void alibabaSmsTest() {
|
||||
public void byLoadTest() {
|
||||
if (StrUtil.isBlank(PHONE)) {
|
||||
return;
|
||||
}
|
||||
// 通过负载均衡服务获取短信服务对象
|
||||
SmsResponse smsResponse = SmsFactory.getSmsBlend().sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void alibabaSmsTest() {
|
||||
// 阿里
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.ALIBABA).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(AlibabaSmsImpl.SUPPLIER).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
|
||||
@ -40,7 +56,7 @@ class Sms4jTest {
|
||||
return;
|
||||
}
|
||||
// 华为
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.HUAWEI).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(HuaweiSmsImpl.SUPPLIER).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -51,7 +67,7 @@ class Sms4jTest {
|
||||
return;
|
||||
}
|
||||
// 容联云
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.CLOOPEN).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(CloopenSmsImpl.SUPPLIER).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -62,7 +78,7 @@ class Sms4jTest {
|
||||
return;
|
||||
}
|
||||
// 亿美软通
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.EMAY).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(EmaySmsImpl.SUPPLIER).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -73,7 +89,7 @@ class Sms4jTest {
|
||||
return;
|
||||
}
|
||||
// 京东云
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.JD_CLOUD).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(JdCloudSmsImpl.SUPPLIER).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -84,7 +100,7 @@ class Sms4jTest {
|
||||
return;
|
||||
}
|
||||
// 云片
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.YUNPIAN).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(YunPianSmsImpl.SUPPLIER).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -95,7 +111,7 @@ class Sms4jTest {
|
||||
return;
|
||||
}
|
||||
// 腾讯
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.TENCENT).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(TencentSmsImpl.SUPPLIER).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -106,7 +122,7 @@ class Sms4jTest {
|
||||
return;
|
||||
}
|
||||
// 合一
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.UNI_SMS).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(UniSmsImpl.SUPPLIER).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -117,7 +133,7 @@ class Sms4jTest {
|
||||
return;
|
||||
}
|
||||
// 天翼云
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.CTYUN).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(CtyunSmsImpl.SUPPLIER).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -128,7 +144,7 @@ class Sms4jTest {
|
||||
return;
|
||||
}
|
||||
// 网易云短信
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.NETEASE).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(NeteaseSmsImpl.SUPPLIER).sendMessage(PHONE, SmsUtil.getRandomInt(6));
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -143,7 +159,7 @@ class Sms4jTest {
|
||||
}
|
||||
// 助通短信短信
|
||||
String msg = StrUtil.format("【图书商城】您好,你的验证码是{}:(5分钟失效)", SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.ZHUTONG).sendMessage(PHONE, msg);
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(ZhutongSmsImpl.SUPPLIER).sendMessage(PHONE, msg);
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -159,7 +175,7 @@ class Sms4jTest {
|
||||
// 助通短信短信
|
||||
LinkedHashMap<String, String> messages = new LinkedHashMap<>(1);
|
||||
messages.put("code", SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.ZHUTONG).sendMessage(PHONE, "59264", messages);
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(ZhutongSmsImpl.SUPPLIER).sendMessage(PHONE, "59264", messages);
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -174,7 +190,7 @@ class Sms4jTest {
|
||||
}
|
||||
// 助通短信短信
|
||||
String msg = StrUtil.format("【图书商城】您好,你的验证码是{}:(5分钟失效)", SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.ZHUTONG).massTexting(ListUtil.of(PHONE, "180****1111"), msg);
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(ZhutongSmsImpl.SUPPLIER).massTexting(ListUtil.of(PHONE, "180****1111"), msg);
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
@ -190,8 +206,9 @@ class Sms4jTest {
|
||||
// 助通短信短信
|
||||
LinkedHashMap<String, String> messages = new LinkedHashMap<>(1);
|
||||
messages.put("code", SmsUtil.getRandomInt(6));
|
||||
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.ZHUTONG).massTexting(ListUtil.of(PHONE, "180****1111"), "59264", messages);
|
||||
SmsResponse smsResponse = SmsFactory.getBySupplier(ZhutongSmsImpl.SUPPLIER).massTexting(ListUtil.of(PHONE, "180****1111"), "59264", messages);
|
||||
log.info(JSONUtil.toJsonStr(smsResponse));
|
||||
Assert.isTrue(smsResponse.isSuccess());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,54 +0,0 @@
|
||||
package org.dromara.sms4j.starter.aop;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.smsProxy.RestrictedProcess;
|
||||
import org.dromara.sms4j.api.universal.SmsRedisUtil;
|
||||
import org.dromara.sms4j.comm.config.SmsConfig;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.starter.utils.SmsSpringUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
public class RestrictedProcessImpl extends RestrictedProcess {
|
||||
private static final Long minTimer = 60 * 1000L;
|
||||
private static final Long accTimer = 24 * 60 * 60 * 1000L;
|
||||
private static final String REDIS_KEY = "sms:restricted:";
|
||||
|
||||
|
||||
@Override
|
||||
public SmsBlendException process(SmsConfig config,String args) throws Exception {
|
||||
SmsRedisUtil redis = SmsSpringUtil.getBean(SmsRedisUtil.class);
|
||||
if (Objects.isNull(redis)){
|
||||
throw new SmsBlendException("The redis tool could not be found");
|
||||
}
|
||||
Integer accountMax = config.getAccountMax();//每日最大发送量
|
||||
Integer minuteMax = config.getMinuteMax();//每分钟最大发送量
|
||||
if (SmsUtil.isNotEmpty(accountMax)) { //是否配置了每日限制
|
||||
Integer i = (Integer) redis.getByKey(REDIS_KEY + args + "max");
|
||||
if (SmsUtil.isEmpty(i)) {
|
||||
redis.setOrTime(REDIS_KEY + args + "max", 1, accTimer / 1000);
|
||||
} else if (i > accountMax) {
|
||||
log.info("The phone:" + args + ",number of short messages reached the maximum today");
|
||||
return new SmsBlendException("The phone:" + args + ",number of short messages reached the maximum today");
|
||||
} else {
|
||||
redis.setOrTime(REDIS_KEY + args + "max", i + 1, accTimer / 1000);
|
||||
}
|
||||
}
|
||||
if (SmsUtil.isNotEmpty(minuteMax)) { //是否配置了每分钟最大限制
|
||||
Integer o = (Integer) redis.getByKey(REDIS_KEY + args);
|
||||
if (SmsUtil.isNotEmpty(o)) {
|
||||
if (o < minuteMax) {
|
||||
redis.setOrTime(REDIS_KEY + args, o + 1, minTimer / 1000);
|
||||
} else {
|
||||
log.info("The phone:" + args + ",number of short messages reached the maximum today");
|
||||
return new SmsBlendException("The phone:", args + " Text messages are sent too often!");
|
||||
}
|
||||
} else {
|
||||
redis.setOrTime(REDIS_KEY + args, 1, minTimer / 1000);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package org.dromara.sms4j.starter.aop;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.dao.SmsDao;
|
||||
import org.dromara.sms4j.api.proxy.RestrictedProcess;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||
import org.dromara.sms4j.provider.factory.BeanFactory;
|
||||
import org.dromara.sms4j.starter.utils.SmsSpringUtil;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
@Slf4j
|
||||
public class SpringRestrictedProcess implements RestrictedProcess {
|
||||
private static final Long minTimer = 60 * 1000L;
|
||||
private static final Long accTimer = 24 * 60 * 60 * 1000L;
|
||||
private static final String REDIS_KEY = "sms:restricted:";
|
||||
|
||||
|
||||
@Override
|
||||
public SmsBlendException process(String phone) throws Exception {
|
||||
SmsConfig config = BeanFactory.getSmsConfig();
|
||||
SmsDao smsDao = SmsSpringUtil.getBean(SmsDao.class);
|
||||
if (Objects.isNull(smsDao)) {
|
||||
throw new SmsBlendException("The dao tool could not be found");
|
||||
}
|
||||
Integer accountMax = config.getAccountMax(); // 每日最大发送量
|
||||
Integer minuteMax = config.getMinuteMax(); // 每分钟最大发送量
|
||||
if (SmsUtil.isNotEmpty(accountMax)) { // 是否配置了每日限制
|
||||
Integer i = (Integer) smsDao.get(REDIS_KEY + phone + "max");
|
||||
if (SmsUtil.isEmpty(i)) {
|
||||
smsDao.set(REDIS_KEY + phone + "max", 1, accTimer / 1000);
|
||||
} else if (i >= accountMax) {
|
||||
log.info("The phone:" + phone + ",number of short messages reached the maximum today");
|
||||
return new SmsBlendException("The phone:" + phone + ",number of short messages reached the maximum today");
|
||||
} else {
|
||||
smsDao.set(REDIS_KEY + phone + "max", i + 1, accTimer / 1000);
|
||||
}
|
||||
}
|
||||
if (SmsUtil.isNotEmpty(minuteMax)) { // 是否配置了每分钟最大限制
|
||||
Integer o = (Integer) smsDao.get(REDIS_KEY + phone);
|
||||
if (SmsUtil.isNotEmpty(o)) {
|
||||
if (o < minuteMax) {
|
||||
smsDao.set(REDIS_KEY + phone, o + 1, minTimer / 1000);
|
||||
} else {
|
||||
log.info("The phone:" + phone + ",number of short messages reached the maximum today");
|
||||
return new SmsBlendException("The phone:", phone + " Text messages are sent too often!");
|
||||
}
|
||||
} else {
|
||||
smsDao.set(REDIS_KEY + phone, 1, minTimer / 1000);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -1,33 +1,21 @@
|
||||
package org.dromara.sms4j.starter.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.smsProxy.SmsInvocationHandler;
|
||||
import org.dromara.sms4j.api.universal.SmsRedisUtil;
|
||||
import org.dromara.sms4j.comm.config.SmsBanner;
|
||||
import org.dromara.sms4j.comm.config.SmsConfig;
|
||||
import org.dromara.sms4j.comm.config.SmsSqlConfig;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.core.SupplierSqlConfig;
|
||||
import org.dromara.sms4j.core.config.SupplierFactory;
|
||||
import org.dromara.sms4j.starter.aop.RestrictedProcessImpl;
|
||||
import org.dromara.sms4j.provider.config.SmsBanner;
|
||||
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||
import org.dromara.sms4j.provider.factory.BeanFactory;
|
||||
import org.dromara.sms4j.starter.utils.ConfigUtil;
|
||||
import org.dromara.sms4j.starter.utils.SmsRedisUtils;
|
||||
import org.dromara.sms4j.starter.utils.SmsSpringUtil;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
|
||||
@ -40,10 +28,6 @@ public class SmsAutowiredConfig {
|
||||
this.smsSpringUtil = smsSpringUtil;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.sql")
|
||||
protected SmsSqlConfig smsSqlConfig(){return BeanFactory.getSmsSqlConfig();}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@ConfigurationProperties(prefix = "sms") //指定配置文件注入属性前缀
|
||||
@ -53,55 +37,59 @@ public class SmsAutowiredConfig {
|
||||
|
||||
/** 注入一个定时器*/
|
||||
@Bean
|
||||
@Lazy
|
||||
protected DelayedTime delayedTime(){
|
||||
return BeanFactory.getDelayedTime();
|
||||
return BeanFactory.getDelayedTime();
|
||||
}
|
||||
|
||||
/** 注入线程池*/
|
||||
@Bean("smsExecutor")
|
||||
@Lazy
|
||||
protected Executor taskExecutor(SmsConfig config){
|
||||
return BeanFactory.setExecutor(config);
|
||||
return BeanFactory.setExecutor(config);
|
||||
}
|
||||
|
||||
/** 注入一个配置文件读取工具*/
|
||||
@Bean
|
||||
@Bean("smsConfigUtil")
|
||||
@Lazy
|
||||
protected ConfigUtil configUtil(Environment environment){
|
||||
return new ConfigUtil(environment);
|
||||
}
|
||||
|
||||
/** smsConfig参数意义为确保注入时smsConfig已经存在*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "sms", name = "config-type", havingValue = "config_file")
|
||||
@ConditionalOnProperty(prefix = "sms", name = "config-type", havingValue = "yaml")
|
||||
protected SupplierConfig supplierConfig(SmsConfig smsConfig){
|
||||
return new SupplierConfig();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "sms", name = "config-type", havingValue = "sql_config")
|
||||
protected SupplierSqlConfig supplierSqlConfig(SmsSqlConfig smsSqlConfig) throws SQLException {
|
||||
DataSource bean = SmsSpringUtil.getBean(DataSource.class);
|
||||
if (!Objects.isNull(bean)){
|
||||
BeanFactory.getJDBCTool().setConnection(bean.getConnection());
|
||||
}
|
||||
return new SupplierSqlConfig();
|
||||
}
|
||||
// @Bean
|
||||
// @ConditionalOnProperty(prefix = "sms", name = "config-type", havingValue = "sql_config")
|
||||
// protected SupplierSqlConfig supplierSqlConfig(SmsSqlConfig smsSqlConfig) throws SQLException {
|
||||
// DataSource bean = SmsSpringUtil.getBean(DataSource.class);
|
||||
// if (!Objects.isNull(bean)){
|
||||
// BeanFactory.getJDBCTool().setConnection(bean.getConnection());
|
||||
// }
|
||||
// return new SupplierSqlConfig();
|
||||
// }
|
||||
|
||||
@PostConstruct
|
||||
void init(){
|
||||
/* 如果配置中启用了redis,则注入redis工具*/
|
||||
if (BeanFactory.getSmsConfig().getRedisCache()){
|
||||
//如果用户没有实现RedisUtil接口则注入默认的实现
|
||||
if (!SmsSpringUtil.interfaceExist(SmsRedisUtil.class)){
|
||||
smsSpringUtil.createBean(SmsRedisUtils.class);
|
||||
}
|
||||
SmsInvocationHandler.setRestrictedProcess(new RestrictedProcessImpl());
|
||||
log.debug("The redis cache is enabled for sms4j");
|
||||
}
|
||||
// 将spring中存在的所有配置,设置到配置工厂,并添加至负载均衡器
|
||||
Map<String, org.dromara.sms4j.api.universal.SupplierConfig> beansOfType = SmsSpringUtil.getBeansOfType(org.dromara.sms4j.api.universal.SupplierConfig.class);
|
||||
for (org.dromara.sms4j.api.universal.SupplierConfig s : beansOfType.values()) {
|
||||
SupplierFactory.setSupplierConfig(s);
|
||||
}
|
||||
// /* 如果配置中启用了redis,则注入redis工具*/
|
||||
// if (BeanFactory.getSmsConfig().getRedisCache()){
|
||||
// //如果用户没有实现RedisUtil接口则注入默认的实现
|
||||
// if (!SmsSpringUtil.interfaceExist(SmsRedisUtil.class)){
|
||||
// smsSpringUtil.createBean(SmsRedisUtils.class);
|
||||
// }
|
||||
// SmsInvocationHandler.setRestrictedProcess(new RestrictedProcessImpl());
|
||||
// log.debug("The redis cache is enabled for sms4j");
|
||||
// }
|
||||
// // 将spring中存在的所有配置,设置到配置工厂,并添加至负载均衡器
|
||||
// Map<String, org.dromara.sms4j.api.universal.SupplierConfig> beansOfType = SmsSpringUtil.getBeansOfType(org.dromara.sms4j.api.universal.SupplierConfig.class);
|
||||
// for (org.dromara.sms4j.api.universal.SupplierConfig s : beansOfType.values()) {
|
||||
// SupplierFactory.setSupplierConfig(s);
|
||||
// }
|
||||
|
||||
//打印banner
|
||||
if (BeanFactory.getSmsConfig().getIsPrint()){
|
||||
SmsBanner.PrintBanner(Constant.VERSION);
|
||||
|
||||
@ -0,0 +1,96 @@
|
||||
package org.dromara.sms4j.starter.config;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaFactory;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.dao.SmsDao;
|
||||
import org.dromara.sms4j.api.dao.SmsDaoDefaultImpl;
|
||||
import org.dromara.sms4j.api.proxy.RestrictedProcess;
|
||||
import org.dromara.sms4j.api.universal.SupplierConfig;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenFactory;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.utils.SmsUtil;
|
||||
import org.dromara.sms4j.core.factory.SmsFactory;
|
||||
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
|
||||
import org.dromara.sms4j.ctyun.config.CtyunFactory;
|
||||
import org.dromara.sms4j.emay.config.EmayFactory;
|
||||
import org.dromara.sms4j.huawei.config.HuaweiFactory;
|
||||
import org.dromara.sms4j.jdcloud.config.JdCloudFactory;
|
||||
import org.dromara.sms4j.netease.config.NeteaseFactory;
|
||||
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
|
||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||
import org.dromara.sms4j.starter.aop.SpringRestrictedProcess;
|
||||
import org.dromara.sms4j.tencent.config.TencentFactory;
|
||||
import org.dromara.sms4j.unisms.config.UniFactory;
|
||||
import org.dromara.sms4j.yunpian.config.YunPianFactory;
|
||||
import org.dromara.sms4j.zhutong.config.ZhutongFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class SmsBlendsInitializer {
|
||||
|
||||
private final List<BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig>> factoryList;
|
||||
|
||||
private final SmsConfig smsConfig;
|
||||
private final Map<String, Map<String, Object>> blends;
|
||||
|
||||
@PostConstruct
|
||||
public void initBlends() {
|
||||
this.registerDefaultFactory();
|
||||
// 注册短信对象工厂
|
||||
ProviderFactoryHolder.registerFactory(factoryList);
|
||||
// 解析供应商配置
|
||||
for(String configId : blends.keySet()) {
|
||||
Map<String, Object> configMap = blends.get(configId);
|
||||
Object supplierObj = configMap.get(Constant.SUPPLIER_KEY);
|
||||
String supplier = supplierObj == null ? "" : String.valueOf(supplierObj);
|
||||
supplier = StrUtil.isEmpty(supplier) ? configId : supplier;
|
||||
BaseProviderFactory<SmsBlend, SupplierConfig> providerFactory = (BaseProviderFactory<SmsBlend, org.dromara.sms4j.api.universal.SupplierConfig>) ProviderFactoryHolder.requireForSupplier(supplier);
|
||||
if(providerFactory == null) {
|
||||
log.warn("创建\"{}\"的短信服务失败,未找到供应商为\"{}\"的服务", configId, supplier);
|
||||
continue;
|
||||
}
|
||||
configMap.put("config-id", configId);
|
||||
SmsUtil.replaceKeysSeperator(configMap, "-", "_");
|
||||
JSONObject configJson = new JSONObject(configMap);
|
||||
org.dromara.sms4j.api.universal.SupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass());
|
||||
if(Boolean.TRUE.equals(smsConfig.getRestricted())) {
|
||||
SmsFactory.createRestrictedSmsBlend(supplierConfig);
|
||||
} else {
|
||||
SmsFactory.createSmsBlend(supplierConfig);
|
||||
}
|
||||
}
|
||||
|
||||
//注册短信拦截实现
|
||||
SmsInvocationHandler.setRestrictedProcess(new SpringRestrictedProcess());
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册默认工厂实例
|
||||
*/
|
||||
private void registerDefaultFactory() {
|
||||
ProviderFactoryHolder.registerFactory(AlibabaFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(CloopenFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(CtyunFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(EmayFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(HuaweiFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(JdCloudFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(NeteaseFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(TencentFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(UniFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(YunPianFactory.instance());
|
||||
ProviderFactoryHolder.registerFactory(ZhutongFactory.instance());
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,120 +1,18 @@
|
||||
package org.dromara.sms4j.starter.config;
|
||||
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
|
||||
import org.dromara.sms4j.cloopen.config.CloopenConfig;
|
||||
import org.dromara.sms4j.core.config.SupplierFactory;
|
||||
import org.dromara.sms4j.ctyun.config.CtyunConfig;
|
||||
import org.dromara.sms4j.emay.config.EmayConfig;
|
||||
import org.dromara.sms4j.huawei.config.HuaweiConfig;
|
||||
import org.dromara.sms4j.jdcloud.config.JdCloudConfig;
|
||||
import org.dromara.sms4j.netease.config.NeteaseConfig;
|
||||
import org.dromara.sms4j.tencent.config.TencentConfig;
|
||||
import org.dromara.sms4j.unisms.config.UniConfig;
|
||||
import org.dromara.sms4j.yunpian.config.YunpianConfig;
|
||||
import org.dromara.sms4j.zhutong.config.ZhutongConfig;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SupplierConfig {
|
||||
|
||||
|
||||
/**
|
||||
* 阿里差异化配置
|
||||
*/
|
||||
/** smsConfig参数意义为确保注入时smsConfig已经存在*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.alibaba")
|
||||
protected AlibabaConfig alibabaConfig() {
|
||||
return SupplierFactory.getAlibabaConfig();
|
||||
@ConfigurationProperties(prefix = "sms.blends")
|
||||
protected Map<String, Map<String, Object>> blends(){
|
||||
return new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 华为差异化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.huawei")
|
||||
protected HuaweiConfig huaweiConfig() {
|
||||
return SupplierFactory.getHuaweiConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 云片短信差异化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.yunpian")
|
||||
protected YunpianConfig yunpianConfig() {
|
||||
return SupplierFactory.getYunpianConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 合一短信差异化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.uni")
|
||||
protected UniConfig uniConfig() {
|
||||
return SupplierFactory.getUniConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 腾讯短信差异化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.tencent")
|
||||
protected TencentConfig tencentConfig() {
|
||||
return SupplierFactory.getTencentConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 京东云短信差异化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.jdcloud")
|
||||
protected JdCloudConfig jdCloudConfig() {
|
||||
return SupplierFactory.getJdCloudConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 容联云短信差异化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.cloopen")
|
||||
protected CloopenConfig cloopenConfig() {
|
||||
return SupplierFactory.getCloopenConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 亿美软通短信差异化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.emay")
|
||||
protected EmayConfig emayConfig() {
|
||||
return SupplierFactory.getEmayConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 天翼云短信差异化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.ctyun")
|
||||
protected CtyunConfig ctyunConfig() {
|
||||
return SupplierFactory.getCtyunConfig();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 网易云信差异化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.netease")
|
||||
protected NeteaseConfig neteaseConfig() {
|
||||
return SupplierFactory.getNeteaseConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* 助通信差异化配置
|
||||
*/
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms.zhutong")
|
||||
protected ZhutongConfig zhutongConfig() {
|
||||
return SupplierFactory.getZhutongConfig();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,479 +0,0 @@
|
||||
package org.dromara.sms4j.starter.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.universal.SmsRedisUtil;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
@Slf4j
|
||||
public class SmsRedisUtils implements SmsRedisUtil {
|
||||
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
public void init(RedisConnectionFactory connectionFactory) {
|
||||
if (connectionFactory == null){
|
||||
log.error("RedisConnectionFactory is not found");
|
||||
}
|
||||
// 指定相应的序列化方案
|
||||
StringRedisSerializer keySerializer = new StringRedisSerializer();
|
||||
JdkSerializationRedisSerializer valueSerializer = new JdkSerializationRedisSerializer();
|
||||
// 构建StringRedisTemplate
|
||||
StringRedisTemplate stringTemplate = new StringRedisTemplate();
|
||||
stringTemplate.setConnectionFactory(connectionFactory);
|
||||
stringTemplate.afterPropertiesSet();
|
||||
// 构建RedisTemplate
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(keySerializer);
|
||||
template.setHashKeySerializer(keySerializer);
|
||||
template.setValueSerializer(valueSerializer);
|
||||
template.setHashValueSerializer(valueSerializer);
|
||||
template.afterPropertiesSet();
|
||||
this.redisTemplate = template;
|
||||
}
|
||||
|
||||
public SmsRedisUtils() {
|
||||
}
|
||||
|
||||
public SmsRedisUtils(RedisTemplate<String, Object> redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:设置redis的key的到期时间
|
||||
*
|
||||
* @param key redis的key
|
||||
* @param time 到期时间
|
||||
* @name: setTimeByKey
|
||||
* @author :Wind
|
||||
*/
|
||||
public boolean setTimeByKey(String key, Long time) {
|
||||
try {
|
||||
if (time > 0) {
|
||||
redisTemplate.expire(key, time, TimeUnit.SECONDS);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:放入redis
|
||||
*
|
||||
* @param key 要放入的key
|
||||
* @param value 要放入的value
|
||||
* @name: set
|
||||
* @author :Wind
|
||||
*/
|
||||
public boolean set(String key, Object value) {
|
||||
|
||||
redisTemplate.opsForValue().set(key, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:放入带过期时间的缓存
|
||||
*
|
||||
* @param time 到期时间(秒)
|
||||
* @name: setOrTime
|
||||
* @author :Wind
|
||||
*/
|
||||
public boolean setOrTime(String key, Object value, Long time) {
|
||||
try {
|
||||
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:将Map中的数据批量放置到redis中
|
||||
* <p>
|
||||
*
|
||||
* @param valueMap 要放入的数据
|
||||
* @name: multiSet
|
||||
* @author :Wind
|
||||
*/
|
||||
public boolean multiSet(Map valueMap) {
|
||||
try {
|
||||
redisTemplate.opsForValue().multiSet(valueMap);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error(e.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:获取key对应的值
|
||||
*
|
||||
* @param key 要查询的key
|
||||
* @name: getByKey
|
||||
* @author :Wind
|
||||
*/
|
||||
public Object getByKey(String key) {
|
||||
return redisTemplate.opsForValue().get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:获取字符串型值
|
||||
*
|
||||
* @param
|
||||
* @name: getKyeString
|
||||
* @author :Wind
|
||||
*/
|
||||
public String getKyeString(String key) {
|
||||
return (String) getByKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:判断key是否存在
|
||||
*
|
||||
* @param key 要判断的key
|
||||
* @name: hasKey
|
||||
* @author :Wind
|
||||
*/
|
||||
public Boolean hasKey(String key) {
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 说明:根据key删除redis缓存可以批量删除
|
||||
*
|
||||
* @param key 要删除的key
|
||||
* @name: deleteKey
|
||||
* @author :Wind
|
||||
*/
|
||||
public Boolean deleteKey(String... key) {
|
||||
if (key != null && key.length > 0) {
|
||||
if (key.length == 1) {
|
||||
return redisTemplate.delete(key[0]);
|
||||
} else {
|
||||
Long delete = redisTemplate.delete(Arrays.asList(key));
|
||||
return delete >= 1L;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Boolean delete(String key) {
|
||||
Set<String> keys = redisTemplate.keys(key + "*");
|
||||
redisTemplate.delete(keys);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据key 获取key的过期时间
|
||||
*
|
||||
* @param key 键 不能为null
|
||||
* @return 时间(秒) 返回-1, 代表为永久有效
|
||||
*/
|
||||
public Long getKeyExpire(String key) {
|
||||
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改redis中key的名称
|
||||
*
|
||||
* @param oldKey 旧的key值
|
||||
* @param newKey 新的key值
|
||||
*/
|
||||
public void renameKey(String oldKey, String newKey) {
|
||||
redisTemplate.rename(oldKey, newKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:将map对象存入redis
|
||||
* <p>
|
||||
*
|
||||
* @param map 要存入redis中的map
|
||||
* @name: setMap
|
||||
* @author :Wind
|
||||
*/
|
||||
public <T, M> void MapSetMap(String key, Map<T, M> map) {
|
||||
redisTemplate.opsForHash().putAll(key, map);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:获取所有hash表中字段
|
||||
* <p>
|
||||
*
|
||||
* @param
|
||||
* @name: getMapByKey
|
||||
* @author :Wind
|
||||
*/
|
||||
public Set<Object> MapGetHashByKey(String key) {
|
||||
return redisTemplate.opsForHash().keys(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:根据key和fieId获取对应的值
|
||||
* <p>
|
||||
*
|
||||
* @param fieId hash中的fieId也是Map的Key
|
||||
* @name: getValueByFieID
|
||||
* @author :Wind
|
||||
*/
|
||||
public Object MapGetValueByFieID(String key, String fieId) {
|
||||
return redisTemplate.opsForHash().get(key, fieId);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:根据key获取所有的键值对
|
||||
* <p>
|
||||
*
|
||||
* @param key redis中的key
|
||||
* @name: getMapByKey
|
||||
* @author :Wind
|
||||
*/
|
||||
public Map<Object, Object> MapGetMapByKey(String key) {
|
||||
return redisTemplate.opsForHash().entries(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:向key中添加一对新的键值对
|
||||
* <p>
|
||||
*
|
||||
* @param hashKey 键值对的key
|
||||
* @param value 键值对的value
|
||||
* @name: setNewMapValue
|
||||
* @author :Wind
|
||||
*/
|
||||
public void MapSetNewMapValue(String key, String hashKey, Object value) {
|
||||
redisTemplate.opsForHash().put(key, hashKey, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:根据key和field删除数据
|
||||
* <p>
|
||||
*
|
||||
* @param fields 要删除的fields
|
||||
* @return Long 影响的条数
|
||||
* @name: hashDelete
|
||||
* @author :Wind
|
||||
*/
|
||||
public Long MapHashDelete(String key, Object... fields) {
|
||||
return redisTemplate.opsForHash().delete(key, fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:查看key下存了多少条键值对
|
||||
* <p>
|
||||
*
|
||||
* @param key redis的key
|
||||
* @name: getMapValueSize
|
||||
* @author :Wind
|
||||
*/
|
||||
public Long MapGetMapValueSize(String key) {
|
||||
return redisTemplate.opsForHash().size(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置值到List中的头部
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @return
|
||||
* @author :Wind
|
||||
*/
|
||||
public Boolean listAddInHead(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().leftPush(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置值到List中的头部
|
||||
*
|
||||
* @param key List名字
|
||||
* @param values
|
||||
* @return
|
||||
* @author :Wind
|
||||
*/
|
||||
public Boolean listAddAllInHead(String key, Collection<?> values) {
|
||||
try {
|
||||
redisTemplate.opsForList().leftPushAll(key, values);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果存在List->key, 则设置值到List中的头部
|
||||
*
|
||||
* @param key List名字
|
||||
* @param value
|
||||
* @return
|
||||
* @author :Wind
|
||||
*/
|
||||
public Boolean listAddIfPresent(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().leftPushIfPresent(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置值到List中的尾部
|
||||
*
|
||||
* @param key List名字
|
||||
* @param value 值
|
||||
* @return
|
||||
*/
|
||||
public Boolean listAddInEnd(String key, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPush(key, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量设置值到List中的尾部
|
||||
*
|
||||
* @param key List名字
|
||||
* @param values 要设置的集合
|
||||
* @return
|
||||
*/
|
||||
public Boolean listAddAllInEnd(String key, Collection<?> values) {
|
||||
try {
|
||||
redisTemplate.opsForList().rightPushAll(key, values);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过索引去设置List->key中的值
|
||||
*
|
||||
* @param key redis的key
|
||||
* @param index 索引
|
||||
* @param value 值
|
||||
* @return
|
||||
* @author :Wind
|
||||
*/
|
||||
public Boolean listAddByIndex(String key, long index, Object value) {
|
||||
try {
|
||||
redisTemplate.opsForList().set(key, index, value);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.error(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 根据索引获取list中的值
|
||||
*
|
||||
* @param key list名字
|
||||
* @param index
|
||||
* @return
|
||||
* @author :Wind
|
||||
*/
|
||||
public Object listGetByIndex(String key, long index) {
|
||||
return redisTemplate.opsForList().index(key, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据索引范围获取list中的值
|
||||
*
|
||||
* @param key list名字
|
||||
* @param start
|
||||
* @param end
|
||||
* @return
|
||||
* @author :Wind
|
||||
*/
|
||||
public List<Object> listGetByRange(String key, long start, long end) {
|
||||
return redisTemplate.opsForList().range(key, start, end);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除并获取列表中第一个元素(如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止)
|
||||
*
|
||||
* @param key list名字
|
||||
* @return
|
||||
* @author :Wind
|
||||
*/
|
||||
public Object listLeftPop(String key) {
|
||||
return redisTemplate.opsForList().leftPop(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移除并获取列表中最后一个元素(如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止)
|
||||
*
|
||||
* @param key list名字
|
||||
* @return
|
||||
* @author :Wind
|
||||
*/
|
||||
public Object listRightPop(String key) {
|
||||
return redisTemplate.opsForList().rightPop(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:获取列表元素的大小
|
||||
* <p>
|
||||
*
|
||||
* @param
|
||||
* @name: listGetSize
|
||||
* @author :Wind
|
||||
*/
|
||||
public Long listGetSize(String key) {
|
||||
return redisTemplate.opsForList().size(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除集合中值等于value的元素(
|
||||
* index=0, 删除所有值等于value的元素;
|
||||
* index>0, 从头部开始删除第一个值等于value的元素;
|
||||
* index<0, 从尾部开始删除第一个值等于value的元素)
|
||||
*
|
||||
* @param key
|
||||
* @param index
|
||||
* @param value
|
||||
* @return
|
||||
* @author :Wind
|
||||
*/
|
||||
public Long listRemove(String key, long index, Object value) {
|
||||
return redisTemplate.opsForList().remove(key, index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:清除所有缓存
|
||||
* <p><b>该方法会清理掉redis中所有的缓存,谨慎使用</b>
|
||||
* <p>
|
||||
*
|
||||
* @name: empty
|
||||
* @author :Wind
|
||||
*/
|
||||
public void empty() {
|
||||
redisTemplate.getConnectionFactory().getConnection().flushAll();
|
||||
}
|
||||
}
|
||||
@ -2,5 +2,4 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
org.dromara.sms4j.starter.config.SmsMainConfig,\
|
||||
org.dromara.sms4j.starter.config.SmsAutowiredConfig,\
|
||||
org.dromara.sms4j.starter.config.SupplierConfig,\
|
||||
org.dromara.sms4j.comm.config.SmsConfig,\
|
||||
org.dromara.sms4j.comm.config.SmsSqlConfig
|
||||
org.dromara.sms4j.provider.config.SmsConfig
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user