!51 2.1.0版本合并

🚀 2.1.0版本合并
This commit is contained in:
风如歌 2023-05-26 13:54:22 +00:00 committed by Gitee
commit 6570e43ad9
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
87 changed files with 2923 additions and 1698 deletions

View File

@ -1,9 +1,9 @@
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sms4j v2.0.2</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sms4j v2.1.0</h1>
<h4 align="center" style="margin: 30px 0 30px; font-weight: bold;">sms4j -- 让发送短信变的更简单</h4>
<p align="center">
<a href="https://gitee.com/dromara/sms4j/stargazers"><img src="https://gitee.com/dromara/sms4j/badge/star.svg?theme=gvp"></a>
<a href="https://gitee.com/dromara/sms4j/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-green"></a>
<a href="https://gitee.com/dromara/sms4j"><img src="https://img.shields.io/badge/version-v2.0.2-blue"></a>
<a href="https://gitee.com/dromara/sms4j"><img src="https://img.shields.io/badge/version-v2.1.0-blue"></a>
</p>
<img src="/public/logo.png">
@ -28,6 +28,7 @@
- **京东云国内短信**
- **容联云国内短信**
- **亿美软通国内短信**
- **天翼云短信**
- **合一短信**
- **云片短信**

26
pom.xml
View File

@ -18,6 +18,8 @@
<module>sms4j-core</module>
<module>sms4j-autoimmit</module>
<module>sms4j-spring-boot-starter</module>
<module>sms4j-solon-plugin</module>
<module>sms4j-spring-boot-example</module>
</modules>
<!-- 开源协议 Apache 2.0 -->
@ -47,17 +49,16 @@
</scm>
<properties>
<spring.boot.version>2.7.11</spring.boot.version>
<revision>2.0.2</revision>
<aliyun.version>2.0.23</aliyun.version>
<tencent.version>3.1.622</tencent.version>
<unisms.version>0.0.4</unisms.version>
<revision>2.1.0-SNAPSHOT</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring.boot.version>2.7.12</spring.boot.version>
<solon.version>2.2.0</solon.version>
<redisson.version>3.17.0</redisson.version>
<jdcloud.version>1.3.3</jdcloud.version>
<json.version>2.0.15</json.version>
<okhttp.version>3.14.9</okhttp.version>
<forest.version>1.5.30</forest.version>
<hutool.version>5.8.16</hutool.version>
<okhttp.version>3.14.9</okhttp.version>
<hutool.version>5.8.18</hutool.version>
<xmlblend.version>2.3.0</xmlblend.version>
<activation.version>1.1.1</activation.version>
</properties>
@ -119,13 +120,6 @@
<version>${revision}</version>
</dependency>
<!-- 阿里JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${json.version}</version>
</dependency>
<!--Forest依赖 声明式HTTP客户端框架-->
<dependency>
<groupId>com.dtflys.forest</groupId>

View File

@ -0,0 +1,211 @@
package org.dromara.sms4j.api;
import com.dtflys.forest.config.ForestConfiguration;
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.comm.delayedTime.DelayedTime;
import org.dromara.sms4j.comm.factory.BeanFactory;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
public abstract class AbstractSmsBlend implements SmsBlend{
protected final Executor pool;
protected final DelayedTime delayed;
protected final ForestConfiguration http = BeanFactory.getForestConfiguration();
protected AbstractSmsBlend(Executor pool, DelayedTime delayed) {
this.pool = pool;
this.delayed = delayed;
}
protected AbstractSmsBlend() {
this.pool = BeanFactory.getExecutor();
this.delayed = BeanFactory.getDelayedTime();
}
/**
* <p>说明发送固定消息模板短信
* <p>此方法将使用配置文件中预设的短信模板进行短信发送
* <p>该方法指定的模板变量只能存在一个配置文件中
* <p>如使用的是腾讯的短信参数字符串中可以同时存在多个参数使用 & 分隔例如您的验证码为{1}{2}分钟内有效可以传为 message="xxxx"+"&"+"5"
* sendMessage
*
* @param phone 接收短信的手机号
* message 消息内容
* @author :Wind
*/
public abstract SmsResponse sendMessage(String phone, String message);
/**
* <p>说明使用自定义模板发送短信
* sendMessage
*
* @param templateId 模板id
* @param messages key为模板变量名称 value为模板变量值
* @author :Wind
*/
public abstract SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages);
/**
* <p>说明群发固定模板短信
* massTexting
*
* @author :Wind
*/
public abstract SmsResponse massTexting(List<String> phones, String message);
/**
* <p>说明使用自定义模板群发短信
* massTexting
*
* @author :Wind
*/
public abstract SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages);
/**
* <p>说明异步短信发送固定消息模板短信
* sendMessageAsync
*
* @param phone 要发送的号码
* @param message 发送内容
* @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);
}
/**
* <p>说明异步发送短信不关注发送结果
* sendMessageAsync
*
* @param phone 要发送的号码
* @param message 发送内容
* @author :Wind
*/
@Restricted
public final void sendMessageAsync(String phone, String message){
pool.execute(() -> {
sendMessage(phone, message);
});
}
/**
* <p>说明异步短信发送使用自定义模板发送短信
* sendMessage
*
* @param templateId 模板id
* @param messages key为模板变量名称 value为模板变量值
* @param callBack 回调
* @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);
}
/**
* <p>说明异步短信发送使用自定义模板发送短信不关注发送结果
* sendMessageAsync
*
* @param templateId 模板id
* @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);
});
}
/**
* <p>说明使用固定模板发送延时短信
* delayedMessage
*
* @param phone 接收短信的手机号
* @param message 要发送的短信
* @param delayedTime 延迟时间
* @author :Wind
*/
@Restricted
public final void delayedMessage(String phone, String message, Long delayedTime){
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, message);
}
}, delayedTime);
}
/**
* <p>说明使用自定义模板发送定时短信 sendMessage
* delayedMessage
*
* @param templateId 模板id
* @param messages key为模板变量名称 value为模板变量值
* @param phone 要发送的手机号
* @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
public void run() {
sendMessage(phone, templateId, messages);
}
}, delayedTime);
}
/**
* <p>说明群发延迟短信
* delayMassTexting
*
* @param phones 要群体发送的手机号码
* @author :Wind
*/
@Restricted
public final void delayMassTexting(List<String> phones, String message, Long delayedTime){
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, message);
}
}, delayedTime);
}
/**
* <p>说明使用自定义模板发送群体延迟短信
* delayMassTexting
*
* @param phones 要群体发送的手机号码
* @param templateId 模板id
* @param messages key为模板变量名称 value为模板变量值
* @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
public void run() {
massTexting(phones, templateId, messages);
}
}, delayedTime);
}
}

View File

@ -6,136 +6,35 @@ import org.dromara.sms4j.api.entity.SmsResponse;
import java.util.LinkedHashMap;
import java.util.List;
/**
* SmsBlend
* <p> 通用接口定义国内短信方法
* @author :Wind
* 2023/5/16 16:03
**/
public interface SmsBlend {
/**
* <p>说明发送固定消息模板短信
* <p>此方法将使用配置文件中预设的短信模板进行短信发送
* <p>该方法指定的模板变量只能存在一个配置文件中
* <p>如使用的是腾讯的短信参数字符串中可以同时存在多个参数使用 & 分隔例如您的验证码为{1}{2}分钟内有效可以传为 message="xxxx"+"&"+"5"
* sendMessage
*
* @param phone 接收短信的手机号
* message 消息内容
* @author :Wind
*/
SmsResponse sendMessage(String phone, String message);
/**
* <p>说明使用自定义模板发送短信
* sendMessage
*
* @param templateId 模板id
* @param messages key为模板变量名称 value为模板变量值
* @author :Wind
*/
SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages);
/**
* <p>说明群发固定模板短信
* massTexting
*
* @author :Wind
*/
SmsResponse massTexting(List<String> phones, String message);
/**
* <p>说明使用自定义模板群发短信
* massTexting
*
* @author :Wind
*/
SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages);
/**
* <p>说明异步短信发送固定消息模板短信
* sendMessageAsync
*
* @param phone 要发送的号码
* @param message 发送内容
* @param callBack 回调
* @author :Wind
*/
void sendMessageAsync(String phone, String message, CallBack callBack);
/**
* <p>说明异步发送短信不关注发送结果
* sendMessageAsync
*
* @param phone 要发送的号码
* @param message 发送内容
* @author :Wind
*/
void sendMessageAsync(String phone, String message);
/**
* <p>说明异步短信发送使用自定义模板发送短信
* sendMessage
*
* @param templateId 模板id
* @param messages key为模板变量名称 value为模板变量值
* @param callBack 回调
* @author :Wind
*/
void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages, CallBack callBack);
/**
* <p>说明异步短信发送使用自定义模板发送短信不关注发送结果
* sendMessageAsync
*
* @param templateId 模板id
* @param messages key为模板变量名称 value为模板变量值
* @author :Wind
*/
void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages);
/**
* <p>说明使用固定模板发送延时短信
* delayedMessage
*
* @param phone 接收短信的手机号
* @param message 要发送的短信
* @param delayedTime 延迟时间
* @author :Wind
*/
void delayedMessage(String phone, String message, Long delayedTime);
/**
* <p>说明使用自定义模板发送定时短信 sendMessage
* delayedMessage
*
* @param templateId 模板id
* @param messages key为模板变量名称 value为模板变量值
* @param phone 要发送的手机号
* @param delayedTime 延迟的时间
* @author :Wind
*/
void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
/**
* <p>说明群发延迟短信
* delayMassTexting
*
* @param phones 要群体发送的手机号码
* @author :Wind
*/
void delayMassTexting(List<String> phones, String message, Long delayedTime);
/**
* <p>说明使用自定义模板发送群体延迟短信
* delayMassTexting
*
* @param phones 要群体发送的手机号码
* @param templateId 模板id
* @param messages key为模板变量名称 value为模板变量值
* @param delayedTime 延迟的时间
* @author :Wind
*/
void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
}

View File

@ -0,0 +1,43 @@
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;
}
}

View File

@ -0,0 +1,48 @@
package org.dromara.sms4j.api.smsProxy;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.comm.config.SmsConfig;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Objects;
@Slf4j
public class SmsInvocationHandler implements InvocationHandler {
private SmsBlend smsBlend;
private SmsConfig config;
private static RestrictedProcess restrictedProcess = new RestrictedProcess();
private SmsInvocationHandler(SmsBlend smsBlend, SmsConfig config) {
this.smsBlend = smsBlend;
this.config = config;
}
public static SmsInvocationHandler newSmsInvocationHandler(SmsBlend smsBlend, SmsConfig config){
return new SmsInvocationHandler(smsBlend,config);
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Object result = null;
if ("sendMessage".equals(method.getName()) || "massTexting".equals(method.getName())) {
//取手机号作为参数
String phone = (String) objects[0];
SmsBlendException smsBlendException = restrictedProcess.process(config,phone);
if (!Objects.isNull(smsBlendException)) {
throw smsBlendException;
}
}
result = method.invoke(smsBlend, objects);
return result;
}
/**
* 设置 restrictedProcess
*/
public static void setRestrictedProcess(RestrictedProcess restrictedProcess) {
SmsInvocationHandler.restrictedProcess = restrictedProcess;
}
}

View File

@ -0,0 +1,10 @@
package org.dromara.sms4j.api.universal;
/**
* SupplierConfig
* <p> 空接口无含义只为标定配置类的额外类型
* @author :Wind
* 2023/5/16 15:14
**/
public interface SupplierConfig {
}

View File

@ -1,136 +0,0 @@
package org.dromara.sms4j.autoimmit.aop;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.config.SmsConfig;
import org.dromara.sms4j.autoimmit.utils.RedisUtils;
import org.dromara.sms4j.comm.utils.SmsUtil;
import org.dromara.sms4j.comm.utils.TimeExpiredPoolCache;
import org.dromara.sms4j.autoimmit.utils.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
@Aspect
@Slf4j
public class AopAdvice {
private static final Long minTimer = 60 * 1000L;
private static final Long accTimer = 24 * 60 * 60 * 1000L;
private static final String REDIS_KEY = "sms:restricted:";
@Autowired
private SmsConfig config;
@Autowired
private SpringUtil springUtil;
@Pointcut("@annotation(org.dromara.sms4j.comm.annotation.Restricted)")
public void restricted() {
}
@Around("restricted()")
public Object restrictedSendMessage(ProceedingJoinPoint p) throws Throwable {
String args = "";
ArrayList<String> argsList = new ArrayList<>();
try {
args = (String) p.getArgs()[0];
} catch (Exception e) {
for (Object o : (ArrayList<?>) p.getArgs()[0]) {
argsList.add((String) o);
}
}
SmsBlendException process = redisProcess(args);
if (process != null) {
throw process;
}
argsList.forEach(f -> {
SmsBlendException proce = null;
try {
proce = redisProcess(f);
} catch (Exception e) {
log.error(e.getMessage());
throw new RuntimeException(e);
}
if (proce != null) {
throw proce;
}
});
return p.proceed();
}
private SmsBlendException process(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 +",number of short messages reached the maximum today");
return new SmsBlendException("The phone:", args + " Text messages are sent too often");
}
} else {
instance.put(args, 1, minTimer);
}
}
return null;
}
private SmsBlendException redisProcess(String args) throws Exception{
if (config.getRedisCache().equals("false")){
return process(args);
}
RedisUtils redis = SpringUtil.getBean(RedisUtils.class);
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;
}
}

View File

@ -0,0 +1,54 @@
package org.dromara.sms4j.autoimmit.aop;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.smsProxy.RestrictedProcess;
import org.dromara.sms4j.autoimmit.utils.RedisUtils;
import org.dromara.sms4j.autoimmit.utils.SpringUtil;
import org.dromara.sms4j.comm.config.SmsConfig;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.utils.SmsUtil;
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 {
RedisUtils redis = SpringUtil.getBean(RedisUtils.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;
}
}

View File

