提交经测试版本

This commit is contained in:
Sh1yu 2023-10-27 15:43:35 +08:00
parent 76cb93c4cc
commit 436a41508f
24 changed files with 564 additions and 268 deletions

View File

@ -0,0 +1,42 @@
package org.dromara.sms4j.api.proxy;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
/**
* 核心方法执行器接口
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
public interface CoreMethodProcessor extends SmsProcessor {
default Object[] preProcessor(Method method, Object source, Object[] param) {
String name = method.getName();
int parameterCount = method.getParameterCount();
if ("sendMessage".equals(method.getName())) {
if (2 == parameterCount) {
sendMessagePreProcess((String) param[0],(String) param[1]);
return param;
}
if (3 == parameterCount) {
sendMessageByTemplatePreProcess((String)param[0],(String) param[1],(LinkedHashMap<String, String>)param[2]);
return param;
}
}
if ("massTexting".equals(method.getName())) {
if (2 == parameterCount) {
massTextingPreProcess((List<String>)param[0],(String)param[1]);
return param;
}
if (3 == parameterCount) {
massTextingByTemplatePreProcess((List<String>)param[0],(String)param[1],(LinkedHashMap<String, String>)param[2]);
return param;
}
}
return param;
}
void sendMessagePreProcess(String phone, String message);
void sendMessageByTemplatePreProcess(String phone, String templateId, LinkedHashMap<String, String> messages);
void massTextingPreProcess(List<String> phones, String message);
void massTextingByTemplatePreProcess(List<String> phones, String templateId, LinkedHashMap<String, String> messages);
}

View File

@ -0,0 +1,12 @@
package org.dromara.sms4j.api.proxy;
/**
* 排序接口
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
public interface Order {
default public int getOrder(){
return 999;
}
}

View File

@ -1,17 +0,0 @@
package org.dromara.sms4j.api.proxy;
import org.dromara.sms4j.comm.exception.SmsBlendException;
/**
* 短信拦截处理接口
*/
public interface RestrictedProcess {
/**
* 拦截校验过程
* @param phone
* @return
*/
SmsBlendException process(String phone);
}

View File

@ -0,0 +1,24 @@
package org.dromara.sms4j.api.proxy;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 执行器接口
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
public interface SmsProcessor extends Order {
default Object[] preProcessor(Method method, Object source, Object[] param) {
return null;
}
default Object postProcessor(Object result, Object[] param) {
return null;
}
default Object exceptionHandleProcessor(Method method, Object source, Object[] param) throws InvocationTargetException, IllegalAccessException {
return null;
}
}

View File

@ -0,0 +1,12 @@
package org.dromara.sms4j.api.proxy;
import java.util.List;
/**
* 支持接口如果执行器需要根据支持厂商加载那可以实现此接口
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
public interface SuppotFilter{
List<String> getSupports();
}

View File

@ -0,0 +1,13 @@
package org.dromara.sms4j.api.proxy.aware;
import java.util.Map;
/**
* 厂商配置感知接口
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
public interface SmsBlendConfigAware {
void setSmsBlendsConfig(Map<String, Map<String, Object>> blends);
}

View File

@ -0,0 +1,12 @@
package org.dromara.sms4j.api.proxy.aware;
/**
* 系统配置感知接口
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
public interface SmsConfigAware {
void setSmsConfig(Object config);
}

View File

@ -0,0 +1,12 @@
package org.dromara.sms4j.api.proxy.aware;
import org.dromara.sms4j.api.dao.SmsDao;
/**
* 缓存感知接口
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
public interface SmsDaoAware {
void setSmsDao(SmsDao smsDao);
}

View File

@ -6,12 +6,11 @@ import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.core.datainterface.SmsReadConfig;
import org.dromara.sms4j.core.load.SmsLoad;
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
import org.dromara.sms4j.core.proxy.SmsProxyFactory;
import org.dromara.sms4j.provider.config.BaseConfig;
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -64,7 +63,6 @@ public abstract class SmsFactory {
*/
public static void createSmsBlend(SmsReadConfig smsReadConfig, String configId) {
BaseConfig supplierConfig = smsReadConfig.getSupplierConfig(configId);
supplierConfig.setConfigId(configId);
SmsBlend smsBlend = create(supplierConfig);
register(smsBlend);
}
@ -81,7 +79,7 @@ public abstract class SmsFactory {
public static void createSmsBlend(SmsReadConfig smsReadConfig) {
List<BaseConfig> supplierConfigList = smsReadConfig.getSupplierConfigList();
supplierConfigList.forEach(supplierConfig -> {
SmsBlend smsBlend = create((SupplierConfig)supplierConfig);
SmsBlend smsBlend = create(supplierConfig);
register(smsBlend);
});
}
@ -93,9 +91,9 @@ public abstract class SmsFactory {
* @param config 短信配置
* @author :Wind
*/
@Deprecated
public static void createRestrictedSmsBlend(SupplierConfig config) {
SmsBlend smsBlend = create(config);
smsBlend = renderWithRestricted(smsBlend);
register(smsBlend);
}
@ -109,11 +107,10 @@ public abstract class SmsFactory {
* @param configId 配置ID
* @author :Wind
*/
@Deprecated
public static void createRestrictedSmsBlend(SmsReadConfig smsReadConfig, String configId) {
BaseConfig supplierConfig = smsReadConfig.getSupplierConfig(configId);
supplierConfig.setConfigId(configId);
SmsBlend smsBlend = create(supplierConfig);
smsBlend = renderWithRestricted(smsBlend);
register(smsBlend);
}
@ -126,11 +123,11 @@ public abstract class SmsFactory {
* @param smsReadConfig 读取额外配置接口
* @author :Wind
*/
@Deprecated
public static void createRestrictedSmsBlend(SmsReadConfig smsReadConfig) {
List<BaseConfig> supplierConfigList = smsReadConfig.getSupplierConfigList();
supplierConfigList.forEach(supplierConfig -> {
SmsBlend smsBlend = create(supplierConfig);
smsBlend = renderWithRestricted(smsBlend);
register(smsBlend);
});
}
@ -140,18 +137,22 @@ public abstract class SmsFactory {
if (factory == null) {
throw new SmsBlendException("不支持当前供应商配置");
}
return factory.createSms(config);
SmsBlend sms = factory.createSms(config);
return renderWithProxy(sms);
}
/**
* renderWithRestricted
* <p> 构建smsBlend对象的代理对象
*
* @author :Wind
*/
private static SmsBlend renderWithRestricted(SmsBlend sms) {
SmsInvocationHandler smsInvocationHandler = SmsInvocationHandler.newSmsInvocationHandler(sms);
return (SmsBlend) Proxy.newProxyInstance(sms.getClass().getClassLoader(), new Class[]{SmsBlend.class}, smsInvocationHandler);
@Deprecated
private static SmsBlend renderWithProxy(SmsBlend sms) {
return SmsProxyFactory.getProxySmsBlend(sms);
}
/**

View File

@ -0,0 +1,34 @@
package org.dromara.sms4j.core.proxy;
import org.dromara.sms4j.provider.config.SmsConfig;
import java.util.Map;
/**
* 环境信息持有
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
public class EnvirmentHolder {
private static SmsConfig smsConfig = null;
private static Map<String, Map<String, Object>> blends = null;
public static void frozenEnvirmet(SmsConfig smsConfig, Map<String, Map<String, Object>> blends) {
if (null!=EnvirmentHolder.smsConfig||null!=EnvirmentHolder.blends){
return;
}
EnvirmentHolder.smsConfig = smsConfig;
EnvirmentHolder.blends = blends;
}
//只有核心包执行器部分才能获取
static SmsConfig getSmsConfig() {
return smsConfig;
}
//只有核心包执行器部分才能获取
static Map<String, Map<String, Object>> getBlends() {
return blends;
}
}

View File

@ -1,58 +0,0 @@
package org.dromara.sms4j.core.proxy;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.dao.SmsDao;
import org.dromara.sms4j.api.proxy.RestrictedProcess;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.provider.config.SmsConfig;
import org.dromara.sms4j.provider.factory.BeanFactory;
import java.util.Objects;
@Slf4j
public class RestrictedProcessDefaultImpl implements RestrictedProcess {
static Long minTimer = 60 * 1000L;
static Long accTimer = 24 * 60 * 60 * 1000L;
/**
* 缓存实例
*/
@Setter
private SmsDao smsDao;
public SmsBlendException process(String phone) {
if (Objects.isNull(smsDao)) {
throw new SmsBlendException("The dao tool could not be found");
}
SmsConfig config = BeanFactory.getSmsConfig();
Integer accountMax = config.getAccountMax(); // 每日最大发送量
Integer minuteMax = config.getMinuteMax(); // 每分钟最大发送量
if (SmsUtils.isNotEmpty(accountMax)) { // 是否配置了每日限制
Integer i = (Integer) smsDao.get(phone + "max");
if (SmsUtils.isEmpty(i)) {
smsDao.set(phone + "max", 1, accTimer);
} else if (i >= accountMax) {
log.info("The phone:" + phone + ",number of short messages reached the maximum today");
return new SmsBlendException("The phone:" + phone + ",number of short messages reached the maximum today");
} else {
smsDao.set(phone + "max", i + 1, accTimer);
}
}
if (SmsUtils.isNotEmpty(minuteMax)) { // 是否配置了每分钟最大限制
Integer o = (Integer) smsDao.get(phone);
if (SmsUtils.isNotEmpty(o)) {
if (o < minuteMax) {
smsDao.set(phone, o + 1, minTimer);
} else {
log.info("The phone:" + phone + " Text messages are sent too often");
return new SmsBlendException("The phone:", phone + " Text messages are sent too often");
}
} else {
smsDao.set(phone, 1, minTimer);
}
}
return null;
}
}

View File

@ -2,45 +2,69 @@ package org.dromara.sms4j.core.proxy;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.proxy.RestrictedProcess;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.api.proxy.SmsProcessor;
import org.dromara.sms4j.api.proxy.SuppotFilter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.LinkedList;
import java.util.List;
/**
* SmsBlend增强封装smsblend和执行器
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
@Slf4j
public class SmsInvocationHandler implements InvocationHandler {
private final SmsBlend smsBlend;
private static RestrictedProcess restrictedProcess = new RestrictedProcessDefaultImpl();
private final LinkedList<SmsProcessor> processors;
private SmsInvocationHandler(SmsBlend smsBlend) {
public SmsInvocationHandler(SmsBlend smsBlend, LinkedList<SmsProcessor> processors) {
this.smsBlend = smsBlend;
}
public static SmsInvocationHandler newSmsInvocationHandler(SmsBlend smsBlend) {
return new SmsInvocationHandler(smsBlend);
this.processors = processors;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
Object result;
if ("sendMessage".equals(method.getName()) || "massTexting".equals(method.getName())) {
//取手机号作为参数
String phone = (String) objects[0];
SmsBlendException smsBlendException = restrictedProcess.process(phone);
if (!Objects.isNull(smsBlendException)) {
throw smsBlendException;
}
}
Object result = null;
//前置执行器
objects = doPreProcess(smsBlend, method, objects);
try {
result = method.invoke(smsBlend, objects);
} catch (Exception e) {
//错误执行器
doErrorHandleProcess(smsBlend, method, objects);
}
//后置执行器
doPostrocess(smsBlend, method, objects, result);
return result;
}
/**
* 设置 restrictedProcess
*/
public static void setRestrictedProcess(RestrictedProcess restrictedProcess) {
SmsInvocationHandler.restrictedProcess = restrictedProcess;
public Object[] doPreProcess(Object o, Method method, Object[] objects) {
for (SmsProcessor processor : processors) {
objects = processor.preProcessor(method, o, objects);
}
return objects;
}
public void doErrorHandleProcess(Object o, Method method, Object[] objects) throws InvocationTargetException, IllegalAccessException {
for (SmsProcessor processor : processors) {
processor.exceptionHandleProcessor(method, o, objects);
}
}
public Object doPostrocess(Object o, Method method, Object[] objects, Object result) {
for (SmsProcessor processor : processors) {
Object overrideResult = processor.postProcessor(result, objects);
if (overrideResult != null) {
return overrideResult;
}
}
return result;
}
}

View File

@ -0,0 +1,108 @@
package org.dromara.sms4j.core.proxy;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.dao.SmsDao;
import org.dromara.sms4j.api.dao.SmsDaoDefaultImpl;
import org.dromara.sms4j.api.proxy.Order;
import org.dromara.sms4j.api.proxy.SmsProcessor;
import org.dromara.sms4j.api.proxy.SuppotFilter;
import org.dromara.sms4j.api.proxy.aware.SmsBlendConfigAware;
import org.dromara.sms4j.api.proxy.aware.SmsDaoAware;
import org.dromara.sms4j.api.proxy.aware.SmsConfigAware;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
/**
* SmsBlend代理工厂
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
@Slf4j
public abstract class SmsProxyFactory {
private static final LinkedList<SmsProcessor> processors = new LinkedList<>();
public static SmsBlend getProxySmsBlend(SmsBlend smsBlend) {
LinkedList<SmsProcessor> ownerProcessors = processors.stream().filter(processor -> !shouldSkipProcess(processor,smsBlend)).collect(Collectors.toCollection(LinkedList::new));
return (SmsBlend) Proxy.newProxyInstance(smsBlend.getClass().getClassLoader(), new Class[]{SmsBlend.class}, new SmsInvocationHandler(smsBlend, ownerProcessors));
}
/**
* 增加拦截器
*/
public static void addProcessor(SmsProcessor processor) {
//校验拦截器是否正确
processorValidate(processor);
awareTransfer(processor);
processors.add(processor);
processors.sort(Comparator.comparingInt(Order::getOrder));
}
/*
* @see SuppotFilter
*/
public static boolean shouldSkipProcess(SmsProcessor processor, SmsBlend smsBlend) {
//判断当前的执行器有没有开厂商过滤支不支持当前厂商
if (processor instanceof SuppotFilter) {
List<String> supports = ((SuppotFilter) processor).getSupports();
boolean exsit = supports.stream().filter(support -> support.equals(smsBlend.getSupplier())).findAny().isPresent();
if (!exsit) {
return true;
}
}
return false;
}
//所有处理器需要的各个参数可以通过这种aware接口形式传给对象
private static void awareTransfer(SmsProcessor processor) {
if (processor instanceof SmsDaoAware){
((SmsDaoAware) processor).setSmsDao(getSmsDaoFromFramework());
}
if (processor instanceof SmsConfigAware){
((SmsConfigAware) processor).setSmsConfig(EnvirmentHolder.getSmsConfig());
}
if (processor instanceof SmsBlendConfigAware){
((SmsBlendConfigAware) processor).setSmsBlendsConfig(EnvirmentHolder.getBlends());
}
}
//校验拦截器是否正确
private static void processorValidate(SmsProcessor processor) {
}
//获取Sms的实现
private static SmsDao getSmsDaoFromFramework() {
SmsDao smsDao;
smsDao = getSmsDaoFromFramework("org.dromara.sms4j.starter.holder.SpringSmsDaoHolder", "SpringBoot");
if (null != smsDao) {
return smsDao;
}
smsDao = getSmsDaoFromFramework("org.dromara.sms4j.solon.holder.SolonSmsDaoHolder", "Solon");
if (null != smsDao) {
return smsDao;
}
log.error("尝试框架加载失败最终使用默认SmsDao");
return SmsDaoDefaultImpl.getInstance();
}
//获取Sms的实现
private static SmsDao getSmsDaoFromFramework(String className, String frameworkName) {
try {
Class<?> clazz = Class.forName(className);
Method getSmsDao = clazz.getMethod("getSmsDao", null);
return (SmsDao) getSmsDao.invoke(null, null);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
log.error("{}:加载SmsDao失败尝试其他框架加载......", frameworkName);
}
return null;
}
}

View File

@ -19,9 +19,13 @@ import org.dromara.sms4j.cloopen.config.CloopenFactory;
import org.dromara.sms4j.comm.constant.Constant;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.core.proxy.EnvirmentHolder;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.core.proxy.RestrictedProcessDefaultImpl;
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
import org.dromara.sms4j.core.proxy.processor.BlackListProcessor;
import org.dromara.sms4j.core.proxy.processor.CoreMethodParamValidateProcessor;
import org.dromara.sms4j.core.proxy.processor.RestrictedProcessor;
import org.dromara.sms4j.core.proxy.SmsProxyFactory;
import org.dromara.sms4j.core.proxy.processor.SingleBlendRestrictedProcessor;
import org.dromara.sms4j.ctyun.config.CtyunFactory;
import org.dromara.sms4j.emay.config.EmayFactory;
import org.dromara.sms4j.huawei.config.HuaweiFactory;
@ -99,16 +103,13 @@ public class SEInitializer {
return;
}
for (SupplierConfig supplierConfig : configList) {
if(Boolean.TRUE.equals(smsConfig.getRestricted())) {
SmsFactory.createRestrictedSmsBlend(supplierConfig);
} else {
SmsFactory.createSmsBlend(supplierConfig);
}
}
}
/**
* 注册服务商工厂
*
* @param factory 服务商工厂
*/
public SEInitializer registerFactory(BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig> factory) {
@ -118,15 +119,13 @@ public class SEInitializer {
/**
* 注册DAO实例
*
* @param smsDao DAO实例
*/
public SEInitializer registerSmsDao(SmsDao smsDao) {
if (smsDao == null) {
throw new SmsBlendException("注册DAO实例失败实例不能为空");
}
RestrictedProcessDefaultImpl process = new RestrictedProcessDefaultImpl();
process.setSmsDao(smsDao);
SmsInvocationHandler.setRestrictedProcess(process);
this.smsDao = smsDao;
return this;
}
@ -152,8 +151,18 @@ public class SEInitializer {
//初始化SmsConfig整体配置文件
BeanUtil.copyProperties(smsConfig, BeanFactory.getSmsConfig());
// 解析服务商配置
Map<String, Map<String, Object>> blends = smsConfig.getBlends();
//持有初始化配置信息
EnvirmentHolder.frozenEnvirmet(smsConfig, blends);
//注册执行器实现
SmsProxyFactory.addProcessor(new RestrictedProcessor());
SmsProxyFactory.addProcessor(new BlackListProcessor());
SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor());
SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor());
for (String configId : blends.keySet()) {
Map<String, Object> configMap = blends.get(configId);
Object supplierObj = configMap.get(Constant.SUPPLIER_KEY);
@ -168,13 +177,9 @@ public class SEInitializer {
SmsUtils.replaceKeysSeperator(configMap, "-", "_");
JSONObject configJson = new JSONObject(configMap);
SupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass());
if(Boolean.TRUE.equals(smsConfig.getRestricted())) {
SmsFactory.createRestrictedSmsBlend(supplierConfig);
} else {
SmsFactory.createSmsBlend(supplierConfig);
}
}
}
/**
* 注册默认工厂实例

View File

@ -73,4 +73,9 @@ public abstract class BaseConfig implements SupplierConfig {
}
this.maxRetries = maxRetries;
}
/**
* 最大发送数量默认integer上限
*/
private int maximum = Integer.MAX_VALUE;
}

View File

@ -4,6 +4,8 @@ package org.dromara.sms4j.provider.config;
import lombok.Data;
import org.dromara.sms4j.comm.enumerate.ConfigType;
import java.util.ArrayList;
@Data
public class SmsConfig {
@ -53,4 +55,9 @@ public class SmsConfig {
/** 是否打印http log*/
private Boolean HttpLog = false;
/**
* 黑名单配置
*/
private ArrayList<String> blackList = new ArrayList<>();
}

View File

@ -1,61 +0,0 @@
package org.dromara.sms4j.solon.aop;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.dao.SmsDao;
import org.dromara.sms4j.api.dao.SmsDaoDefaultImpl;
import org.dromara.sms4j.api.proxy.RestrictedProcess;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.provider.config.SmsConfig;
import org.dromara.sms4j.provider.factory.BeanFactory;
import org.noear.solon.core.AppContext;
@Slf4j
public class SolonRestrictedProcess implements RestrictedProcess {
private static final Long minTimer = 60 * 1000L;
private static final Long accTimer = 24 * 60 * 60 * 1000L;
private static final String REDIS_KEY = "sms:restricted:";
private SmsDao smsDao;
public SolonRestrictedProcess(AppContext context) {
context.getBeanAsync(SmsDao.class, bean -> smsDao = bean);
}
@Override
public SmsBlendException process(String phone) {
if (SmsUtils.isEmpty(smsDao)){
smsDao = SmsDaoDefaultImpl.getInstance();
}
SmsConfig config = BeanFactory.getSmsConfig();
Integer accountMax = config.getAccountMax(); // 每日最大发送量
Integer minuteMax = config.getMinuteMax(); // 每分钟最大发送量
if (SmsUtils.isNotEmpty(accountMax)) { // 是否配置了每日限制
Integer i = (Integer) smsDao.get(REDIS_KEY + phone + "max");
if (SmsUtils.isEmpty(i)) {
smsDao.set(REDIS_KEY + phone + "max", 1, accTimer / 1000);
} else if (i >= accountMax) {
log.info("The phone:" + phone + ",number of short messages reached the maximum today");
return new SmsBlendException("The phone:" + phone + ",number of short messages reached the maximum today");
} else {
smsDao.set(REDIS_KEY + phone + "max", i + 1, accTimer / 1000);
}
}
if (SmsUtils.isNotEmpty(minuteMax)) { // 是否配置了每分钟最大限制
Integer o = (Integer) smsDao.get(REDIS_KEY + phone);
if (SmsUtils.isNotEmpty(o)) {
if (o < minuteMax) {
smsDao.set(REDIS_KEY + phone, o + 1, minTimer / 1000);
} else {
log.info("The phone:" + phone + ",number of short messages reached the maximum today");
return new SmsBlendException("The phone:", phone + " Text messages are sent too often");
}
} else {
smsDao.set(REDIS_KEY + phone, 1, minTimer / 1000);
}
}
return null;
}
}

View File

@ -10,8 +10,13 @@ import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.cloopen.config.CloopenFactory;
import org.dromara.sms4j.comm.constant.Constant;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.core.proxy.EnvirmentHolder;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
import org.dromara.sms4j.core.proxy.processor.BlackListProcessor;
import org.dromara.sms4j.core.proxy.processor.CoreMethodParamValidateProcessor;
import org.dromara.sms4j.core.proxy.processor.RestrictedProcessor;
import org.dromara.sms4j.core.proxy.SmsProxyFactory;
import org.dromara.sms4j.core.proxy.processor.SingleBlendRestrictedProcessor;
import org.dromara.sms4j.ctyun.config.CtyunFactory;
import org.dromara.sms4j.emay.config.EmayFactory;
import org.dromara.sms4j.huawei.config.HuaweiFactory;
@ -21,7 +26,7 @@ import org.dromara.sms4j.netease.config.NeteaseFactory;
import org.dromara.sms4j.provider.config.SmsConfig;
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
import org.dromara.sms4j.solon.aop.SolonRestrictedProcess;
import org.dromara.sms4j.solon.holder.SolonSmsDaoHolder;
import org.dromara.sms4j.tencent.config.TencentFactory;
import org.dromara.sms4j.unisms.config.UniFactory;
import org.dromara.sms4j.yunpian.config.YunPianFactory;
@ -56,6 +61,15 @@ public class SmsBlendsInitializer {
this.registerDefaultFactory();
// 注册短信对象工厂
ProviderFactoryHolder.registerFactory(factoryList);
//持有初始化配置信息
EnvirmentHolder.frozenEnvirmet(smsConfig, blends);
//框架依赖持有缓存扩展
new SolonSmsDaoHolder(context);
//注册执行器实现
SmsProxyFactory.addProcessor(new RestrictedProcessor());
SmsProxyFactory.addProcessor(new BlackListProcessor());
SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor());
SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor());
// 解析供应商配置
for(String configId : blends.keySet()) {
Map<String, Object> configMap = blends.get(configId);
@ -71,15 +85,9 @@ public class SmsBlendsInitializer {
SmsUtils.replaceKeysSeperator(configMap, "-", "_");
JSONObject configJson = new JSONObject(configMap);
SupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass());
if(Boolean.TRUE.equals(smsConfig.getRestricted())) {
SmsFactory.createRestrictedSmsBlend(supplierConfig);
} else {
SmsFactory.createSmsBlend(supplierConfig);
}
}
//注册短信拦截实现
SmsInvocationHandler.setRestrictedProcess(new SolonRestrictedProcess(context));
}
/**

View File

@ -0,0 +1,22 @@
package org.dromara.sms4j.solon.holder;
import org.dromara.sms4j.api.dao.SmsDao;
import org.dromara.sms4j.api.dao.SmsDaoDefaultImpl;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.noear.solon.core.AppContext;
public class SolonSmsDaoHolder{
private static SmsDao smsDao;
public SolonSmsDaoHolder(AppContext context) {
context.getBeanAsync(SmsDao.class, bean -> smsDao = bean);
}
public static SmsDao getSmsDao() {
if (SmsUtils.isEmpty(smsDao)){
smsDao = SmsDaoDefaultImpl.getInstance();
}
return smsDao;
}
}

View File

@ -1,6 +1,11 @@
sms:
# 标注从yml读取配置
config-type: yaml
# 黑名单
black-list:
-13899998888
# 账户上限
account-max: 1
blends:
# 阿里短信例子
ali:
@ -60,6 +65,8 @@ sms:
template-id: pub_verif_short
# 模版名称
templateName: code
# 渠道上限
maximum: 2
lianlu:
supplier: lianlu
templateId: 模板id

View File

@ -0,0 +1,120 @@
package org.dromara.sms4j.example;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.api.proxy.CoreMethodProcessor;
import org.dromara.sms4j.comm.constant.SupplierConstant;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
/**
* @author sh1yu
*/
@Slf4j
@SpringBootTest
public class SmsProcessorTest {
/**
* 填测试手机号
*/
private static final String PHONE = "13899998888";
//参数校验测试
@Test
public void testParamValidate() {
SmsBlendException knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.ALIBABA).sendMessage("", SmsUtils.getRandomInt(6));
} catch (SmsBlendException e) {
knowEx = e;
}
Assert.notNull(knowEx);
knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.ALIBABA).sendMessage(PHONE, "");
} catch (SmsBlendException e) {
knowEx = e;
}
Assert.notNull(knowEx);
knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.ALIBABA).sendMessage(PHONE, "111", new LinkedHashMap<>());
} catch (SmsBlendException e) {
knowEx = e;
}
Assert.notNull(knowEx);
knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.ALIBABA).massTexting(Collections.singletonList(PHONE), "");
} catch (SmsBlendException e) {
knowEx = e;
}
Assert.notNull(knowEx);
knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.ALIBABA).massTexting(Collections.singletonList(PHONE), "2222", new LinkedHashMap<>());
} catch (SmsBlendException e) {
knowEx = e;
}
Assert.notNull(knowEx);
knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.ALIBABA).massTexting(new ArrayList<String>(), "321321");
} catch (SmsBlendException e) {
knowEx = e;
}
Assert.notNull(knowEx);
}
//黑名单测试 黑名单手机号13899998888
@Test
public void testBlackList() {
SmsBlendException knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.ALIBABA).sendMessage(PHONE, SmsUtils.getRandomInt(6));
} catch (SmsBlendException e) {
knowEx = e;
}
Assert.notNull(knowEx);
}
//账号级上限测试需成功发送一笔特殊配置
@Test
public void testAcctLimt() {
SmsResponse smsResponse = SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE, SmsUtils.getRandomInt(6));
Assert.isTrue(smsResponse.isSuccess());
SmsBlendException knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE, SmsUtils.getRandomInt(6));
} catch (SmsBlendException e) {
knowEx = e;
}
Assert.notNull(knowEx);
}
//账号级上限测试需成功发送一笔特殊配置
@Test
public void testChannelLimt() {
SmsResponse smsResponse = SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE, SmsUtils.getRandomInt(6));
Assert.isTrue(smsResponse.isSuccess());
SmsBlendException knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE, SmsUtils.getRandomInt(6));
} catch (SmsBlendException e) {
knowEx = e;
}
Assert.notNull(knowEx);
}
}