@ -1,17 +1,17 @@
package org.dromara.sms4j.autoimmit.config;
import org.dromara.sms4j.autoimmit.aop.AopAdvice;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.smsProxy.SmsInvocationHandler;
import org.dromara.sms4j.autoimmit.aop.RestrictedProcessImpl;
import org.dromara.sms4j.autoimmit.utils.ConfigUtil;
import org.dromara.sms4j.autoimmit.utils.RedisUtils;
import org.dromara.sms4j.autoimmit.utils.SpringUtil;
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.enumerate.ConfigType;
import org.dromara.sms4j.comm.enumerate.SupplierType;
import org.dromara.sms4j.comm.factory.BeanFactory;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.core.SupplierSqlConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ -71,21 +71,21 @@ public class SmsAutowiredConfig {
return new SupplierSqlConfig();
}
void init(){
/* 如果配置中启用了redis则注入redis工具*/
if (BeanFactory.getSmsConfig().getRedisCache()){
springUtil.createBean(RedisUtils.class);
log.debug("The redis cache is enabled for sms-aggregation");
SmsInvocationHandler.setRestrictedProcess(new RestrictedProcessImpl());
log.debug("The redis cache is enabled for sms4j");
}
/* 如果启用了短信限制则注入AOP组件*/
if (BeanFactory.getSmsConfig().getRestricted()){
springUtil.createBean(AopAdvice.class);
log.debug("SMS restriction is enabled");
}
// if (BeanFactory.getSmsConfig().getRestricted()){
// springUtil.createBean(AopAdvice.class);
// log.debug("SMS restriction is enabled");
// }
//打印banner
if (BeanFactory.getSmsConfig().getIsPrint()){
SmsBanner.PrintBanner("V 2.0.1");
SmsBanner.PrintBanner(Constant.VERSION);
}
}
}

View File

@ -1,9 +1,10 @@
package org.dromara.sms4j.autoimmit.config;
import org.dromara.sms4j.emay.config.EmayConfig;
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.tencent.config.TencentConfig;
@ -72,4 +73,13 @@ public class SupplierConfig {
protected EmayConfig emayConfig(){
return SupplierFactory.getEmayConfig();
}
/**
* 天翼云短信差异化配置
*/
@Bean
@ConfigurationProperties(prefix = "sms.ctyun")
protected CtyunConfig ctyunConfig(){
return SupplierFactory.getCtyunConfig();
}
}

View File

@ -8,7 +8,11 @@ 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.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

View File

@ -15,11 +15,6 @@
<description>sms4j-comm</description>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-core</artifactId>

View File