View File

@ -1,55 +0,0 @@
package org.dromara.sms4j.starter.aop;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.dao.SmsDao;
import org.dromara.sms4j.api.dao.SmsDaoDefaultImpl;
import org.dromara.sms4j.api.proxy.RestrictedProcess;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.provider.config.SmsConfig;
import org.dromara.sms4j.provider.factory.BeanFactory;
import org.dromara.sms4j.starter.utils.SmsSpringUtils;
@Slf4j
public class SpringRestrictedProcess implements RestrictedProcess {
private static final Long minTimer = 60 * 1000L;
private static final Long accTimer = 24 * 60 * 60 * 1000L;
private static final String REDIS_KEY = "sms:restricted:";
@Override
public SmsBlendException process(String phone) {
SmsConfig config = BeanFactory.getSmsConfig();
SmsDao smsDao = SmsSpringUtils.getBean(SmsDao.class);
if (SmsUtils.isEmpty(smsDao)){
smsDao = SmsDaoDefaultImpl.getInstance();
}
Integer accountMax = config.getAccountMax(); // 每日最大发送量
Integer minuteMax = config.getMinuteMax(); // 每分钟最大发送量
if (SmsUtils.isNotEmpty(accountMax)) { // 是否配置了每日限制
Integer i = (Integer) smsDao.get(REDIS_KEY + phone + "max");
if (SmsUtils.isEmpty(i)) {
smsDao.set(REDIS_KEY + phone + "max", 1, accTimer / 1000);
} else if (i >= accountMax) {
log.info("The phone:" + phone + ",number of short messages reached the maximum today");
return new SmsBlendException("The phone:" + phone + ",number of short messages reached the maximum today");
} else {
smsDao.set(REDIS_KEY + phone + "max", i + 1, accTimer / 1000);
}
}
if (SmsUtils.isNotEmpty(minuteMax)) { // 是否配置了每分钟最大限制
Integer o = (Integer) smsDao.get(REDIS_KEY + phone);
if (SmsUtils.isNotEmpty(o)) {
if (o < minuteMax) {
smsDao.set(REDIS_KEY + phone, o + 1, minTimer / 1000);
} else {
log.info("The phone:" + phone + ",number of short messages reached the maximum today");
return new SmsBlendException("The phone:", phone + " Text messages are sent too often");
}
} else {
smsDao.set(REDIS_KEY + phone, 1, minTimer / 1000);
}
}
return null;
}
}