@ -1,8 +1,8 @@
package org.dromara.sms4j.comm.config;
import org.dromara.sms4j.comm.enumerate.ConfigType;
import lombok.Data;
import org.dromara.sms4j.comm.enumerate.ConfigType;
@Data
public class SmsConfig {

View File

@ -1,9 +1,6 @@
package org.dromara.sms4j.comm.constant;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Constant
* <p> 短信应用常量
@ -12,6 +9,8 @@ import java.nio.charset.StandardCharsets;
* 2023/3/31 19:33
**/
public abstract class Constant {
/** 项目版本号*/
public static final String VERSION = "V 2.1.0";
/**
* 用于格式化鉴权头域,"Authorization"参数赋值

View File

@ -1,6 +1,7 @@
package org.dromara.sms4j.comm.delayedTime;
import java.util.*;
import java.util.Timer;
import java.util.TimerTask;
/**
* <p>类名: DelayedTime

View File

@ -1,41 +0,0 @@
package org.dromara.sms4j.comm.enumerate;
/**
* SupplierType
* <p> 短信供应商枚举
* @author :Wind
* 2023/4/7 20:55
**/
public enum SupplierType {
/** 阿里云*/
ALIBABA("阿里云短信"),
/** 华为云*/
HUAWEI("华为云短信"),
/** 云片*/
YUNPIAN("云片短信"),
/** 腾讯云*/
TENCENT("腾讯云短信"),
/** 合一短信*/
UNI_SMS("合一短信"),
/** 京东云 */
JD_CLOUD("京东云短信"),
/** 容联云 */
CLOOPEN("容联云短信"),
/**
* 亿美软通
*/
EMAY("亿美软通"),
;
private final String name;
SupplierType(String name) {
this.name = name;
}
public String getName() {
return name;
}
}

View File

@ -5,7 +5,11 @@ 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.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Hashtable;
import java.util.Map;

View File

@ -1,45 +1,37 @@
package org.dromara.sms4j.comm.utils;
import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import org.dromara.sms4j.comm.exception.SmsSqlException;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONUtil;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Random;
/**
* @author wind
*/
public class SmsUtil {
private SmsUtil() {
} //私有构造防止实例化
/**
* <p>说明生成一个指定长度的随机字符串包含大小写英文字母和数字但不包含符号
*
* @param len 要生成的字符串的长度
* getRandomString
* getRandomString
* @author :Wind
*/
public static String getRandomString(int len) {
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
StringBuilder sb = new StringBuilder();
try {
Random random = SecureRandom.getInstanceStrong();
for (int i = 0; i < len; i++) {
int number = random.nextInt(62);
sb.append(str.charAt(number));
}
} catch (NoSuchAlgorithmException e){
throw new RuntimeException(e);
}
return sb.toString();
return RandomUtil.randomString(RandomUtil.BASE_CHAR_NUMBER + RandomUtil.BASE_CHAR.toUpperCase(), len);
}
/**
* <p>说明获取一个长度为6的随机字符串
*getRandomString
* getRandomString
*
* @author :Wind
*/
public static String getRandomString() {
@ -48,37 +40,29 @@ public class SmsUtil {
/**
* <p>说明生成一个指定长度的只有数字组成的随机字符串
*
* @param len 要生成的长度
* getRandomInt
* getRandomInt
* @author :Wind
*/
public static String getRandomInt(int len) {
String str = "0123456789";
StringBuilder sb = new StringBuilder();
try {
Random random = SecureRandom.getInstanceStrong();
for (int i = 0; i < len; i++) {
int number = random.nextInt(10);
sb.append(str.charAt(number));
}
} catch (NoSuchAlgorithmException e){
throw new RuntimeException(e);
}
return sb.toString();
return RandomUtil.randomString(RandomUtil.BASE_NUMBER, len);
}
/**
* 指定元素是否为null或者空字符串
*
* @param str 指定元素
* @return 是否为null或者空字符串
* @author :Wind
*/
public static boolean isEmpty(Object str) {
return str == null || "".equals(str);
return ObjectUtil.isEmpty(str);
}
/**
* 指定元素是否不为 (null或者空字符串)
*
* @param str 指定元素
* @return 是否为null或者空字符串
* @author :Wind
@ -87,43 +71,72 @@ public class SmsUtil {
return !isEmpty(str);
}
public static String listToString(List<String> list) {
StringBuilder str = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
if (i == list.size() - 1) {
str.append(list.get(i));
} else {
str.append(list.get(i));
str.append(",");
}
}
return str.toString();
}
/**
* jsonForObject
* jsonForObject
* <p>将json字符串转化为指定的对象
*
* @author :Wind
*/
*/
public static <T> T jsonForObject(String json, Class<T> t) {
try {
return json == null||"".equals(json)?null: JSONObject.toJavaObject(JSONObject.parseObject(json), t);
} catch (JSONException e) {
throw new SmsSqlException("json sequence exception" + e.getMessage());
}
return JSONUtil.toBean(json, t);
}
/**
* copyBean
* copyBean
* <p>拷贝bean只有源对象不为null才会拷贝
*
* @param t 源对象
* @param m 目标对象
* @author :Wind
*/
public static <T,M>void copyBean(T t,M m){
if (t != null){
BeanUtil.copyProperties(t, m);
*/
public static <T, M> void copyBean(T t, M m) {
BeanUtil.copyProperties(t, m);
}
/**
* getNewMap
* <p>获取一个新的空LinkedHashMap
*
* @return 空的 LinkedHashMap 实例
* @author :Wind
*/
public static LinkedHashMap<String, String> getNewMap() {
return new LinkedHashMap<>();
}
/**
* listToString
* <p>将list转化为string元素之间使用逗号分隔此方法只支持list内部元素为String类型的
*
* @param list 要转换的list
* @author :Wind
*/
public static String listToString(List<String> list) {
return CollUtil.join(list, ",");
}
/**
* conjunction 为分隔符将集合转换为字符串
*
* @param list 集合
* @return 结果字符串
*/
public static String arrayToString(List<String> list) {
return CollUtil.join(list, ",", "+86", "");
}
/**
* List +86后转 数组
*
* @param list 集合
* @return 结果字符串
*/
public static String[] listToArray(List<String> list) {
List<String> toStr = new ArrayList<>();
for (String s : list) {
toStr.add("+86" + s);
}
return toStr.toArray(new String[list.size()]);
}
}

View File

@ -1,9 +1,9 @@
package org.dromara.sms4j.comm.utils;
import com.alibaba.fastjson.JSONObject;
import org.dromara.sms4j.comm.exception.SmsBlendException;
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;
@ -66,8 +66,7 @@ public class TimeExpiredPoolCache {
private static boolean persistenceInit() {
String path = FileTool.getPath() + FILE_TYPE;
try {
DataWrapper d = JSONObject.parseObject(FileTool.readFile(path), DataWrapper.class);
DataWrapper d = JSONUtil.toBean(FileTool.readFile(path), DataWrapper.class);
if (dataPool != null) {
return true;
}
@ -95,7 +94,7 @@ public class TimeExpiredPoolCache {
private static void persistence() {
String path = FileTool.getPath() + FILE_TYPE;
FileTool.createFile(path);
FileTool.writeFile(new File(path), JSONObject.toJSONString(dataPool), false);
FileTool.writeFile(new File(path), JSONUtil.toJsonStr(dataPool), false);
}
/**

View File

@ -1,21 +0,0 @@
package org.dromara.sms4j.comm.utils.http;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
public class HttpJsonTool {
public static <T> T getJSONBody(Object response,Class<T>t){
return JSONObject.parseObject(response.toString(), t);
}
public static <T> T getJSONBody(String response,Class<T>t){
return JSONObject.parseObject(response, t);
}
public static JSONObject getJSONObject(Object obj){
return JSONObject.parseObject(obj.toString());
}
}

View File

@ -2,13 +2,14 @@ package org.dromara.sms4j.core;
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
import org.dromara.sms4j.cloopen.config.CloopenConfig;
import org.dromara.sms4j.comm.enumerate.SupplierType;
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.provider.enumerate.SupplierType;
import org.dromara.sms4j.tencent.config.TencentConfig;
import org.dromara.sms4j.unisms.config.UniConfig;
import org.dromara.sms4j.yunpian.config.YunpianConfig;
@ -48,6 +49,7 @@ public class SupplierSqlConfig {
yunPian();
cloopen();
emay();
ctyun();
}
public SupplierSqlConfig() {
@ -136,4 +138,14 @@ public class SupplierSqlConfig {
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);
}
}

View File

@ -1,14 +1,26 @@
package org.dromara.sms4j.core.config;
import org.dromara.sms4j.comm.enumerate.SupplierType;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.emay.config.EmayConfig;
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.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.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;
/**
@ -21,103 +33,104 @@ public class SupplierFactory {
private SupplierFactory() {
}
/** 阿里云差异化配置*/
private static AlibabaConfig alibabaConfig;
/** 华为云差异化配置*/
private static HuaweiConfig huaweiConfig;
/** 合一短信差异化配置*/
private static UniConfig uniConfig;
/** 腾讯云短信差异化配置*/
private static TencentConfig tencentConfig;
/** 云片短信差异配置*/
private static YunpianConfig yunpianConfig;
/** 京东云短信差异配置 */
private static JdCloudConfig jdCloudConfig;
/** 容联云短信差异配置 */
private static CloopenConfig cloopenConfig;
/**
* 阿里云配置获取
*/
public static AlibabaConfig getAlibabaConfig() {
return AlibabaFactory.instance().getConfig();
}
/**
* 亿美软通短信差异配置
* 华为云配置获取
*/
private static EmayConfig emayConfig;
/** 阿里云配置获取*/
public static AlibabaConfig getAlibabaConfig() {
if (alibabaConfig == null){
alibabaConfig = AlibabaConfig.builder().build();
}
return alibabaConfig;
}
/** 华为云配置获取*/
public static HuaweiConfig getHuaweiConfig() {
if (huaweiConfig == null){
huaweiConfig = HuaweiConfig.builder().build();
}
return huaweiConfig;
return HuaweiFactory.instance().getConfig();
}
/** 合一短信配置获取*/
/**
* 合一短信配置获取
*/
public static UniConfig getUniConfig() {
if (uniConfig == null){
uniConfig = UniConfig.builder().build();
}
return uniConfig;
return UniFactory.instance().getConfig();
}
/** 腾讯短信配置获取*/
/**
* 腾讯短信配置获取
*/
public static TencentConfig getTencentConfig() {
if (tencentConfig == null){
tencentConfig = TencentConfig.builder().build();
}
return tencentConfig;
return TencentFactory.instance().getConfig();
}
/** 云片短信配置获取*/
/**
* 云片短信配置获取
*/
public static YunpianConfig getYunpianConfig() {
if (yunpianConfig == null){
yunpianConfig = YunpianConfig.builder().build();
}
return yunpianConfig;
return YunPianFactory.instance().getConfig();
}
/** 京东云短信配置获取 */
/**
* 京东云短信配置获取
*/
public static JdCloudConfig getJdCloudConfig() {
if (jdCloudConfig == null){
jdCloudConfig = JdCloudConfig.builder().build();
}
return jdCloudConfig;
return JdCloudFactory.instance().getConfig();
}
/** 容联云短信配置获取 */
/**
* 容联云短信配置获取
*/
public static CloopenConfig getCloopenConfig() {
if (cloopenConfig == null){
cloopenConfig = CloopenConfig.builder().build();
}
return cloopenConfig;
return CloopenFactory.instance().getConfig();
}
/**
* 亿美软通配置获取
*/
public static EmayConfig getEmayConfig() {
if (emayConfig == null) {
emayConfig = EmayConfig.builder().build();
return EmayFactory.instance().getConfig();
}
/**
* 天翼云配置获取
*/
public static CtyunConfig getCtyunConfig() {
return CtyunFactory.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 {
throw new SmsBlendException("Loading failure! Please check the configuration type.");
}
return emayConfig;
}
/**
* 设置 alibabaConfig
*/
public static void setAlibabaConfig(AlibabaConfig alibabaConfig) {
SupplierFactory.alibabaConfig = alibabaConfig;
AlibabaFactory.instance().setConfig(alibabaConfig);
SmsFactory.refresh(SupplierType.ALIBABA);
}
@ -125,7 +138,7 @@ public class SupplierFactory {
* 设置 huaweiConfig
*/
public static void setHuaweiConfig(HuaweiConfig huaweiConfig) {
SupplierFactory.huaweiConfig = huaweiConfig;
HuaweiFactory.instance().setConfig(huaweiConfig);
SmsFactory.refresh(SupplierType.HUAWEI);
}
@ -133,7 +146,7 @@ public class SupplierFactory {
* 设置 uniConfig
*/
public static void setUniConfig(UniConfig uniConfig) {
SupplierFactory.uniConfig = uniConfig;
UniFactory.instance().setConfig(uniConfig);
SmsFactory.refresh(SupplierType.UNI_SMS);
}
@ -141,7 +154,7 @@ public class SupplierFactory {
* 设置 tencentConfig
*/
public static void setTencentConfig(TencentConfig tencentConfig) {
SupplierFactory.tencentConfig = tencentConfig;
TencentFactory.instance().setConfig(tencentConfig);
SmsFactory.refresh(SupplierType.TENCENT);
}
@ -149,7 +162,7 @@ public class SupplierFactory {
* 设置 yunpianConfig
*/
public static void setYunpianConfig(YunpianConfig yunpianConfig) {
SupplierFactory.yunpianConfig = yunpianConfig;
YunPianFactory.instance().setConfig(yunpianConfig);
SmsFactory.refresh(SupplierType.YUNPIAN);
}
@ -157,7 +170,7 @@ public class SupplierFactory {
* 设置 jdCloudConfig
*/
public static void setJdCloudConfig(JdCloudConfig jdCloudConfig) {
SupplierFactory.jdCloudConfig = jdCloudConfig;
JdCloudFactory.instance().setConfig(jdCloudConfig);
SmsFactory.refresh(SupplierType.JD_CLOUD);
}
@ -165,7 +178,7 @@ public class SupplierFactory {
* 设置 cloopenConfig
*/
public static void setCloopenConfig(CloopenConfig cloopenConfig) {
SupplierFactory.cloopenConfig = cloopenConfig;
CloopenFactory.instance().setConfig(cloopenConfig);
SmsFactory.refresh(SupplierType.CLOOPEN);
}
@ -173,7 +186,15 @@ public class SupplierFactory {
* 设置 emayConfig
*/
public static void setEmayConfig(EmayConfig emayConfig) {
SupplierFactory.emayConfig = emayConfig;
EmayFactory.instance().setConfig(emayConfig);
SmsFactory.refresh(SupplierType.EMAY);
}
/**
* 设置 ctyunConfig
*/
public static void setCtyunConfig(CtyunConfig ctyunConfig) {
CtyunFactory.instance().setConfig(ctyunConfig);
SmsFactory.refresh(SupplierType.CTYUN);
}
}

View File

@ -1,17 +1,16 @@
package org.dromara.sms4j.core.factory;
import org.dromara.sms4j.aliyun.config.AlibabaSmsConfig;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.cloopen.config.CloopenSmsConfig;
import org.dromara.sms4j.comm.enumerate.SupplierType;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.api.smsProxy.SmsInvocationHandler;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.core.SupplierSqlConfig;
import org.dromara.sms4j.core.config.SupplierFactory;
import org.dromara.sms4j.emay.config.EmaySmsConfig;
import org.dromara.sms4j.huawei.config.HuaweiSmsConfig;
import org.dromara.sms4j.jdcloud.config.JdCloudSmsConfig;
import org.dromara.sms4j.tencent.config.TencentSmsConfig;
import org.dromara.sms4j.unisms.config.UniSmsConfig;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
import org.dromara.sms4j.provider.enumerate.SupplierType;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* SmsFactory
@ -23,6 +22,9 @@ import org.dromara.sms4j.unisms.config.UniSmsConfig;
* 2023/4/8 15:55
**/
public abstract class SmsFactory {
private static Map<SupplierType, SmsBlend> beans = new HashMap<>();
private SmsFactory() {
}
@ -34,23 +36,8 @@ public abstract class SmsFactory {
* @author :Wind
*/
public static SmsBlend createSmsBlend(SupplierType supplierType) {
switch (supplierType) {
case ALIBABA:
return AlibabaSmsConfig.createAlibabaSms(SupplierFactory.getAlibabaConfig());
case HUAWEI:
return HuaweiSmsConfig.createHuaweiSms(SupplierFactory.getHuaweiConfig());
case UNI_SMS:
return UniSmsConfig.createUniSms(SupplierFactory.getUniConfig());
case TENCENT:
return TencentSmsConfig.createTencentSms(SupplierFactory.getTencentConfig());
case JD_CLOUD:
return JdCloudSmsConfig.createJdCloudSms(SupplierFactory.getJdCloudConfig());
case CLOOPEN:
return CloopenSmsConfig.createCloopenSms(SupplierFactory.getCloopenConfig());
case EMAY:
return EmaySmsConfig.createEmaySms(SupplierFactory.getEmayConfig());
}
throw new SmsBlendException("An attempt to construct a SmsBlend object failed. Please check that the enumeration is valid");
BaseProviderFactory providerFactory = supplierType.getProviderFactory();
return providerFactory.createSms(providerFactory.getConfig());
}
/**
@ -61,13 +48,9 @@ public abstract class SmsFactory {
* @author :Wind
*/
public static void refresh() {
AlibabaSmsConfig.refresh(SupplierFactory.getAlibabaConfig());
HuaweiSmsConfig.refresh(SupplierFactory.getHuaweiConfig());
UniSmsConfig.refresh(SupplierFactory.getUniConfig());
TencentSmsConfig.refresh(SupplierFactory.getTencentConfig());
JdCloudSmsConfig.refresh(SupplierFactory.getJdCloudConfig());
CloopenSmsConfig.refresh(SupplierFactory.getCloopenConfig());
EmaySmsConfig.refresh(SupplierFactory.getEmayConfig());
for(SupplierType type : SupplierType.values()) {
refresh(type);
}
}
/**
@ -78,31 +61,8 @@ public abstract class SmsFactory {
* @author :Wind
*/
public static void refresh(SupplierType supplierType) {
switch (supplierType) {
case ALIBABA:
AlibabaSmsConfig.refresh(SupplierFactory.getAlibabaConfig());
break;
case HUAWEI:
HuaweiSmsConfig.refresh(SupplierFactory.getHuaweiConfig());
break;
case UNI_SMS:
UniSmsConfig.refresh(SupplierFactory.getUniConfig());
break;
case TENCENT:
TencentSmsConfig.refresh(SupplierFactory.getTencentConfig());
break;
case JD_CLOUD:
JdCloudSmsConfig.refresh(SupplierFactory.getJdCloudConfig());
break;
case CLOOPEN:
CloopenSmsConfig.refresh(SupplierFactory.getCloopenConfig());
break;
case EMAY:
EmaySmsConfig.refresh(SupplierFactory.getEmayConfig());
break;
default:
throw new SmsBlendException("An attempt to construct a SmsBlend object failed. Please check that the enumeration is valid");
}
BaseProviderFactory providerFactory = supplierType.getProviderFactory();
providerFactory.refresh(providerFactory.getConfig());
}
/**
@ -115,4 +75,44 @@ public abstract class SmsFactory {
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);
}
return smsBlend;
}
/**
* refreshRestrictedSmsBlend
* <p>刷新带有短信拦截的对象实现
* @param supplierType 厂商枚举
* @author :Wind
*/
public static void refreshRestrictedSmsBlend(SupplierType supplierType) {
refresh(supplierType);
beans.put(supplierType,getSmsBlend(supplierType));
}
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
);
}
}

View File

@ -2,7 +2,8 @@ package org.dromara.sms4j.core.load;
import org.dromara.sms4j.api.SmsBlend;
import java.util.*;
import java.util.ArrayList;
import java.util.List;
/**
* SmsLoad

View File

@ -5,13 +5,14 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.comm.config.BaseConfig;
@Data
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class AlibabaConfig extends BaseConfig {
public class AlibabaConfig extends BaseConfig implements SupplierConfig {
/**
* 模板变量名称

View File

@ -0,0 +1,87 @@
package org.dromara.sms4j.aliyun.config;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.aliyun.service.AlibabaSmsImpl;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
/**
* AlibabaSmsConfig
* <p> 阿里巴巴对象建造者
*
* @author :Wind
* 2023/4/8 14:54
**/
@Slf4j
public class AlibabaFactory implements BaseProviderFactory<AlibabaSmsImpl, AlibabaConfig> {
private static AlibabaSmsImpl alibabaSms;
private static final AlibabaFactory INSTANCE = new AlibabaFactory();
private static final class ConfigHolder {
private static AlibabaConfig config = AlibabaConfig.builder().build();
}
private AlibabaFactory() {
}
/**
* 获取建造者实例
* @return 建造者实例
*/
public static AlibabaFactory instance() {
return INSTANCE;
}
/**
* 创建短信实现对象
* @param alibabaConfig 短信配置对象
* @return 短信实现对象
*/
@Override
public AlibabaSmsImpl createSms(AlibabaConfig alibabaConfig) {
if (alibabaSms == null) {
alibabaSms = new AlibabaSmsImpl(
alibabaConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime());
}
return alibabaSms;
}
/**
* 刷新短信实现对象
* @param alibabaConfig 短信配置对象
* @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;
}
}

View File

@ -1,62 +0,0 @@
package org.dromara.sms4j.aliyun.config;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.aliyun.service.AlibabaSmsImpl;
import org.dromara.sms4j.comm.factory.BeanFactory;
/**
* AlibabaSmsConfig
* <p> 阿里巴巴对象建造者
*
* @author :Wind
* 2023/4/8 14:54
**/
@Slf4j
public class AlibabaSmsConfig {
private static AlibabaSmsImpl alibabaSms;
private static AlibabaSmsConfig alibabaSmsConfig;
/**
* getAlibabaSms
* <p> 建造一个短信实现对像
*
* @author :Wind
*/
public static AlibabaSmsImpl createAlibabaSms(AlibabaConfig alibabaConfig) {
if (alibabaSmsConfig == null) {
alibabaSmsConfig = new AlibabaSmsConfig();
}
if (alibabaSms == null) {
alibabaSms = new AlibabaSmsImpl(
alibabaConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime());
}
return alibabaSms;
}
/**
* refresh
* <p> 刷新对象
*
* @author :Wind
*/
public static AlibabaSmsImpl refresh(AlibabaConfig alibabaConfig) {
// 如果配置对象为空则创建一个
if (alibabaSmsConfig == null) {
alibabaSmsConfig = new AlibabaSmsConfig();
}
//重新构造一个实现对象
alibabaSms = new AlibabaSmsImpl(
alibabaConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime());
return alibabaSms;
}
private AlibabaSmsConfig() {
}
}

View File

@ -1,24 +1,21 @@
package org.dromara.sms4j.aliyun.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dtflys.forest.config.ForestConfiguration;
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.SmsBlend;
import org.dromara.sms4j.api.callback.CallBack;
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.factory.BeanFactory;
import org.dromara.sms4j.comm.utils.SmsUtil;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
/**
* <p>类名: AlibabaSmsImpl
@ -29,27 +26,19 @@ import java.util.concurrent.Executor;
**/
@Slf4j
public class AlibabaSmsImpl implements SmsBlend {
public class AlibabaSmsImpl extends AbstractSmsBlend {
private final AlibabaConfig alibabaSmsConfig;
private final Executor pool;
private final DelayedTime delayed;
private final ForestConfiguration http = BeanFactory.getForestConfiguration();
/**
* AlibabaSmsImpl
* <p>构造器用于构造短信实现模块
*
* @author :Wind
*/
public AlibabaSmsImpl(AlibabaConfig alibabaSmsConfig, Executor pool, DelayedTime delayedTime) {
super(pool, delayedTime);
this.alibabaSmsConfig = alibabaSmsConfig;
this.pool = pool;
this.delayed = delayedTime;
}
@Override
@ -63,7 +52,7 @@ public class AlibabaSmsImpl implements SmsBlend {
@Override
@Restricted
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
String messageStr = JSON.toJSONString(messages);
String messageStr = JSONUtil.toJsonStr(messages);
return getSmsResponse(phone, messageStr, templateId);
}
@ -78,12 +67,12 @@ public class AlibabaSmsImpl implements SmsBlend {
@Override
@Restricted
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
String messageStr = JSON.toJSONString(messages);
return getSmsResponse(arrayToString(phones), messageStr, templateId);
String messageStr = JSONUtil.toJsonStr(messages);
return getSmsResponse(SmsUtil.arrayToString(phones), messageStr, templateId);
}
private SmsResponse getSmsResponse(String phone, String message, String templateId) {
SmsResponse smsResponse = new SmsResponse();
AtomicReference<SmsResponse> reference = new AtomicReference<>();
String requestUrl;
String paramStr;
try {
@ -94,100 +83,32 @@ public class AlibabaSmsImpl implements SmsBlend {
throw new SmsBlendException(e.getMessage());
}
log.debug("requestUrl {}", requestUrl);
http.post(requestUrl)
super.http.post(requestUrl)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addBody(paramStr)
.onSuccess(((data, req, res) -> {
JSONObject jsonBody = res.get(JSONObject.class);
log.info(jsonBody.toJSONString());
reference.set(this.getResponse(res.get(JSONObject.class)));
}))
.onError((ex, req, res) -> {
JSONObject jsonBody = res.get(JSONObject.class);
log.info(jsonBody.toJSONString());
reference.set(this.getResponse(res.get(JSONObject.class)));
})
.execute();
return reference.get();
}
private SmsResponse getResponse(JSONObject resJson) {
SmsResponse smsResponse = new SmsResponse();
if (resJson == null) {
smsResponse.setErrorCode("500");
smsResponse.setErrMessage("aliyun send sms response is null.check param");
return smsResponse;
}
smsResponse.setCode(resJson.getStr("Code"));
smsResponse.setMessage(resJson.getStr("Message"));
if ("OK".equals(smsResponse.getCode())) {
smsResponse.setBizId(resJson.getStr("BizId"));
}
return smsResponse;
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message, CallBack callBack) {
CompletableFuture<SmsResponse> smsResponseCompletableFuture = CompletableFuture.supplyAsync(() -> sendMessage(phone, message), pool);
smsResponseCompletableFuture.thenAcceptAsync(callBack::callBack);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message) {
pool.execute(() -> {
sendMessage(phone, message);
});
}
@Override
@Restricted
public 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);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
pool.execute(() -> {
sendMessage(phone, templateId, messages);
});
}
@Override
@Restricted
public void delayedMessage(String phone, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, templateId, messages);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, templateId, messages);
}
}, delayedTime);
}
private String arrayToString(List<String> list) {
StringBuilder sb = new StringBuilder();
for (String s : list) {
sb.append(",").append("+86").append(s);
}
return sb.substring(1);
}
}

View File

@ -1,14 +1,20 @@
package org.dromara.sms4j.aliyun.utils;
import cn.hutool.core.codec.Base64;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
import org.dromara.sms4j.comm.constant.Constant;
import javax.crypto.Mac;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SimpleTimeZone;
import java.util.TreeMap;
import java.util.UUID;
/**
* @author Richard
@ -21,18 +27,18 @@ public class AliyunUtils {
*/
private static final String ALGORITHM = "HMAC-SHA1";
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
public static String generateSendSmsRequestUrl(AlibabaConfig alibabaConfig, String message, String phone, String templateId) throws Exception {
// 这里一定要设置GMT时区
sdf.setTimeZone(new SimpleTimeZone(0, "GMT"));
SDF.setTimeZone(new SimpleTimeZone(0, "GMT"));
Map<String, String> paras = new HashMap<>();
// 1. 公共请求参数
paras.put("SignatureMethod", ALGORITHM);
paras.put("SignatureNonce", UUID.randomUUID().toString());
paras.put("AccessKeyId", alibabaConfig.getAccessKeyId());
paras.put("SignatureVersion", "1.0");
paras.put("Timestamp", sdf.format(new Date()));
paras.put("Timestamp", SDF.format(new Date()));
paras.put("Format", "JSON");
paras.put("Action", alibabaConfig.getAction());
paras.put("Version", alibabaConfig.getVersion());
@ -80,10 +86,8 @@ public class AliyunUtils {
* @param stringToSign 待生成签名的字符串
*/
private static String sign(String accessSecret, String stringToSign) throws Exception {
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(new javax.crypto.spec.SecretKeySpec(accessSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
return Base64.encode(signData);
HMac hMac = new HMac(HmacAlgorithm.HmacSHA1, accessSecret.getBytes());
return hMac.digestBase64(stringToSign,StandardCharsets.UTF_8, false);
}
/**

View File

@ -5,6 +5,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.comm.config.BaseConfig;
/**
@ -17,7 +18,7 @@ import org.dromara.sms4j.comm.config.BaseConfig;
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class CloopenConfig extends BaseConfig {
public class CloopenConfig extends BaseConfig implements SupplierConfig {
/**
* 应用 ID

View File

@ -0,0 +1,77 @@
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;
/**
* 容联云短信配置
*
* @author Charles7c
* @since 2023/4/10 22:10
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CloopenFactory implements BaseProviderFactory<CloopenSmsImpl, CloopenConfig> {
private static CloopenSmsImpl cloopenSms;
private static final CloopenFactory INSTANCE = new CloopenFactory();
private static final class ConfigHolder {
private static CloopenConfig config = CloopenConfig.builder().build();
}
/**
* 获取建造者实例
* @return 建造者实例
*/
public static CloopenFactory instance() {
return INSTANCE;
}
/**
* 创建容连云短信实现对象
* @param cloopenConfig 短信配置对象
* @return 短信实现对象
*/
@Override
public CloopenSmsImpl createSms(CloopenConfig cloopenConfig) {
if (cloopenSms == null) {
cloopenSms = new CloopenSmsImpl(cloopenConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
}
return cloopenSms;
}
/**
* 刷新容连云短信实现对象
* @param cloopenConfig 短信配置对象
* @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;
}
}

View File

@ -1,44 +0,0 @@
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;
/**
* 容联云短信配置
*
* @author Charles7c
* @since 2023/4/10 22:10
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CloopenSmsConfig {
private static CloopenSmsImpl cloopenSms;
private static CloopenSmsConfig cloopenSmsConfig;
/**
* 创建容联云短信实现
*/
public static CloopenSmsImpl createCloopenSms(CloopenConfig cloopenConfig) {
if (cloopenSmsConfig == null) {
cloopenSmsConfig = new CloopenSmsConfig();
}
if (cloopenSms == null) {
cloopenSms = new CloopenSmsImpl(cloopenConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
}
return cloopenSms;
}
/**
* 刷新对象
*/
public static CloopenSmsImpl refresh(CloopenConfig cloopenConfig) {
if (cloopenSmsConfig == null) {
cloopenSmsConfig = new CloopenSmsConfig();
}
cloopenSms = new CloopenSmsImpl(cloopenConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
return cloopenSms;
}
}

View File

@ -4,8 +4,7 @@ import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import com.dtflys.forest.Forest;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.callback.CallBack;
import org.dromara.sms4j.api.AbstractSmsBlend;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.cloopen.api.CloopenRestApi;
import org.dromara.sms4j.cloopen.config.CloopenConfig;
@ -13,8 +12,10 @@ import org.dromara.sms4j.cloopen.util.CloopenHelper;
import org.dromara.sms4j.comm.annotation.Restricted;
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
/**
@ -24,20 +25,15 @@ import java.util.concurrent.Executor;
* @since 2023/4/10 22:10
*/
@Slf4j
public class CloopenSmsImpl implements SmsBlend {
public class CloopenSmsImpl extends AbstractSmsBlend {
private final CloopenRestApi restApi;
private final CloopenConfig config;
private final Executor pool;
private final DelayedTime delayed;
public CloopenSmsImpl(CloopenConfig config, Executor pool, DelayedTime delayed) {
super(pool,delayed);
this.config = config;
this.pool = pool;
this.delayed = delayed;
restApi = Forest.client(CloopenRestApi.class);
}
@ -72,74 +68,4 @@ public class CloopenSmsImpl implements SmsBlend {
paramMap.put("datas", messages.keySet().stream().map(messages::get).toArray(String[]::new));
return helper.request(restApi::sendSms, paramMap);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message, CallBack callBack) {
CompletableFuture<SmsResponse> smsResponseCompletableFuture = CompletableFuture.supplyAsync(() -> sendMessage(phone, message), pool);
smsResponseCompletableFuture.thenAcceptAsync(callBack::callBack);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message) {
pool.execute(() -> sendMessage(phone, message));
}
@Override
@Restricted
public 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);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
pool.execute(() -> sendMessage(phone, templateId, messages));
}
@Override
@Restricted
public void delayedMessage(String phone, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, templateId, messages);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, templateId, messages);
}
}, delayedTime);
}
}

View File

@ -0,0 +1,40 @@
package org.dromara.sms4j.ctyun.config;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.comm.config.BaseConfig;
/**
* <p>类名: CtyunConfig
* <p>说明 天翼云短信差异配置
*
* @author :bleachhtred
* 2023/5/12 15:06
**/
@Data
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class CtyunConfig extends BaseConfig implements SupplierConfig {
/**
* 模板变量名称
*/
private String templateName;
/**
* 请求地址
*/
@Builder.Default
private String requestUrl = "https://sms-global.ctapi.ctyun.cn/sms/api/v1";
/**
* 接口名称
*/
@Builder.Default
private String action = "SendSms";
}

View File

@ -0,0 +1,87 @@
package org.dromara.sms4j.ctyun.config;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.ctyun.service.CtyunSmsImpl;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
/**
* <p>类名: CtyunSmsConfig
* <p>说明 天翼云 云通信短信配置器
*
* @author :bleachhtred
* 2023/5/12 15:06
**/
@Slf4j
public class CtyunFactory implements BaseProviderFactory<CtyunSmsImpl, CtyunConfig> {
private static CtyunSmsImpl ctyunSms;
private static final CtyunFactory INSTANCE = new CtyunFactory();
private static final class ConfigHolder {
private static CtyunConfig config = CtyunConfig.builder().build();
}
private CtyunFactory() {
}
/**
* 获取建造者实例
* @return 建造者实例
*/
public static CtyunFactory instance() {
return INSTANCE;
}
/**
* getCtyunSms
* <p> 建造一个短信实现对像
*
* @author :bleachhtred
*/
@Override
public CtyunSmsImpl createSms(CtyunConfig ctyunConfig) {
if (ctyunSms == null) {
ctyunSms = new CtyunSmsImpl(
ctyunConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime());
}
return ctyunSms;
}
/**
* refresh
* <p> 刷新对象
*
* @author :bleachhtred
*/
@Override
public CtyunSmsImpl refresh(CtyunConfig ctyunConfig) {
//重新构造一个实现对象
ctyunSms = new CtyunSmsImpl(
ctyunConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime());
return ctyunSms;
}
/**
* 获取配置
* @return 配置对象
*/
@Override
public CtyunConfig getConfig() {
return ConfigHolder.config;
}
/**
* 设置配置
* @param config 配置对象
*/
@Override
public void setConfig(CtyunConfig config) {
ConfigHolder.config = config;
}
}

View File

@ -0,0 +1,101 @@
package org.dromara.sms4j.ctyun.service;
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 java.util.LinkedHashMap;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
/**
* <p>类名: CtyunSmsImpl
* <p>说明 天翼云短信实现
*
* @author :bleachhtred
* 2023/5/12 15:06
**/
@Slf4j
public class CtyunSmsImpl extends AbstractSmsBlend {
private final CtyunConfig ctyunConfig;
public CtyunSmsImpl(CtyunConfig ctyunConfig, Executor pool, DelayedTime delayedTime) {
super(pool, delayedTime);
this.ctyunConfig = ctyunConfig;
}
@Override
@Restricted
public SmsResponse sendMessage(String phone, String message) {
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put(ctyunConfig.getTemplateName(), message);
return sendMessage(phone, ctyunConfig.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(ctyunConfig.getTemplateName(), message);
return massTexting(phones, ctyunConfig.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);
}
private SmsResponse getSmsResponse(String phone, String message, String templateId) {
AtomicReference<SmsResponse> smsResponse = new AtomicReference<>();
String requestUrl;
String paramStr;
try {
requestUrl = ctyunConfig.getRequestUrl();
paramStr = CtyunUtils.generateParamJsonStr(ctyunConfig, phone, message, templateId);
} catch (Exception e) {
log.error("ctyun send message error", e);
throw new SmsBlendException(e.getMessage());
}
log.debug("requestUrl {}", requestUrl);
http.post(requestUrl)
.addHeader(CtyunUtils.signHeader(paramStr, ctyunConfig.getAccessKeyId(), ctyunConfig.getAccessKeySecret()))
.addBody(paramStr)
.onSuccess(((data, req, res) -> {
smsResponse.set(this.getResponse(res.get(JSONObject.class)));
}))
.onError((ex, req, res) -> {
smsResponse.set(this.getResponse(res.get(JSONObject.class)));
})
.execute();
return smsResponse.get();
}
private SmsResponse getResponse(JSONObject resJson) {
SmsResponse smsResponse = new SmsResponse();
smsResponse.setCode(resJson.getStr("code"));
smsResponse.setMessage(resJson.getStr("message"));
smsResponse.setBizId(resJson.getStr("requestId"));
return smsResponse;
}
}

View File

@ -0,0 +1,118 @@
package org.dromara.sms4j.ctyun.utils;
import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import cn.hutool.json.JSONUtil;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.ctyun.config.CtyunConfig;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CtyunUtils {
/**
* 获取签名时间戳
*/
private static String signatureTime(){
SimpleDateFormat timeFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'");
return timeFormat.format(new Date());
}
/**
* 获取签名请求头
*/
public static Map<String, String> signHeader(String body, String key, String secret){
Map<String, String> map = new ConcurrentHashMap<>();
// 构造时间戳
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
Date now = new Date();
String signatureDate = dateFormat.format(now);
String signatureTime = signatureTime();
// 构造请求流水号
String uuid = UUID.randomUUID().toString();
String calculateContentHash = getSHA256(body);
byte[] kTime = hmacSHA256(signatureTime.getBytes(), secret.getBytes());
byte[] kAk = hmacSHA256(key.getBytes(), kTime);
byte[] kDate = hmacSHA256(signatureDate.getBytes(), kAk);
// 构造待签名字符串
// 报文原封不动进行sha256摘要
String signatureStr = String.format("ctyun-eop-request-id:%s\neop-date:%s\n", uuid, signatureTime) + "\n\n" + calculateContentHash;
// 构造签名
String signature = Base64.getEncoder().encodeToString(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("ctyun-eop-request-id", uuid);
map.put("Eop-date", signatureTime);
map.put("Eop-Authorization", signHeader);
return map;
}
/**
* 生成请求body参数
*
* @param ctyunConfig 配置数据
* @param phone 手机号
* @param message 短信内容
* @param templateId 模板id
*/
public static String generateParamJsonStr(CtyunConfig ctyunConfig, String phone, String message, String templateId) {
Map<String, String> paramMap = new HashMap<>();
paramMap.put("action", ctyunConfig.getAction());
paramMap.put("phoneNumber", phone);
paramMap.put("signName", ctyunConfig.getSignature());
paramMap.put("templateParam", message);
paramMap.put("templateCode", templateId);
return JSONUtil.toJsonStr(paramMap);
}
private static String toHex(byte[] data) {
StringBuilder sb = new StringBuilder(data.length * 2);
for (byte b : data) {
String hex = Integer.toHexString(b);
if (hex.length() == 1) {
sb.append("0");
} else if (hex.length() == 8) {
hex = hex.substring(6);
}
sb.append(hex);
}
return sb.toString().toLowerCase(Locale.getDefault());
}
private static String getSHA256(String text) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
md.update(text.getBytes(StandardCharsets.UTF_8));
return toHex(md.digest());
} catch (NoSuchAlgorithmException var3) {
return null;
}
}
private static byte[] hmacSHA256(byte[] data, byte[] key){
try {
HMac hMac = new HMac(HmacAlgorithm.HmacSHA256, key);
return hMac.digest(data);
} catch (Exception e) {
throw new SmsBlendException(e.getMessage());
}
}
}

View File

@ -2,6 +2,7 @@ package org.dromara.sms4j.emay.config;
import lombok.Builder;
import lombok.Data;
import org.dromara.sms4j.api.universal.SupplierConfig;
/**
* @author Richard
@ -9,7 +10,7 @@ import lombok.Data;
*/
@Data
@Builder
public class EmayConfig {
public class EmayConfig implements SupplierConfig {
/** appKey*/
private String appId ;
/** appSecret */

View File

@ -0,0 +1,75 @@
package org.dromara.sms4j.emay.config;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.emay.service.EmaySmsImpl;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
/**
* EmaySmsConfig
* <p> Emay短信对象建造
*
* @author Richard
* @date 2023/04/11 12:00
* */
public class EmayFactory implements BaseProviderFactory<EmaySmsImpl, EmayConfig> {
private static EmaySmsImpl emaySms;
private static final EmayFactory INSTANCE = new EmayFactory();
private static final class ConfigHolder {
private static EmayConfig config = EmayConfig.builder().build();
}
private EmayFactory() {
}
/**
* 获取建造者实例
* @return 建造者实例
*/
public static EmayFactory instance() {
return INSTANCE;
}
/**
* 创建亿美软通短信实现对象
* @param emayConfig 短信配置对象
* @return 短信实现对象
*/
@Override
public EmaySmsImpl createSms(EmayConfig emayConfig) {
if (emaySms == null){
emaySms = new EmaySmsImpl(emayConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
}
return emaySms;
}
/**
* 刷新短信实现对象
* @param emayConfig 短信配置对象
* @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;
}
}

View File

@ -1,40 +0,0 @@
package org.dromara.sms4j.emay.config;
import org.dromara.sms4j.emay.service.EmaySmsImpl;
import org.dromara.sms4j.comm.factory.BeanFactory;
/**
* EmaySmsConfig
* <p> Emay短信对象建造
*
* @author Richard
* @date 2023/04/11 12:00
* */
public class EmaySmsConfig {
private static EmaySmsImpl emaySms;
private static EmaySmsConfig emaySmsConfig;
private EmaySmsConfig() {
}
/** 建造一个亿美软通短信实现*/
public static EmaySmsImpl createEmaySms(EmayConfig emayConfig) {
if (emaySmsConfig == null){
emaySmsConfig = new EmaySmsConfig();
}
if (emaySms == null){
emaySms = new EmaySmsImpl(emayConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
}
return emaySms;
}
/** 刷新对象*/
public static EmaySmsImpl refresh(EmayConfig emayConfig){
if (emaySmsConfig == null){
emaySmsConfig = new EmaySmsConfig();
}
emaySms = new EmaySmsImpl(emayConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
return emaySms;
}
}

View File

@ -1,45 +1,38 @@
package org.dromara.sms4j.emay.service;
import com.alibaba.fastjson.JSONObject;
import com.dtflys.forest.config.ForestConfiguration;
import org.dromara.sms4j.emay.config.EmayConfig;
import org.dromara.sms4j.emay.util.EmayBuilder;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.callback.CallBack;
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.factory.BeanFactory;
import org.dromara.sms4j.comm.utils.SmsUtil;
import org.dromara.sms4j.emay.config.EmayConfig;
import org.dromara.sms4j.emay.util.EmayBuilder;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import static org.dromara.sms4j.comm.utils.SmsUtil.listToString;
/**
* @author Richard
* @date 2023-04-11 12:00
*/
@Slf4j
public class EmaySmsImpl implements SmsBlend {
public class EmaySmsImpl extends AbstractSmsBlend {
public EmaySmsImpl(EmayConfig config, Executor pool, DelayedTime delayed) {
super(pool, delayed);
this.config = config;
this.pool = pool;
this.delayed = delayed;
}
private EmayConfig config;
private Executor pool;
private DelayedTime delayed;
private final ForestConfiguration http = BeanFactory.getForestConfiguration();
@Override
@Restricted
public SmsResponse sendMessage(String phone, String message) {
@ -71,7 +64,7 @@ public class EmaySmsImpl implements SmsBlend {
if (phones.size() > 500) {
throw new SmsBlendException("单次发送超过最大发送上限建议每次群发短信人数低于500");
}
return sendMessage(listToString(phones), message);
return sendMessage(SmsUtil.listToString(phones), message);
}
@Override
@ -84,77 +77,7 @@ public class EmaySmsImpl implements SmsBlend {
for (Map.Entry<String, String> entry : messages.entrySet()) {
list.add(entry.getValue());
}
return sendMessage(listToString(phones), EmayBuilder.listToString(list));
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message, CallBack callBack) {
CompletableFuture<SmsResponse> smsResponseCompletableFuture = CompletableFuture.supplyAsync(() -> sendMessage(phone, message), pool);
smsResponseCompletableFuture.thenAcceptAsync(callBack::callBack);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message) {
pool.execute(() -> sendMessage(phone, message));
}
@Override
@Restricted
public 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);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
pool.execute(() -> sendMessage(phone, templateId, messages));
}
@Override
@Restricted
public void delayedMessage(String phone, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, templateId, messages);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, templateId, messages);
}
}, delayedTime);
return sendMessage(SmsUtil.listToString(phones), EmayBuilder.listToString(list));
}
private SmsResponse getSendResponse(Map<String, Object> body, String requestUrl) {
@ -177,14 +100,25 @@ public class EmaySmsImpl implements SmsBlend {
private static SmsResponse getSmsResponse(JSONObject execute) {
SmsResponse smsResponse = new SmsResponse();
String code = execute.getString("code");
smsResponse.setCode(code);
if ("success".equalsIgnoreCase(code)) {
JSONObject data = execute.getJSONObject("data");
String smsId = data.getString("smsId");
smsResponse.setBizId(smsId);
if (execute == null) {
smsResponse.setErrorCode("500");
smsResponse.setErrMessage("emay send sms response is null.check param");
return smsResponse;
}
String code = execute.getStr("code");
if (SmsUtil.isEmpty(code)) {
smsResponse.setErrorCode("emay response code is null");
smsResponse.setErrMessage("emay is error");
} else {
smsResponse.setCode(code);
if ("success".equalsIgnoreCase(code)) {
JSONArray data = execute.getJSONArray("data");
JSONObject result = (JSONObject) data.get(0);
String smsId = result.getStr("smsId");
smsResponse.setBizId(smsId);
}
smsResponse.setData(execute);
}
smsResponse.setData(execute);
return smsResponse;
}
}

View File

@ -2,10 +2,11 @@ package org.dromara.sms4j.huawei.config;
import lombok.Builder;
import lombok.Data;
import org.dromara.sms4j.api.universal.SupplierConfig;
@Data
@Builder
public class HuaweiConfig {
public class HuaweiConfig implements SupplierConfig {
/** appKey*/
private String appKey ;

View File

@ -0,0 +1,67 @@
package org.dromara.sms4j.huawei.config;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.huawei.service.HuaweiSmsImpl;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
/**
* HuaweiSmsConfig
* <p> 华为短信对象建造
*
* @author :Wind
* 2023/4/8 15:27
**/
public class HuaweiFactory implements BaseProviderFactory<HuaweiSmsImpl, HuaweiConfig> {
private static HuaweiSmsImpl huaweiSms;
private static final HuaweiFactory INSTANCE = new HuaweiFactory();
private static final class ConfigHolder {
private static HuaweiConfig config = HuaweiConfig.builder().build();
}
private HuaweiFactory() {
}
/**
* 获取建造者实例
* @return 建造者实例
*/
public static HuaweiFactory instance() {
return INSTANCE;
}
/** 建造一个华为短信实现*/
@Override
public HuaweiSmsImpl createSms(HuaweiConfig huaweiConfig) {
if (huaweiSms == null){
huaweiSms = new HuaweiSmsImpl(huaweiConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
}
return huaweiSms;
}
/** 刷新对象*/
@Override
public HuaweiSmsImpl refresh(HuaweiConfig huaweiConfig){
huaweiSms = new HuaweiSmsImpl(huaweiConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
return huaweiSms;
}
/**
* 获取配置
* @return 配置对象
*/
@Override
public HuaweiConfig getConfig() {
return ConfigHolder.config;
}
/**
* 设置配置
* @param config 配置对象
*/
@Override
public void setConfig(HuaweiConfig config) {
ConfigHolder.config = config;
}
}

View File

@ -1,39 +0,0 @@
package org.dromara.sms4j.huawei.config;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.huawei.service.HuaweiSmsImpl;
/**
* HuaweiSmsConfig
* <p> 华为短信对象建造
*
* @author :Wind
* 2023/4/8 15:27
**/
public class HuaweiSmsConfig {
private static HuaweiSmsImpl huaweiSms;
private static HuaweiSmsConfig huaweiSmsConfig;
private HuaweiSmsConfig() {
}
/** 建造一个华为短信实现*/
public static HuaweiSmsImpl createHuaweiSms(HuaweiConfig huaweiConfig) {
if (huaweiSmsConfig == null){
huaweiSmsConfig = new HuaweiSmsConfig();
}
if (huaweiSms == null){
huaweiSms = new HuaweiSmsImpl(huaweiConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
}
return huaweiSms;
}
/** 刷新对象*/
public static HuaweiSmsImpl refresh(HuaweiConfig huaweiConfig){
if (huaweiSmsConfig == null){
huaweiSmsConfig = new HuaweiSmsConfig();
}
huaweiSms = new HuaweiSmsImpl(huaweiConfig, BeanFactory.getExecutor(),BeanFactory.getDelayedTime());
return huaweiSms;
}
}

View File

@ -2,8 +2,6 @@ package org.dromara.sms4j.huawei.entity;
import lombok.Data;
import java.util.Date;
/**
* SmsId
* <p> 短信ID列表

View File

@ -1,48 +1,40 @@
package org.dromara.sms4j.huawei.service;
import com.dtflys.forest.config.ForestConfiguration;
import org.dromara.sms4j.api.callback.CallBack;
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.factory.BeanFactory;
import org.dromara.sms4j.huawei.config.HuaweiConfig;
import org.dromara.sms4j.huawei.entity.HuaweiResponse;
import org.dromara.sms4j.huawei.utils.HuaweiBuilder;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.Executor;
import static org.dromara.sms4j.huawei.utils.HuaweiBuilder.listToString;
@Slf4j
public class HuaweiSmsImpl implements SmsBlend {
public class HuaweiSmsImpl extends AbstractSmsBlend {
public HuaweiSmsImpl(HuaweiConfig config, Executor pool, DelayedTime delayed) {
super(pool,delayed);
this.config = config;
this.pool = pool;
this.delayed = delayed;
}
private HuaweiConfig config;
private Executor pool;
private DelayedTime delayed;
private final ForestConfiguration http = BeanFactory.getForestConfiguration();
@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);
LinkedHashMap<String, String> mes = new LinkedHashMap<>();
mes.put(UUID.randomUUID().toString().replaceAll("-", ""), message);
return sendMessage(phone, config.getTemplateId(), mes);
}
@Override
@ -55,26 +47,30 @@ public class HuaweiSmsImpl implements SmsBlend {
}
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);
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);
SmsResponse smsResponse = new SmsResponse();
http.post(url)
.addHeader(headers)
.addBody(requestBody)
.onSuccess(((data,req,res)->{
.onSuccess(((data, req, res) -> {
HuaweiResponse jsonBody = res.get(HuaweiResponse.class);
smsResponse.setCode(jsonBody.getCode());
smsResponse.setMessage(jsonBody.getDescription());
smsResponse.setBizId(jsonBody.getResult().get(0).getSmsMsgId());
smsResponse.setData(jsonBody.getResult());
}))
.onError((ex,req,res)->{
.onError((ex, req, res) -> {
HuaweiResponse huaweiResponse = res.get(HuaweiResponse.class);
smsResponse.setErrMessage(huaweiResponse.getDescription());
smsResponse.setErrorCode(huaweiResponse.getCode());
log.debug(huaweiResponse.getDescription());
if (huaweiResponse == null) {
smsResponse.setErrorCode("500");
smsResponse.setErrMessage("huawei send sms response is null.check param");
} else {
smsResponse.setErrMessage(huaweiResponse.getDescription());
smsResponse.setErrorCode(huaweiResponse.getCode());
}
})
.execute();
return smsResponse;
@ -83,7 +79,7 @@ public class HuaweiSmsImpl implements SmsBlend {
@Override
@Restricted
public SmsResponse massTexting(List<String> phones, String message) {
return sendMessage(listToString(phones),message);
return sendMessage(listToString(phones), message);
}
@Override
@ -91,76 +87,4 @@ public class HuaweiSmsImpl implements SmsBlend {
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
return sendMessage(listToString(phones), templateId, messages);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message, CallBack callBack) {
CompletableFuture<SmsResponse> smsResponseCompletableFuture = CompletableFuture.supplyAsync(() -> sendMessage(phone, message), pool);
smsResponseCompletableFuture.thenAcceptAsync(callBack::callBack);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message) {
pool.execute(() -> sendMessage(phone, message));
}
@Override
@Restricted
public 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);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
pool.execute(() -> {
sendMessage(phone, templateId, messages);
});
}
@Override
@Restricted
public void delayedMessage(String phone, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, templateId, messages);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, templateId, messages);
}
}, delayedTime);
}
}

View File

@ -14,7 +14,12 @@ import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class HuaweiBuilder {
private HuaweiBuilder(){}
@ -122,8 +127,9 @@ public class HuaweiBuilder {
stringBuffer.append(s);
stringBuffer.append("\"");
stringBuffer.append(",");
stringBuffer.append("\"");
}
stringBuffer.deleteCharAt(stringBuffer.length()-1);
stringBuffer.delete(stringBuffer.length()-3,stringBuffer.length()-1);
stringBuffer.append("]");
return stringBuffer.toString();
}

View File

@ -5,6 +5,7 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.comm.config.BaseConfig;
/**
@ -17,7 +18,7 @@ import org.dromara.sms4j.comm.config.BaseConfig;
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class JdCloudConfig extends BaseConfig {
public class JdCloudConfig extends BaseConfig implements SupplierConfig {
/**
* 地域信息

View File

@ -7,6 +7,7 @@ import com.jdcloud.sdk.http.Protocol;
import com.jdcloud.sdk.service.sms.client.SmsClient;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.jdcloud.service.JdCloudSmsImpl;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
/**
* 京东云短信配置
@ -14,11 +15,26 @@ import org.dromara.sms4j.jdcloud.service.JdCloudSmsImpl;
* @author Charles7c
* @since 2023/4/10 20:01
*/
public class JdCloudSmsConfig {
public class JdCloudFactory implements BaseProviderFactory<JdCloudSmsImpl, JdCloudConfig> {
private static JdCloudSmsImpl jdCloudSms;
private static JdCloudSmsConfig jdCloudSmsConfig;
private static final JdCloudFactory INSTANCE = new JdCloudFactory();
private static final class ConfigHolder {
private static JdCloudConfig config = JdCloudConfig.builder().build();
}
private JdCloudFactory() {
}
/**
* 获取建造者实例
* @return 建造者实例
*/
public static JdCloudFactory instance() {
return INSTANCE;
}
/**
* 客户端对象
@ -36,13 +52,11 @@ public class JdCloudSmsConfig {
/**
* 创建京东云短信实现
*/
public static JdCloudSmsImpl createJdCloudSms(JdCloudConfig jdCloudConfig) {
if (jdCloudSmsConfig == null) {
jdCloudSmsConfig = new JdCloudSmsConfig();
}
@Override
public JdCloudSmsImpl createSms(JdCloudConfig jdCloudConfig) {
if (jdCloudSms == null) {
jdCloudSms = new JdCloudSmsImpl(
jdCloudSmsConfig.client(jdCloudConfig),
this.client(jdCloudConfig),
jdCloudConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime()
@ -54,16 +68,33 @@ public class JdCloudSmsConfig {
/**
* 刷新对象
*/
public static JdCloudSmsImpl refresh(JdCloudConfig jdCloudConfig) {
if (jdCloudSmsConfig == null) {
jdCloudSmsConfig = new JdCloudSmsConfig();
}
@Override
public JdCloudSmsImpl refresh(JdCloudConfig jdCloudConfig) {
jdCloudSms = new JdCloudSmsImpl(
jdCloudSmsConfig.client(jdCloudConfig),
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;
}
}

View File

@ -5,8 +5,7 @@ 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.SmsBlend;
import org.dromara.sms4j.api.callback.CallBack;
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;
@ -16,8 +15,6 @@ import org.dromara.sms4j.jdcloud.config.JdCloudConfig;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;
@ -28,21 +25,16 @@ import java.util.stream.Collectors;
* @since 2023/4/10 20:01
*/
@Slf4j
public class JdCloudSmsImpl implements SmsBlend {
public class JdCloudSmsImpl extends AbstractSmsBlend {
private final SmsClient client;
private final JdCloudConfig config;
private final Executor pool;
private final DelayedTime delayed;
public JdCloudSmsImpl(SmsClient client, JdCloudConfig config, Executor pool, DelayedTime delayed) {
super(pool,delayed);
this.client = client;
this.config = config;
this.pool = pool;
this.delayed = delayed;
}
@Override
@ -85,76 +77,6 @@ public class JdCloudSmsImpl implements SmsBlend {
}
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message, CallBack callBack) {
CompletableFuture<SmsResponse> smsResponseCompletableFuture = CompletableFuture.supplyAsync(() -> sendMessage(phone, message), pool);
smsResponseCompletableFuture.thenAcceptAsync(callBack::callBack);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message) {
pool.execute(() -> sendMessage(phone, message));
}
@Override
@Restricted
public 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);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
pool.execute(() -> sendMessage(phone, templateId, messages));
}
@Override
@Restricted
public void delayedMessage(String phone, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, templateId, messages);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, templateId, messages);
}
}, delayedTime);
}
/**
* 获取短信返回信息
*

View File

@ -0,0 +1,40 @@
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);
/**
* 刷新短信实现对象
* @param c 短信配置对象
* @return 刷新后的短信实现对象
*/
S refresh(C c);
/**
* 获取配置
* @return 配置对象
*/
C getConfig();
/**
* 设置配置
* @param config 配置对象
*/
void setConfig(C config);
}

View File

@ -0,0 +1,66 @@
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.provider.base.BaseProviderFactory;
import org.dromara.sms4j.tencent.config.TencentFactory;
import org.dromara.sms4j.unisms.config.UniFactory;
import org.dromara.sms4j.yunpian.config.YunPianFactory;
/**
* 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()),
;
/**
* 渠道名称
*/
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;
}
}

View File

@ -5,13 +5,14 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.comm.config.BaseConfig;
@Data
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class TencentConfig extends BaseConfig {
public class TencentConfig extends BaseConfig implements SupplierConfig {
/**
* 短信sdkAppId

View File

@ -0,0 +1,81 @@
package org.dromara.sms4j.tencent.config;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
import org.dromara.sms4j.tencent.service.TencentSmsImpl;
/**
* TencentSmsConfig
* <p> 建造腾讯云短信
*
* @author :Wind
* 2023/4/8 16:05
**/
public class TencentFactory implements BaseProviderFactory<TencentSmsImpl, TencentConfig> {
private static TencentSmsImpl tencentSms;
private static final TencentFactory INSTANCE = new TencentFactory();
private static final class ConfigHolder {
private static TencentConfig config = TencentConfig.builder().build();
}
private TencentFactory() {
}
/**
* 获取建造者实例
* @return 建造者实例
*/
public static TencentFactory instance() {
return INSTANCE;
}
/**
* 建造一个腾讯云的短信实现
*/
@Override
public TencentSmsImpl createSms(TencentConfig tencentConfig) {
if (tencentSms == null) {
tencentSms = new TencentSmsImpl(
tencentConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime()
);
}
return tencentSms;
}
/**
* 刷新对象
*/
@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;
}
}

View File

@ -1,52 +0,0 @@
package org.dromara.sms4j.tencent.config;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.tencent.service.TencentSmsImpl;
/**
* TencentSmsConfig
* <p> 建造腾讯云短信
*
* @author :Wind
* 2023/4/8 16:05
**/
public class TencentSmsConfig {
private TencentSmsConfig() {
}
private static TencentSmsImpl tencentSms;
private static TencentSmsConfig tencentSmsConfig;
/**
* 建造一个腾讯云的短信实现
*/
public static TencentSmsImpl createTencentSms(TencentConfig tencentConfig) {
if (tencentSmsConfig == null) {
tencentSmsConfig = new TencentSmsConfig();
}
if (tencentSms == null) {
tencentSms = new TencentSmsImpl(
tencentConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime()
);
}
return tencentSms;
}
/**
* 刷新对象
*/
public static TencentSmsImpl refresh(TencentConfig tencentConfig) {
if (tencentSmsConfig == null) {
tencentSmsConfig = new TencentSmsConfig();
}
tencentSms = new TencentSmsImpl(
tencentConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime()
);
return tencentSms;
}
}

View File

@ -1,39 +1,36 @@
package org.dromara.sms4j.tencent.service;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dtflys.forest.config.ForestConfiguration;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import com.jdcloud.sdk.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.callback.CallBack;
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.factory.BeanFactory;
import org.dromara.sms4j.comm.utils.SmsUtil;
import org.dromara.sms4j.tencent.config.TencentConfig;
import org.dromara.sms4j.tencent.utils.TencentUtils;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
/**
* @author wind
*/
@Slf4j
public class TencentSmsImpl implements SmsBlend {
public class TencentSmsImpl extends AbstractSmsBlend {
private TencentConfig tencentSmsConfig;
private Executor pool;
private DelayedTime delayed;
private final ForestConfiguration http = BeanFactory.getForestConfiguration();
public TencentSmsImpl(TencentConfig tencentSmsConfig, Executor pool, DelayedTime delayed) {
super(pool, delayed);
this.tencentSmsConfig = tencentSmsConfig;
this.pool = pool;
this.delayed = delayed;
}
@Override
@ -77,7 +74,7 @@ public class TencentSmsImpl implements SmsBlend {
list.add(entry.getValue());
}
String[] s = new String[list.size()];
return getSmsResponse(arrayToString(phones), list.toArray(s), templateId);
return getSmsResponse(SmsUtil.listToArray(phones), list.toArray(s), templateId);
}
private SmsResponse getSmsResponse(String[] phones, String[] messages, String templateId) {
@ -101,102 +98,31 @@ public class TencentSmsImpl implements SmsBlend {
.onSuccess(((data, req, res) -> {
JSONObject jsonBody = res.get(JSONObject.class);
JSONObject response = jsonBody.getJSONObject("Response");
JSONArray sendStatusSet = response.getJSONArray("SendStatusSet");
smsResponse.setBizId(sendStatusSet.getJSONObject(0).getString("SerialNo"));
smsResponse.setMessage(sendStatusSet.getJSONObject(0).getString("Message"));
smsResponse.setCode(sendStatusSet.getJSONObject(0).getString("Code"));
String error = response.getStr("Error");
if (StringUtils.isNotBlank(error)){
smsResponse.setErrorCode("500");
smsResponse.setErrMessage(error);
}else {
JSONArray sendStatusSet = response.getJSONArray("SendStatusSet");
smsResponse.setBizId(sendStatusSet.getJSONObject(0).getStr("SerialNo"));
smsResponse.setMessage(sendStatusSet.getJSONObject(0).getStr("Message"));
smsResponse.setCode(sendStatusSet.getJSONObject(0).getStr("Code"));
}
}))
.onError((ex, req, res) -> {
JSONObject jsonBody = res.get(JSONObject.class);
JSONObject response = jsonBody.getJSONObject("Response");
JSONArray sendStatusSet = response.getJSONArray("SendStatusSet");
smsResponse.setErrMessage(sendStatusSet.getJSONObject(0).getString("Message"));
smsResponse.setErrorCode(sendStatusSet.getJSONObject(0).getString("Code"));
if (jsonBody == null) {
smsResponse.setErrorCode("500");
smsResponse.setErrMessage("tencent send sms response is null.check param");
} else {
JSONObject response = jsonBody.getJSONObject("Response");
JSONArray sendStatusSet = response.getJSONArray("SendStatusSet");
smsResponse.setErrMessage(sendStatusSet.getJSONObject(0).getStr("Message"));
smsResponse.setErrorCode(sendStatusSet.getJSONObject(0).getStr("Code"));
}
})
.execute();
return smsResponse;
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message, CallBack callBack) {
CompletableFuture<SmsResponse> smsResponseCompletableFuture = CompletableFuture.supplyAsync(() -> sendMessage(phone, message), pool);
smsResponseCompletableFuture.thenAcceptAsync(callBack::callBack);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message) {
pool.execute(() -> {
sendMessage(phone, message);
});
}
@Override
@Restricted
public 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);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
pool.execute(() -> {
sendMessage(phone, templateId, messages);
});
}
@Override
@Restricted
public void delayedMessage(String phone, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, templateId, messages);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, templateId, messages);
}
}, delayedTime);
}
private String[] arrayToString(List<String> list) {
String[] strs = new String[list.size()];
List<String> toStr = new ArrayList<>();
for (String s : list) {
toStr.add("+86" + s);
}
return toStr.toArray(strs);
}
}

View File

@ -58,16 +58,14 @@ public class TencentUtils {
*/
public static String generateSignature(TencentConfig tencentConfig, String templateId, String[] messages, String[] phones,
String timestamp) throws Exception {
// ************* 步骤 1拼接规范请求串 *************
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
String date = sdf.format(new Date(Long.parseLong(timestamp + "000")));
String canonicalUri = "/";
String canonicalQueryString = "";
String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + tencentConfig.getRequestUrl() + "\n";
String canonicalHeaders = "content-type:application/json; charset=utf-8\nhost:" + tencentConfig.getRequestUrl() + "\n";
String signedHeaders = "content-type;host";
HashMap<String, Object> params = new HashMap<>();
// 实际调用需要更新参数这里仅作为演示签名验证通过的例子
Map<String, Object> params = new HashMap<>();
params.put("PhoneNumberSet", phones);
params.put("SmsSdkAppId", tencentConfig.getSdkAppId());
params.put("SignName", tencentConfig.getSignature());
@ -75,20 +73,15 @@ public class TencentUtils {
params.put("TemplateParamSet", messages);
String payload = JSON.toJSONString(params);
String hashedRequestPayload = sha256Hex(payload);
String canonicalRequest = HTTP_REQUEST_METHOD + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
+ canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
// ************* 步骤 2拼接待签名字符串 *************
String credentialScope = date + "/" + tencentConfig.getService() + "/" + "tc3_request";
String canonicalRequest = HTTP_REQUEST_METHOD + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n" + canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
String credentialScope = date + "/" + tencentConfig.getService() + "/tc3_request";
String hashedCanonicalRequest = sha256Hex(canonicalRequest);
String stringToSign = ALGORITHM + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
// ************* 步骤 3计算签名 *************
byte[] secretDate = hmac256(("TC3" + tencentConfig.getAccessKeySecret()).getBytes(StandardCharsets.UTF_8), date);
byte[] secretService = hmac256(secretDate, tencentConfig.getService());
byte[] secretSigning = hmac256(secretService, "tc3_request");
String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();
// ************* 步骤 4拼接 Authorization *************
return ALGORITHM + " " + "Credential=" + tencentConfig.getAccessKeyId() + "/" + credentialScope + ", "
+ "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
return ALGORITHM + " Credential=" + tencentConfig.getAccessKeyId() + "/" + credentialScope + ", SignedHeaders=" + signedHeaders + ", Signature=" + signature;
}
/**

View File

@ -5,13 +5,14 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.comm.config.BaseConfig;
@Data
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class UniConfig extends BaseConfig {
public class UniConfig extends BaseConfig implements SupplierConfig {
/**
* 是否为简易模式

View File

@ -1,6 +1,7 @@
package org.dromara.sms4j.unisms.config;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
import org.dromara.sms4j.unisms.core.Uni;
import org.dromara.sms4j.unisms.service.UniSmsImpl;
@ -10,14 +11,27 @@ import org.dromara.sms4j.unisms.service.UniSmsImpl;
* @author :Wind
* 2023/4/8 15:46
**/
public class UniSmsConfig {
private UniSmsConfig(){}
private static UniSmsConfig uniSmsConfig;
public class UniFactory implements BaseProviderFactory<UniSmsImpl, UniConfig> {
private static UniSmsImpl uniSmsImpl;
private static final UniFactory INSTANCE = new UniFactory();
private static final class ConfigHolder {
private static UniConfig config = UniConfig.builder().build();
}
private UniFactory() {
}
/**
* 获取建造者实例
* @return 建造者实例
*/
public static UniFactory instance() {
return INSTANCE;
}
/** 短信配置*/
private void buildSms(UniConfig uniConfig){
@ -33,12 +47,10 @@ public class UniSmsConfig {
* <p>建造一个短信实现对像
* @author :Wind
*/
public static UniSmsImpl createUniSms(UniConfig uniConfig){
if (uniSmsConfig == null){
uniSmsConfig = new UniSmsConfig();
}
@Override
public UniSmsImpl createSms(UniConfig uniConfig){
if (uniSmsImpl == null){
uniSmsConfig.buildSms(uniConfig);
this.buildSms(uniConfig);
uniSmsImpl = new UniSmsImpl(
uniConfig,
BeanFactory.getExecutor(),
@ -53,11 +65,9 @@ public class UniSmsConfig {
* <p>刷新对象
* @author :Wind
*/
public static UniSmsImpl refresh(UniConfig uniConfig){
if (uniSmsConfig == null){
uniSmsConfig = new UniSmsConfig();
}
uniSmsConfig.buildSms(uniConfig);
@Override
public UniSmsImpl refresh(UniConfig uniConfig){
this.buildSms(uniConfig);
uniSmsImpl = new UniSmsImpl(
uniConfig,
BeanFactory.getExecutor(),
@ -65,4 +75,23 @@ public class UniSmsConfig {
);
return uniSmsImpl;
}
/**
* 获取配置
* @return 配置对象
*/
@Override
public UniConfig getConfig() {
return ConfigHolder.config;
}
/**
* 设置配置
* @param config 配置对象
*/
@Override
public void setConfig(UniConfig config) {
ConfigHolder.config = config;
}
}

View File

@ -1,6 +1,6 @@
package org.dromara.sms4j.unisms.core;
import com.alibaba.fastjson.JSONObject;
import cn.hutool.json.JSONUtil;
import com.dtflys.forest.config.ForestConfiguration;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.factory.BeanFactory;
@ -79,6 +79,7 @@ public class UniClient {
/**
* request
* <p> uni-sms发送请求
*
* @param action 接口名称
* @author :Wind
*/
@ -91,21 +92,19 @@ public class UniClient {
headers.put("User-Agent", USER_AGENT);
headers.put("Content-Type", "application/json;charset=utf-8");
headers.put("Accept", "application/json");
return new UniResponse(
JSONObject.parseObject(
http.post(this.endpoint)
.addHeader(headers)
.addQuery(this.sign(query))
.addBody(JSONObject.toJSONString(data))
.successWhen(((req, res) -> {
return res.noException() && // 请求过程没有异常
res.statusIsNot(500); // 不能是 500
}))
.onError((ex, req, res) -> {
throw new SmsBlendException(ex.getMessage());
})
.executeAsString()
));
String str = http.post(this.endpoint)
.addHeader(headers)
.addQuery(this.sign(query))
.addBody(JSONUtil.toJsonStr(data))
.successWhen(((req, res) -> {
return res.noException() && // 请求过程没有异常
res.statusIsNot(500); // 不能是 500
}))
.onError((ex, req, res) -> {
throw new SmsBlendException(ex.getMessage());
})
.executeAsString();
return new UniResponse(JSONUtil.parseObj(str));
}
public static class Builder {

View File

@ -1,8 +1,10 @@
package org.dromara.sms4j.unisms.core;
import com.alibaba.fastjson.JSONObject;
import cn.hutool.json.JSONObject;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import java.util.Objects;
public class UniResponse {
public static final String REQUEST_ID_HEADER_KEY = "x-uni-request-id";
@ -20,22 +22,23 @@ public class UniResponse {
*/
public UniResponse(final JSONObject response) throws SmsBlendException {
JSONObject body = response.getJSONObject("data");
this.status = body.getJSONArray("messages").getJSONObject(0).getString("status");
this.requestId = body.getJSONArray("messages").getJSONObject(0).getString("id");
this.raw = body;
if (this.status != "400") {
String code = response.getString("code");
String message = body.getJSONArray("messages").getString(0);
if (!"0".equals(code)) {
throw new SmsBlendException(message, code, this.requestId);
}
this.code = code;
this.message = message;
if (!Objects.isNull(body)) {
this.status = body.getJSONArray("messages").getJSONObject(0).getStr("status");
this.requestId = body.getJSONArray("messages").getJSONObject(0).getStr("id");
this.raw = body;
this.data = body;
}
else {
throw new SmsBlendException(response.getString("message"), "-1");
if (this.status != "400") {
String code = response.getStr("code");
if (!"0".equals(code)) {
this.message = response.getStr("message");
} else {
this.message = body.getJSONArray("messages").getStr(0);
;
}
this.code = code;
} else {
throw new SmsBlendException(response.getStr("message"), "-1");
}
}

View File

@ -1,13 +1,12 @@
package org.dromara.sms4j.unisms.service;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.callback.CallBack;
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.unisms.config.UniConfig;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.unisms.core.Uni;
import org.dromara.sms4j.unisms.core.UniResponse;
@ -16,11 +15,7 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
/**
@ -31,16 +26,13 @@ import java.util.function.Supplier;
**/
@Slf4j
public class UniSmsImpl implements SmsBlend {
public class UniSmsImpl extends AbstractSmsBlend {
private UniConfig config;
private Executor pool;
private DelayedTime delayed;
public UniSmsImpl(UniConfig config, Executor pool, DelayedTime delayed) {
super(pool,delayed);
this.config = config;
this.pool = pool;
this.delayed = delayed;
}
@Override
@ -90,76 +82,6 @@ public class UniSmsImpl implements SmsBlend {
return getSmsResponse(data);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message, CallBack callBack) {
CompletableFuture<SmsResponse> smsResponseCompletableFuture = CompletableFuture.supplyAsync(() -> sendMessage(phone, message), pool);
smsResponseCompletableFuture.thenAcceptAsync(callBack::callBack);
}
@Override
public void sendMessageAsync(String phone, String message) {
pool.execute(()->{
sendMessage(phone, message);
});
}
@Override
@Restricted
public 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);
}
@Override
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
pool.execute(()->{
sendMessage(phone,templateId,messages);
});
}
@Override
@Restricted
public void delayedMessage(String phone, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone,message);
}
},delayedTime);
}
@Override
@Restricted
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone,templateId,messages);
}
},delayedTime);
}
@Override
public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones,message);
}
},delayedTime);
}
@Override
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones,templateId,messages);
}
},delayedTime);
}
private SmsResponse getSmsResponse( Map<String, Object> data) {
SmsResponse smsResponse = new SmsResponse();
try {

View File

@ -0,0 +1,72 @@
package org.dromara.sms4j.yunpian.config;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
import org.dromara.sms4j.yunpian.service.YunPianSmsImpl;
public class YunPianFactory implements BaseProviderFactory<YunPianSmsImpl, YunpianConfig> {
private static YunPianSmsImpl yunpianSmsImpl;
private static final YunPianFactory INSTANCE = new YunPianFactory();
private static final class ConfigHolder {
private static YunpianConfig config = YunpianConfig.builder().build();
}
private YunPianFactory() {
}
/**
* 获取建造者实例
* @return 建造者实例
*/
public static YunPianFactory instance() {
return INSTANCE;
}
/**
* 建造一个云片短信实现
*/
@Override
public YunPianSmsImpl createSms(YunpianConfig yunpianConfig){
if (yunpianSmsImpl == null){
yunpianSmsImpl = new YunPianSmsImpl(
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime(),
yunpianConfig
);
}
return yunpianSmsImpl;
}
/** 刷新对象*/
@Override
public YunPianSmsImpl refresh(YunpianConfig yunpianConfig){
yunpianSmsImpl = new YunPianSmsImpl(
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime(),
yunpianConfig
);
return yunpianSmsImpl;
}
/**
* 获取配置
* @return 配置对象
*/
@Override
public YunpianConfig getConfig() {
return ConfigHolder.config;
}
/**
* 设置配置
* @param config 配置对象
*/
@Override
public void setConfig(YunpianConfig config) {
ConfigHolder.config = config;
}
}

View File

@ -1,24 +0,0 @@
package org.dromara.sms4j.yunpian.config;
import com.dtflys.forest.Forest;
import com.dtflys.forest.config.ForestConfiguration;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.yunpian.service.YunPianSmsImpl;
public class YunPianSmsConfig {
public YunpianConfig yunpianConfig(){
return YunpianConfig.builder().build();
}
public ForestConfiguration forestConfiguration(YunpianConfig yunpianConfig) {
return Forest.config().setBackendName("httpclient");
}
public SmsBlend smsBlend() {
return new YunPianSmsImpl();
}
}

View File

@ -1,26 +1,22 @@
package org.dromara.sms4j.yunpian.config;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.comm.config.BaseConfig;
@Data
@Builder
public class YunpianConfig {
/**
* 账号唯一标识
*/
private String apikey;
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class YunpianConfig extends BaseConfig implements SupplierConfig {
/**
* 短信发送后将向这个地址推送(运营商返回的)发送报告
*/
private String callbackUrl;
/**
* 模板Id
*/
private String templateId;
/**
* 模板变量名称
*/

View File

@ -1,40 +1,46 @@
package org.dromara.sms4j.yunpian.service;
import com.alibaba.fastjson.JSONObject;
import com.dtflys.forest.config.ForestConfiguration;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.callback.CallBack;
import cn.hutool.json.JSONObject;
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.yunpian.config.YunpianConfig;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import static org.dromara.sms4j.comm.utils.SmsUtil.listToString;
/**
* @author wind
*/
public class YunPianSmsImpl extends AbstractSmsBlend {
public class YunPianSmsImpl implements SmsBlend {
private Executor pool;
private DelayedTime delayed;
public YunPianSmsImpl(Executor pool, DelayedTime delayed, YunpianConfig config) {
super(pool, delayed);
this.config = config;
}
private YunpianConfig config;
private ForestConfiguration http;
private static SmsResponse getSmsResponse(JSONObject execute) {
SmsResponse smsResponse = new SmsResponse();
smsResponse.setCode(execute.getString("code"));
smsResponse.setMessage(execute.getString("msg"));
smsResponse.setBizId(execute.getString("sid"));
if (execute.getInteger("code") != 0) {
smsResponse.setErrMessage(execute.getString("msg"));
if (execute == null) {
smsResponse.setErrorCode("500");
smsResponse.setErrMessage("yunpian send sms response is null.check param");
return smsResponse;
}
smsResponse.setCode(execute.getStr("code"));
smsResponse.setMessage(execute.getStr("msg"));
smsResponse.setBizId(execute.getStr("sid"));
if (execute.getInt("code") != 0) {
smsResponse.setErrMessage(execute.getStr("msg"));
}
smsResponse.setData(execute);
return smsResponse;
@ -50,7 +56,7 @@ public class YunPianSmsImpl implements SmsBlend {
@Override
@Restricted
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
Map<String, String> body = setBody(phone, "", messages,templateId);
Map<String, String> body = setBody(phone, "", messages, templateId);
return getSendResponse(body);
}
@ -60,7 +66,7 @@ public class YunPianSmsImpl implements SmsBlend {
if (phones.size() > 1000) {
throw new SmsBlendException("单次发送超过最大发送上限建议每次群发短信人数低于1000");
}
return sendMessage(listToString(phones), message);
return sendMessage(SmsUtil.listToString(phones), message);
}
@Override
@ -69,79 +75,7 @@ public class YunPianSmsImpl implements SmsBlend {
if (phones.size() > 1000) {
throw new SmsBlendException("单次发送超过最大发送上限建议每次群发短信人数低于1000");
}
return sendMessage(listToString(phones), templateId, messages);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message, CallBack callBack) {
CompletableFuture<SmsResponse> smsResponseCompletableFuture = CompletableFuture.supplyAsync(() -> sendMessage(phone, message), pool);
smsResponseCompletableFuture.thenAcceptAsync(callBack::callBack);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message) {
pool.execute(() -> sendMessage(phone, message));
}
@Override
@Restricted
public 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);
}
@Override
@Restricted
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
pool.execute(() -> {
sendMessage(phone, templateId, messages);
});
}
@Override
@Restricted
public void delayedMessage(String phone, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
sendMessage(phone, templateId, messages);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, message);
}
}, delayedTime);
}
@Override
@Restricted
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
this.delayed.schedule(new TimerTask() {
@Override
public void run() {
massTexting(phones, templateId, messages);
}
}, delayedTime);
return sendMessage(SmsUtil.listToString(phones), templateId, messages);
}
private String formattingMap(Map<String, String> messages) {
@ -165,11 +99,12 @@ public class YunPianSmsImpl implements SmsBlend {
message.put(config.getTemplateName(), mes);
}
Map<String, String> body = new HashMap<>();
body.put("apikey", config.getApikey());
body.put("apikey", config.getAccessKeyId());
body.put("mobile", phone);
body.put("tpl_id", tplId);
body.put("tpl_value", formattingMap(message));
if (!config.getCallbackUrl().isEmpty()) body.put("callback_url", config.getCallbackUrl());
if (config.getCallbackUrl() != null && !config.getCallbackUrl().isEmpty())
body.put("callback_url", config.getCallbackUrl());
return body;
}
@ -182,7 +117,7 @@ public class YunPianSmsImpl implements SmsBlend {
private SmsResponse getSendResponse(Map<String, String> body) {
Map<String, String> headers = getHeaders();
AtomicReference<SmsResponse> smsResponse = null;
AtomicReference<SmsResponse> smsResponse = new AtomicReference<>();
http.post(Constant.YUNPIAN_URL + "/sms/tpl_single_send.json")
.addHeader(headers)
.addBody(body)

View File

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>sms4j-solon-plugin</artifactId>
<name>sms4j-solon-plugin</name>
<description>sms4j-solon-plugin</description>
<dependencies>
<!--aop依赖-->
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon</artifactId>
<version>${solon.version}</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-core</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>${redisson.version}</version>
</dependency>
<dependency>
<groupId>org.noear</groupId>
<artifactId>solon-test</artifactId>
<version>${solon.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,16 @@
package org.dromara.sms4j.solon;
import org.dromara.sms4j.solon.config.SmsAutowiredConfig;
import org.noear.solon.core.AopContext;
import org.noear.solon.core.Plugin;
/**
* @author noear 2023/5/16 created
*/
public class XPluginImpl implements Plugin {
@Override
public void start(AopContext context) throws Throwable {
context.beanMake(SmsAutowiredConfig.class);
SmsAutowiredConfig.aopContext = context;
}
}

View File

@ -0,0 +1,55 @@
package org.dromara.sms4j.solon.aop;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.smsProxy.RestrictedProcess;
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.solon.utils.RedisUtils;
import org.noear.solon.core.AopContext;
@Slf4j
public class SolonRestrictedProcess extends RestrictedProcess {
private RedisUtils 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:";
public SolonRestrictedProcess(AopContext context){
context.getBeanAsync(RedisUtils.class, bean->{
redis = bean;
});
}
@Override
public SmsBlendException process(SmsConfig config, String args) throws Exception {
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;
}
}

View File

@ -0,0 +1,93 @@
package org.dromara.sms4j.solon.config;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.smsProxy.SmsInvocationHandler;
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.solon.aop.SolonRestrictedProcess;
import org.dromara.sms4j.solon.utils.RedisUtils;
import org.noear.solon.Solon;
import org.noear.solon.Utils;
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 java.util.concurrent.Executor;
@Slf4j
@Configuration
public class SmsAutowiredConfig {
public static AopContext aopContext;
private <T> T injectObj(String prefix, T obj) {
//@Inject 只支持在字段参数类型上注入
Props props = Solon.cfg().getProp(prefix);
Utils.injectProperties(obj, props);
return obj;
}
@Bean
public SmsSqlConfig smsSqlConfig() {
return injectObj("sms.sql", BeanFactory.getSmsSqlConfig());
}
@Bean
public SmsConfig smsConfig(){
SmsConfig smsConfig = injectObj("sms", BeanFactory.getSmsConfig());
smsConfigCheck();
return smsConfig;
}
/** 注入一个定时器*/
@Bean
public DelayedTime delayedTime(){
return BeanFactory.getDelayedTime();
}
/** 注入线程池*/
@Bean("smsExecutor")
public Executor taskExecutor(@Inject SmsConfig config){
return BeanFactory.setExecutor(config);
}
/** smsConfig参数意义为确保注入时smsConfig已经存在*/
@Bean
@Condition(onProperty ="${sms.config-type}=config_file")
public SupplierConfig supplierConfig(@Inject SmsConfig smsConfig){
return new SupplierConfig();
}
@Bean
@Condition(onProperty ="${sms.config-type}=sql_config")
public SupplierSqlConfig supplierSqlConfig(@Inject SmsSqlConfig smsSqlConfig){
return new SupplierSqlConfig();
}
private void smsConfigCheck(){
/* 如果配置中启用了redis则注入redis工具*/
if (BeanFactory.getSmsConfig().getRedisCache()){
Solon.context().wrapAndPut(RedisUtils.class);
SmsInvocationHandler.setRestrictedProcess(new SolonRestrictedProcess(aopContext));
log.debug("The redis cache is enabled for sms4j");
}
//打印banner
if (BeanFactory.getSmsConfig().getIsPrint()){
SmsBanner.PrintBanner(Constant.VERSION);
}
}
}

View File

@ -0,0 +1,84 @@
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;
import org.dromara.sms4j.jdcloud.config.JdCloudConfig;
import org.dromara.sms4j.tencent.config.TencentConfig;
import org.dromara.sms4j.unisms.config.UniConfig;
import org.dromara.sms4j.yunpian.config.YunpianConfig;
import org.noear.solon.Solon;
import org.noear.solon.Utils;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.core.Props;
@Configuration
public class SupplierConfig {
private <T> T injectObj(String prefix, T obj) {
//@Inject 只支持在字段参数类型上注入
Props props = Solon.cfg().getProp(prefix);
Utils.injectProperties(obj, props);
return obj;
}
/** 阿里差异化配置*/
@Bean
public AlibabaConfig alibabaConfig(){
return injectObj("sms.alibaba", SupplierFactory.getAlibabaConfig());
}
/** 华为差异化配置*/
@Bean
public HuaweiConfig huaweiConfig(){
return injectObj("sms.huawei", SupplierFactory.getHuaweiConfig());
}
/** 云片短信差异化配置*/
@Bean
public YunpianConfig yunpianConfig(){
return injectObj("sms.yunpian", SupplierFactory.getYunpianConfig());
}
/** 合一短信差异化配置*/
@Bean
public UniConfig uniConfig(){
return injectObj("sms.uni", SupplierFactory.getUniConfig());
}
/** 腾讯短信差异化配置*/
@Bean
public TencentConfig tencentConfig(){
return injectObj("sms.tencent", SupplierFactory.getTencentConfig());
}
/** 京东云短信差异化配置 */
@Bean
public JdCloudConfig jdCloudConfig(){
return injectObj("sms.jdcloud", SupplierFactory.getJdCloudConfig());
}
/** 容联云短信差异化配置 */
@Bean
public CloopenConfig cloopenConfig(){
return injectObj("sms.cloopen", SupplierFactory.getCloopenConfig());
}
/**
* 亿美软通短信差异化配置
*/
@Bean
public EmayConfig emayConfig(){
return injectObj("sms.emay", SupplierFactory.getEmayConfig());
}
/** 天翼云短信差异化配置 */
@Bean
public CtyunConfig ctyunConfig(){
return injectObj("sms.ctyun", SupplierFactory.getCtyunConfig());
}
}

View File

@ -0,0 +1,468 @@
package org.dromara.sms4j.solon.utils;
import lombok.extern.slf4j.Slf4j;
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 RedisUtils {
private RedissonClient redisTemplate;
public RedisUtils() {
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 RedisUtils(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();
// }
}

View File

@ -0,0 +1,4 @@
#???????
solon.plugin=org.dromara.sms4j.solon.XPluginImpl
#?????????????????0
solon.plugin.priority=1

View File

@ -0,0 +1,14 @@
package org.dromara.sms4j.demo;
import org.noear.solon.Solon;
import org.noear.solon.annotation.SolonMain;
/**
* @author noear 2023/5/16 created
*/
@SolonMain
public class App {
public static void main(String[] args){
Solon.start(App.class, args);
}
}

View File

@ -0,0 +1,87 @@
package org.dromara.sms4j.demo;
import org.noear.solon.Utils;
import org.noear.solon.annotation.Bean;
import org.noear.solon.annotation.Configuration;
import org.noear.solon.annotation.Inject;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.ClusterServersConfig;
import org.redisson.config.SingleServerConfig;
import java.util.Properties;
/**
* @author noear 2023/5/16 created
*/
@Configuration
public class Config {
@Bean
public RedissonClient redisInit(@Inject("${sms4j.redis}") Properties props) {
return build(props);
}
public RedissonClient build(Properties prop) {
String server_str = prop.getProperty("server");
String db_str = prop.getProperty("db");
String user_str = prop.getProperty("user");
String password_str = prop.getProperty("password");
int db = 0;
if (Utils.isNotEmpty(db_str)) {
db = Integer.parseInt(db_str);
}
//
// 开始实例化 redissonClient
//
org.redisson.config.Config config = new org.redisson.config.Config();
if (server_str.contains(",")) {
//集群
ClusterServersConfig serverConfig = config.useClusterServers();
//注入一般配置
Utils.injectProperties(serverConfig, prop);
//设置关键配置
String[] address = resolveServers(server_str.split(","));
serverConfig.addNodeAddress(address)
.setUsername(user_str)
.setPassword(password_str);
} else {
//单例
SingleServerConfig serverConfig = config.useSingleServer();
//注入一般配置
Utils.injectProperties(serverConfig, prop);
//设置关键配置
String[] address = resolveServers(server_str);
serverConfig.setAddress(address[0])
.setUsername(user_str)
.setPassword(password_str)
.setDatabase(db);
}
return Redisson.create(config);
}
private String[] resolveServers(String... servers) {
String[] uris = new String[servers.length];
for (int i = 0; i < servers.length; i++) {
String sev = servers[i];
if (sev.contains("://")) {
uris[i] = sev;
} else {
uris[i] = "redis://" + sev;
}
}
return uris;
}
}

View File

@ -1,33 +1,30 @@
package org.dromara.sms4j.test;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.comm.enumerate.SupplierType;
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.springframework.boot.test.context.SpringBootTest;
import org.junit.runner.RunWith;
import org.noear.solon.test.SolonJUnit4ClassRunner;
import org.noear.solon.test.SolonTest;
@SpringBootTest
@RunWith(SolonJUnit4ClassRunner.class)
@SolonTest
public class Sms4jTest {
public static final String USER_AGENT = "uni-java-sdk" + "/0.0.4" ;
@Test
public void uniSmsTest() {
UniConfig build = UniConfig.builder()
.signature("洙旭阁")
.accessKeyId("7Cr1ZaQVJQ11Ap4HBQMo7xmFg")
.signature("***")
.accessKeyId("7Cr1***VJQ11Ap4***Mo7xmFg")
.templateId("2001")
.templateName("message")
.isSimple(true)
.build();
SupplierFactory.setUniConfig(build);
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.UNI_SMS).sendMessage("17531165952", "123123");
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.UNI_SMS).sendMessage("175***65952", "123123");
System.out.println(smsResponse);
// UniResponse sms = UniSMS.buildMessage().setTo("17531165952").setSignature("洙旭阁").setTemplateId("2001").setTemplateData(map).send();
// System.out.println(sms);
// Uni.getClient().request("sms.message.send",mes)
// UniClient.request()
}
}

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>sms4j-spring-boot-example</artifactId>
<properties>
<maven.install.skip>true</maven.install.skip>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-spring-boot-starter</artifactId>
<version>${revision}</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,18 @@
package org.dromara.sms4j.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 主类
*
* @author handy
*/
@SpringBootApplication
public class Sms4jApplication {
public static void main(String[] args) {
SpringApplication.run(Sms4jApplication.class, args);
}
}

View File

@ -0,0 +1,29 @@
sms:
alibaba:
#阿里云的accessKey
accessKeyId: 您的accessKey
#阿里云的accessKeySecret
accessKeySecret: 您的accessKeySecret
#短信签名
signature: 测试签名
#模板ID 用于发送固定模板短信使用
templateId: SMS_215125134
#模板变量 上述模板的变量
templateName: code
#请求地址 默认为dysmsapi.aliyuncs.com 如无特殊改变可以不用设置
requestUrl: dysmsapi.aliyuncs.com
huawei:
#华为短信appKey
appKey: 5N6fvXXXX920HaWhVXXXXXX7fYa
#华为短信appSecret
app-secret: Wujt7EYzZTBXXXXXXEhSP6XXXX
#短信签名
signature: 华为短信测试
#通道号
sender: 8823040504797
#模板ID 如果使用自定义模板发送方法可不设定
template-id: acXXXXXXXXc274b2a8263479b954c1ab5
#华为回调地址,如不需要可不设置或为空
statusCallBack:
#华为分配的app请求地址
url: https://XXXXX.cn-north-4.XXXXXXXX.com:443

View File

@ -0,0 +1,43 @@
package org.dromara.sms4j.example;
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.api.entity.SmsResponse;
import org.dromara.sms4j.comm.utils.SmsUtil;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.provider.enumerate.SupplierType;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@Slf4j
@SpringBootTest
class Sms4jTest {
/**
* 填测试手机号
*/
private static final String PHONE = "";
@Test
public void alibabaSmsTest() {
if (StrUtil.isBlank(PHONE)) {
return;
}
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.ALIBABA).sendMessage(PHONE, SmsUtil.getRandomInt(6));
log.info(JSONUtil.toJsonStr(smsResponse));
Assert.isTrue("OK".equals(smsResponse.getCode()));
}
@Test
public void huaweiSmsTest() {
if (StrUtil.isBlank(PHONE)) {
return;
}
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.HUAWEI).sendMessage(PHONE, SmsUtil.getRandomInt(6));
log.info(JSONUtil.toJsonStr(smsResponse));
Assert.isTrue("000000".equals(smsResponse.getCode()));
}
}

View File

@ -16,14 +16,6 @@
<description>sms4j-spring-boot-starter</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-autoimmit</artifactId>