View File

@ -11,8 +11,13 @@ import org.dromara.sms4j.cloopen.config.CloopenFactory;
import org.dromara.sms4j.comm.constant.Constant;
import org.dromara.sms4j.comm.enumerate.ConfigType;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.core.proxy.EnvirmentHolder;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
import org.dromara.sms4j.core.proxy.processor.BlackListProcessor;
import org.dromara.sms4j.core.proxy.processor.CoreMethodParamValidateProcessor;
import org.dromara.sms4j.core.proxy.processor.RestrictedProcessor;
import org.dromara.sms4j.core.proxy.SmsProxyFactory;
import org.dromara.sms4j.core.proxy.processor.SingleBlendRestrictedProcessor;
import org.dromara.sms4j.ctyun.config.CtyunFactory;
import org.dromara.sms4j.emay.config.EmayFactory;
import org.dromara.sms4j.huawei.config.HuaweiFactory;
@ -22,7 +27,6 @@ import org.dromara.sms4j.netease.config.NeteaseFactory;
import org.dromara.sms4j.provider.config.SmsConfig;
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
import org.dromara.sms4j.starter.aop.SpringRestrictedProcess;
import org.dromara.sms4j.tencent.config.TencentFactory;
import org.dromara.sms4j.unisms.config.UniFactory;
import org.dromara.sms4j.yunpian.config.YunPianFactory;
@ -53,7 +57,15 @@ public class SmsBlendsInitializer {
this.registerDefaultFactory();
// 注册短信对象工厂
ProviderFactoryHolder.registerFactory(factoryList);
if(ConfigType.YAML.equals(this.smsConfig.getConfigType())) {
//持有初始化配置信息
EnvirmentHolder.frozenEnvirmet(smsConfig, blends);
//注册执行器实现
SmsProxyFactory.addProcessor(new RestrictedProcessor());
SmsProxyFactory.addProcessor(new BlackListProcessor());
SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor());
SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor());
// 解析供应商配置
for(String configId : blends.keySet()) {
Map<String, Object> configMap = blends.get(configId);
@ -69,16 +81,11 @@ public class SmsBlendsInitializer {
SmsUtils.replaceKeysSeperator(configMap, "-", "_");
JSONObject configJson = new JSONObject(configMap);
org.dromara.sms4j.api.universal.SupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass());
if(Boolean.TRUE.equals(smsConfig.getRestricted())) {
SmsFactory.createRestrictedSmsBlend(supplierConfig);
} else {
SmsFactory.createSmsBlend(supplierConfig);
}
}
}
//注册短信拦截实现
SmsInvocationHandler.setRestrictedProcess(new SpringRestrictedProcess());
}
/**

View File

@ -0,0 +1,12 @@
package org.dromara.sms4j.starter.holder;
import org.dromara.sms4j.api.dao.SmsDao;
import org.dromara.sms4j.api.dao.SmsDaoDefaultImpl;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.starter.utils.SmsSpringUtils;
public class SpringSmsDaoHolder {
public static SmsDao getSmsDao() {
return SmsSpringUtils.getBean(SmsDao.class);
}
}