mirror of
https://gitee.com/dromara/sms4j.git
synced 2025-12-07 01:18:33 +08:00
!109 应用扩展,黑名单实现、渠道上限拦截实现、基础参数统一校验实现
Merge pull request !109 from sh1yu/dev-3.0.x
This commit is contained in:
commit
4616be9cef
@ -3,12 +3,14 @@ package org.dromara.sms4j.api;
|
|||||||
import org.dromara.sms4j.api.callback.CallBack;
|
import org.dromara.sms4j.api.callback.CallBack;
|
||||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* SmsBlend
|
* SmsBlend
|
||||||
* <p> 通用接口,定义国内短信方法
|
* <p> 通用接口,定义国内短信方法
|
||||||
|
*
|
||||||
* @author :Wind
|
* @author :Wind
|
||||||
* 2023/5/16 16:03
|
* 2023/5/16 16:03
|
||||||
**/
|
**/
|
||||||
@ -16,12 +18,14 @@ public interface SmsBlend {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取短信实例唯一标识
|
* 获取短信实例唯一标识
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
String getConfigId();
|
String getConfigId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取供应商标识
|
* 获取供应商标识
|
||||||
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
String getSupplier();
|
String getSupplier();
|
||||||
@ -151,4 +155,43 @@ public interface SmsBlend {
|
|||||||
*/
|
*/
|
||||||
void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
|
void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:加入黑名单【这个需要有全局操作的同时需要操作缓存,那么不给smsblend实际处理,代理部分处理】
|
||||||
|
* joinInBlacklist
|
||||||
|
*
|
||||||
|
* @param phone 需要加入黑名单的手机号
|
||||||
|
* @author :sh1yu
|
||||||
|
*/
|
||||||
|
default void joinInBlacklist(String phone) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:从黑名单移除【为了sms4j组件有统一入口,同时这个需要有全局操作的同时需要操作缓存,那么不给smsblend实际处理,代理部分处理】
|
||||||
|
* removeFromBlacklist
|
||||||
|
*
|
||||||
|
* @param phone 需要加入黑名单的手机号
|
||||||
|
* @author :sh1yu
|
||||||
|
*/
|
||||||
|
default void removeFromBlacklist(String phone) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:批量加入黑名单【为了sms4j组件有统一入口,同时这个需要有全局操作的同时需要操作缓存,那么不给smsblend实际处理,代理部分处理】
|
||||||
|
* batchJoinBlacklist
|
||||||
|
*
|
||||||
|
* @param phones 需要加入黑名单的手机号数组
|
||||||
|
* @author :sh1yu
|
||||||
|
*/
|
||||||
|
default void batchJoinBlacklist(List<String > phones) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:批量从黑名单移除【为了sms4j组件有统一入口,同时这个需要有全局操作的同时需要操作缓存,那么不给smsblend实际处理,代理部分处理】
|
||||||
|
* batchRemovalFromBlacklist
|
||||||
|
*
|
||||||
|
* @param phones 需要移除黑名单的手机号数组
|
||||||
|
* @author :sh1yu
|
||||||
|
*/
|
||||||
|
default void batchRemovalFromBlacklist(List<String > phones) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,44 @@
|
|||||||
|
package org.dromara.sms4j.api.proxy;
|
||||||
|
|
||||||
|
import org.dromara.sms4j.comm.constant.NumberOfParasmeters;
|
||||||
|
|
||||||
|
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 (NumberOfParasmeters.TWO == NumberOfParasmeters.getNumberOfParasmetersEnum(parameterCount)) {
|
||||||
|
sendMessagePreProcess((String) param[0],(String) param[1]);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
if (NumberOfParasmeters.THREE == NumberOfParasmeters.getNumberOfParasmetersEnum(parameterCount)) {
|
||||||
|
sendMessageByTemplatePreProcess((String)param[0],(String) param[1],(LinkedHashMap<String, String>)param[2]);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ("massTexting".equals(method.getName())) {
|
||||||
|
if (NumberOfParasmeters.TWO == NumberOfParasmeters.getNumberOfParasmetersEnum(parameterCount)) {
|
||||||
|
massTextingPreProcess((List<String>)param[0],(String)param[1]);
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
if (NumberOfParasmeters.THREE == NumberOfParasmeters.getNumberOfParasmetersEnum(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);
|
||||||
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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);
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -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,Exception exception) throws InvocationTargetException, IllegalAccessException {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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();
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
@ -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);
|
||||||
|
}
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
package org.dromara.sms4j.comm.constant;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NumberOfParasmeters
|
||||||
|
* <p> 重载方法的参数个数
|
||||||
|
*
|
||||||
|
* @author :sh1yu
|
||||||
|
* 2023/11/01 19:33
|
||||||
|
**/
|
||||||
|
public enum NumberOfParasmeters {
|
||||||
|
//一个参数
|
||||||
|
ONE(1),
|
||||||
|
//两个参数
|
||||||
|
TWO(2),
|
||||||
|
//三个参数
|
||||||
|
THREE(3);
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
NumberOfParasmeters(int code) {
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NumberOfParasmeters getNumberOfParasmetersEnum(int index) {
|
||||||
|
switch (index) {
|
||||||
|
case 1:
|
||||||
|
return NumberOfParasmeters.ONE;
|
||||||
|
case 2:
|
||||||
|
return NumberOfParasmeters.TWO;
|
||||||
|
case 3:
|
||||||
|
return NumberOfParasmeters.THREE;
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("building enum NumberOfParasmeters error,param not match");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,12 +6,11 @@ import org.dromara.sms4j.api.universal.SupplierConfig;
|
|||||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||||
import org.dromara.sms4j.core.datainterface.SmsReadConfig;
|
import org.dromara.sms4j.core.datainterface.SmsReadConfig;
|
||||||
import org.dromara.sms4j.core.load.SmsLoad;
|
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.config.BaseConfig;
|
||||||
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
|
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
|
||||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
||||||
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -64,7 +63,6 @@ public abstract class SmsFactory {
|
|||||||
*/
|
*/
|
||||||
public static void createSmsBlend(SmsReadConfig smsReadConfig, String configId) {
|
public static void createSmsBlend(SmsReadConfig smsReadConfig, String configId) {
|
||||||
BaseConfig supplierConfig = smsReadConfig.getSupplierConfig(configId);
|
BaseConfig supplierConfig = smsReadConfig.getSupplierConfig(configId);
|
||||||
supplierConfig.setConfigId(configId);
|
|
||||||
SmsBlend smsBlend = create(supplierConfig);
|
SmsBlend smsBlend = create(supplierConfig);
|
||||||
register(smsBlend);
|
register(smsBlend);
|
||||||
}
|
}
|
||||||
@ -93,9 +91,9 @@ public abstract class SmsFactory {
|
|||||||
* @param config 短信配置
|
* @param config 短信配置
|
||||||
* @author :Wind
|
* @author :Wind
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static void createRestrictedSmsBlend(SupplierConfig config) {
|
public static void createRestrictedSmsBlend(SupplierConfig config) {
|
||||||
SmsBlend smsBlend = create(config);
|
SmsBlend smsBlend = create(config);
|
||||||
smsBlend = renderWithRestricted(smsBlend);
|
|
||||||
register(smsBlend);
|
register(smsBlend);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,11 +107,10 @@ public abstract class SmsFactory {
|
|||||||
* @param configId 配置ID
|
* @param configId 配置ID
|
||||||
* @author :Wind
|
* @author :Wind
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static void createRestrictedSmsBlend(SmsReadConfig smsReadConfig, String configId) {
|
public static void createRestrictedSmsBlend(SmsReadConfig smsReadConfig, String configId) {
|
||||||
BaseConfig supplierConfig = smsReadConfig.getSupplierConfig(configId);
|
BaseConfig supplierConfig = smsReadConfig.getSupplierConfig(configId);
|
||||||
supplierConfig.setConfigId(configId);
|
|
||||||
SmsBlend smsBlend = create(supplierConfig);
|
SmsBlend smsBlend = create(supplierConfig);
|
||||||
smsBlend = renderWithRestricted(smsBlend);
|
|
||||||
register(smsBlend);
|
register(smsBlend);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,11 +123,11 @@ public abstract class SmsFactory {
|
|||||||
* @param smsReadConfig 读取额外配置接口
|
* @param smsReadConfig 读取额外配置接口
|
||||||
* @author :Wind
|
* @author :Wind
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static void createRestrictedSmsBlend(SmsReadConfig smsReadConfig) {
|
public static void createRestrictedSmsBlend(SmsReadConfig smsReadConfig) {
|
||||||
List<BaseConfig> supplierConfigList = smsReadConfig.getSupplierConfigList();
|
List<BaseConfig> supplierConfigList = smsReadConfig.getSupplierConfigList();
|
||||||
supplierConfigList.forEach(supplierConfig -> {
|
supplierConfigList.forEach(supplierConfig -> {
|
||||||
SmsBlend smsBlend = create(supplierConfig);
|
SmsBlend smsBlend = create(supplierConfig);
|
||||||
smsBlend = renderWithRestricted(smsBlend);
|
|
||||||
register(smsBlend);
|
register(smsBlend);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -140,18 +137,22 @@ public abstract class SmsFactory {
|
|||||||
if (factory == null) {
|
if (factory == null) {
|
||||||
throw new SmsBlendException("不支持当前供应商配置");
|
throw new SmsBlendException("不支持当前供应商配置");
|
||||||
}
|
}
|
||||||
return factory.createSms(config);
|
SmsBlend sms = factory.createSms(config);
|
||||||
|
return renderWithProxy(sms);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* renderWithRestricted
|
* renderWithRestricted
|
||||||
* <p> 构建smsBlend对象的代理对象
|
* <p> 构建smsBlend对象的代理对象
|
||||||
*
|
*
|
||||||
* @author :Wind
|
* @author :Wind
|
||||||
*/
|
*/
|
||||||
private static SmsBlend renderWithRestricted(SmsBlend sms) {
|
@Deprecated
|
||||||
SmsInvocationHandler smsInvocationHandler = SmsInvocationHandler.newSmsInvocationHandler(sms);
|
private static SmsBlend renderWithProxy(SmsBlend sms) {
|
||||||
return (SmsBlend) Proxy.newProxyInstance(sms.getClass().getClassLoader(), new Class[]{SmsBlend.class}, smsInvocationHandler);
|
return SmsProxyFactory.getProxySmsBlend(sms);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -2,45 +2,69 @@ package org.dromara.sms4j.core.proxy;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.dromara.sms4j.api.SmsBlend;
|
import org.dromara.sms4j.api.SmsBlend;
|
||||||
import org.dromara.sms4j.api.proxy.RestrictedProcess;
|
import org.dromara.sms4j.api.proxy.SmsProcessor;
|
||||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
import org.dromara.sms4j.api.proxy.SuppotFilter;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
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
|
@Slf4j
|
||||||
public class SmsInvocationHandler implements InvocationHandler {
|
public class SmsInvocationHandler implements InvocationHandler {
|
||||||
private final SmsBlend smsBlend;
|
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;
|
this.smsBlend = smsBlend;
|
||||||
}
|
this.processors = processors;
|
||||||
|
|
||||||
public static SmsInvocationHandler newSmsInvocationHandler(SmsBlend smsBlend) {
|
|
||||||
return new SmsInvocationHandler(smsBlend);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
|
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
|
||||||
Object result;
|
Object result = null;
|
||||||
if ("sendMessage".equals(method.getName()) || "massTexting".equals(method.getName())) {
|
//前置执行器
|
||||||
//取手机号作为参数
|
objects = doPreProcess(smsBlend, method, objects);
|
||||||
String phone = (String) objects[0];
|
try {
|
||||||
SmsBlendException smsBlendException = restrictedProcess.process(phone);
|
|
||||||
if (!Objects.isNull(smsBlendException)) {
|
|
||||||
throw smsBlendException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result = method.invoke(smsBlend, objects);
|
result = method.invoke(smsBlend, objects);
|
||||||
|
} catch (Exception e) {
|
||||||
|
//错误执行器
|
||||||
|
doErrorHandleProcess(smsBlend, method, objects,e);
|
||||||
|
}
|
||||||
|
//后置执行器
|
||||||
|
doPostrocess(smsBlend, method, objects, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public Object[] doPreProcess(Object o, Method method, Object[] objects) {
|
||||||
* 设置 restrictedProcess
|
for (SmsProcessor processor : processors) {
|
||||||
*/
|
objects = processor.preProcessor(method, o, objects);
|
||||||
public static void setRestrictedProcess(RestrictedProcess restrictedProcess) {
|
}
|
||||||
SmsInvocationHandler.restrictedProcess = restrictedProcess;
|
return objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doErrorHandleProcess(Object o, Method method, Object[] objects,Exception e) throws InvocationTargetException, IllegalAccessException {
|
||||||
|
for (SmsProcessor processor : processors) {
|
||||||
|
processor.exceptionHandleProcessor(method, o, objects,e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
package org.dromara.sms4j.core.proxy.processor;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.sms4j.api.dao.SmsDao;
|
||||||
|
import org.dromara.sms4j.api.proxy.CoreMethodProcessor;
|
||||||
|
import org.dromara.sms4j.api.proxy.aware.SmsConfigAware;
|
||||||
|
import org.dromara.sms4j.api.proxy.aware.SmsDaoAware;
|
||||||
|
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||||
|
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 黑名单前置拦截执行器
|
||||||
|
*
|
||||||
|
* @author sh1yu
|
||||||
|
* @since 2023/10/27 13:03
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class BlackListProcessor implements CoreMethodProcessor, SmsDaoAware {
|
||||||
|
@Setter
|
||||||
|
SmsDao smsDao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessagePreProcess(String phone, String message) {
|
||||||
|
doRestricted(Collections.singletonList(phone));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessageByTemplatePreProcess(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||||
|
doRestricted(Collections.singletonList(phone));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void massTextingPreProcess(List<String> phones, String message) {
|
||||||
|
doRestricted(phones);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void massTextingByTemplatePreProcess(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||||
|
doRestricted(phones);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doRestricted(List<String> phones) {
|
||||||
|
ArrayList<String> blackList = (ArrayList<String>) smsDao.get("sms:blacklist:global");
|
||||||
|
for (String phone : phones) {
|
||||||
|
if (blackList.stream().filter(black -> black.replace("-","").equals(phone)).findAny().isPresent()) {
|
||||||
|
throw new SmsBlendException("The phone:", phone + " hit global blacklist!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
package org.dromara.sms4j.core.proxy.processor;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.sms4j.api.dao.SmsDao;
|
||||||
|
import org.dromara.sms4j.api.proxy.SmsProcessor;
|
||||||
|
import org.dromara.sms4j.api.proxy.aware.SmsConfigAware;
|
||||||
|
import org.dromara.sms4j.api.proxy.aware.SmsDaoAware;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 黑名单前置拦截执行器
|
||||||
|
*
|
||||||
|
* @author sh1yu
|
||||||
|
* @since 2023/10/27 13:03
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class BlackListRecordingProcessor implements SmsProcessor, SmsDaoAware, SmsConfigAware {
|
||||||
|
@Setter
|
||||||
|
SmsDao smsDao;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
Object smsConfig;
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] preProcessor(Method method, Object source, Object[] param) {
|
||||||
|
//添加到黑名单
|
||||||
|
if (method.getName().equals("joinInBlacklist")) {
|
||||||
|
String cacheKey = getCacheKey();
|
||||||
|
ArrayList<String> blackList = getBlackList(cacheKey);
|
||||||
|
blackList.add((String) param[0]);
|
||||||
|
flushBlackList(cacheKey,blackList);
|
||||||
|
}
|
||||||
|
//从黑名单移除
|
||||||
|
if (method.getName().equals("removeFromBlacklist")) {
|
||||||
|
String cacheKey = getCacheKey();
|
||||||
|
ArrayList<String> blackList = getBlackList(cacheKey);
|
||||||
|
blackList.remove((String) param[0]);
|
||||||
|
flushBlackList(cacheKey,blackList);
|
||||||
|
}
|
||||||
|
//批量添加到黑名单
|
||||||
|
if (method.getName().equals("batchJoinBlacklist")) {
|
||||||
|
String cacheKey = getCacheKey();
|
||||||
|
ArrayList<String> blackList = getBlackList(cacheKey);
|
||||||
|
blackList.addAll((List<String>) param[0]);
|
||||||
|
flushBlackList(cacheKey,blackList);
|
||||||
|
}
|
||||||
|
//批量从黑名单移除
|
||||||
|
if (method.getName().equals("batchRemovalFromBlacklist")) {
|
||||||
|
String cacheKey = getCacheKey();
|
||||||
|
ArrayList<String> blackList = getBlackList(cacheKey);
|
||||||
|
blackList.removeAll((List<String>) param[0]);
|
||||||
|
flushBlackList(cacheKey,blackList);
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
|
||||||
|
//构建cachekey
|
||||||
|
public String getCacheKey(){
|
||||||
|
return "sms:blacklist:global";
|
||||||
|
}
|
||||||
|
|
||||||
|
//获取黑名单,没有就新建
|
||||||
|
public ArrayList<String> getBlackList(String cacheKey) {
|
||||||
|
ArrayList<String> blackList;
|
||||||
|
Object cache = smsDao.get(cacheKey);
|
||||||
|
if (null != cache) {
|
||||||
|
blackList = (ArrayList<String>) cache;
|
||||||
|
return blackList;
|
||||||
|
}
|
||||||
|
blackList = new ArrayList<>();
|
||||||
|
smsDao.set("sms:blacklist:global", blackList);
|
||||||
|
return blackList;
|
||||||
|
}
|
||||||
|
|
||||||
|
//让黑名单生效
|
||||||
|
public void flushBlackList(String cacheKey ,ArrayList<String> blackList) {
|
||||||
|
smsDao.set(cacheKey, blackList);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
package org.dromara.sms4j.core.proxy.processor;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.sms4j.api.proxy.CoreMethodProcessor;
|
||||||
|
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 核心方法参数校验前置拦截执行器
|
||||||
|
*
|
||||||
|
* @author sh1yu
|
||||||
|
* @since 2023/10/27 13:03
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class CoreMethodParamValidateProcessor implements CoreMethodProcessor {
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessagePreProcess(String phone, String message) {
|
||||||
|
validatePhone(phone);
|
||||||
|
validateMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessageByTemplatePreProcess(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||||
|
validatePhone(phone);
|
||||||
|
validateMessages(templateId, messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void massTextingPreProcess(List<String> phones, String message) {
|
||||||
|
validateMessage(message);
|
||||||
|
validatePhones(phones);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void massTextingByTemplatePreProcess(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||||
|
validatePhones(phones);
|
||||||
|
validateMessages(templateId, messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateMessage(String message) {
|
||||||
|
if (null == message || "".equals(message)) {
|
||||||
|
throw new SmsBlendException("cant send a null message!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validatePhone(String phone) {
|
||||||
|
if (null == phone || "".equals(phone)) {
|
||||||
|
throw new SmsBlendException("cant send message to null!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validatePhones(List<String> phones) {
|
||||||
|
if (null == phones) {
|
||||||
|
throw new SmsBlendException("cant send message to null!");
|
||||||
|
}
|
||||||
|
for (String phone : phones) {
|
||||||
|
if (null != phone && !"".equals(phone)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new SmsBlendException("cant send message to null!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void validateMessages(String templateId, LinkedHashMap<String, String> messages) {
|
||||||
|
if (null != templateId && !"".equals(templateId) && (messages == null || messages.size() < 1)) {
|
||||||
|
throw new SmsBlendException("cant use template without template param");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
package org.dromara.sms4j.core.proxy.processor;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.sms4j.api.dao.SmsDao;
|
||||||
|
import org.dromara.sms4j.api.proxy.CoreMethodProcessor;
|
||||||
|
import org.dromara.sms4j.api.proxy.aware.SmsDaoAware;
|
||||||
|
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.Collections;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信发送账号级上限前置拦截执行器
|
||||||
|
*
|
||||||
|
* @author sh1yu
|
||||||
|
* @since 2023/10/27 13:03
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class RestrictedProcessor implements CoreMethodProcessor, SmsDaoAware {
|
||||||
|
static Long minTimer = 60 * 1000L;
|
||||||
|
static Long accTimer = 24 * 60 * 60 * 1000L;
|
||||||
|
private static final String REDIS_KEY = "sms:restricted:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存实例
|
||||||
|
*/
|
||||||
|
@Setter
|
||||||
|
private SmsDao smsDao;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessagePreProcess(String phone, String message) {
|
||||||
|
doRestricted(Collections.singletonList(phone));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendMessageByTemplatePreProcess(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||||
|
doRestricted(Collections.singletonList(phone));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void massTextingPreProcess(List<String> phones, String message) {
|
||||||
|
doRestricted(phones);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void massTextingByTemplatePreProcess(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||||
|
doRestricted(phones);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void doRestricted(List<String> phones) {
|
||||||
|
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(); // 每分钟最大发送量
|
||||||
|
for (String phone : phones) {
|
||||||
|
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");
|
||||||
|
throw 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");
|
||||||
|
throw new SmsBlendException("The phone:", phone + " Text messages are sent too often!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
smsDao.set(REDIS_KEY + phone, 1, minTimer / 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
package org.dromara.sms4j.core.proxy.processor;
|
||||||
|
|
||||||
|
import lombok.Setter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.sms4j.api.SmsBlend;
|
||||||
|
import org.dromara.sms4j.api.dao.SmsDao;
|
||||||
|
import org.dromara.sms4j.api.proxy.CoreMethodProcessor;
|
||||||
|
import org.dromara.sms4j.api.proxy.SmsProcessor;
|
||||||
|
import org.dromara.sms4j.api.proxy.aware.SmsBlendConfigAware;
|
||||||
|
import org.dromara.sms4j.api.proxy.aware.SmsDaoAware;
|
||||||
|
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||||
|
import org.dromara.sms4j.comm.utils.SmsUtils;
|
||||||
|
import org.dromara.sms4j.provider.config.BaseConfig;
|
||||||
|
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信发送渠道级上限前置拦截执行器
|
||||||
|
*
|
||||||
|
* @author sh1yu
|
||||||
|
* @since 2023/10/27 13:03
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class SingleBlendRestrictedProcessor implements SmsProcessor, SmsDaoAware, SmsBlendConfigAware {
|
||||||
|
|
||||||
|
private static final String REDIS_KEY = "sms:restricted:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存实例
|
||||||
|
*/
|
||||||
|
@Setter
|
||||||
|
private SmsDao smsDao;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
Map smsBlendsConfig;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] preProcessor(Method method, Object source, Object[] param) {
|
||||||
|
String name = method.getName();
|
||||||
|
if (!"sendMessage".equals(name) && !"massText".equals(name)) {
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
SmsBlend smsBlend = (SmsBlend) source;
|
||||||
|
String configId = smsBlend.getConfigId();
|
||||||
|
Map targetConfig = (Map) smsBlendsConfig.get(configId);
|
||||||
|
int maximum = (int) targetConfig.get("maximum");
|
||||||
|
Integer i = (Integer) smsDao.get(REDIS_KEY + configId + "maximum");
|
||||||
|
if (SmsUtils.isEmpty(i)) {
|
||||||
|
smsDao.set(REDIS_KEY + configId + "maximum", 1);
|
||||||
|
} else if (i >= maximum) {
|
||||||
|
log.info("The channel:" + configId + ",messages reached the maximum");
|
||||||
|
throw new SmsBlendException("The channel:" + configId + ",messages reached the maximum");
|
||||||
|
} else {
|
||||||
|
smsDao.set(REDIS_KEY + configId + "maximum", i + 1);
|
||||||
|
}
|
||||||
|
return param;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,9 +19,10 @@ import org.dromara.sms4j.cloopen.config.CloopenFactory;
|
|||||||
import org.dromara.sms4j.comm.constant.Constant;
|
import org.dromara.sms4j.comm.constant.Constant;
|
||||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||||
import org.dromara.sms4j.comm.utils.SmsUtils;
|
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.factory.SmsFactory;
|
||||||
import org.dromara.sms4j.core.proxy.RestrictedProcessDefaultImpl;
|
import org.dromara.sms4j.core.proxy.processor.*;
|
||||||
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
|
import org.dromara.sms4j.core.proxy.SmsProxyFactory;
|
||||||
import org.dromara.sms4j.ctyun.config.CtyunFactory;
|
import org.dromara.sms4j.ctyun.config.CtyunFactory;
|
||||||
import org.dromara.sms4j.emay.config.EmayFactory;
|
import org.dromara.sms4j.emay.config.EmayFactory;
|
||||||
import org.dromara.sms4j.huawei.config.HuaweiFactory;
|
import org.dromara.sms4j.huawei.config.HuaweiFactory;
|
||||||
@ -95,20 +96,17 @@ public class SEInitializer {
|
|||||||
// 初始化SmsConfig整体配置文件
|
// 初始化SmsConfig整体配置文件
|
||||||
BeanUtil.copyProperties(smsConfig, BeanFactory.getSmsConfig());
|
BeanUtil.copyProperties(smsConfig, BeanFactory.getSmsConfig());
|
||||||
// 创建短信服务对象
|
// 创建短信服务对象
|
||||||
if(CollUtil.isEmpty(configList)) {
|
if (CollUtil.isEmpty(configList)) {
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
for(SupplierConfig supplierConfig : configList) {
|
for (SupplierConfig supplierConfig : configList) {
|
||||||
if(Boolean.TRUE.equals(smsConfig.getRestricted())) {
|
|
||||||
SmsFactory.createRestrictedSmsBlend(supplierConfig);
|
|
||||||
} else {
|
|
||||||
SmsFactory.createSmsBlend(supplierConfig);
|
SmsFactory.createSmsBlend(supplierConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册服务商工厂
|
* 注册服务商工厂
|
||||||
|
*
|
||||||
* @param factory 服务商工厂
|
* @param factory 服务商工厂
|
||||||
*/
|
*/
|
||||||
public SEInitializer registerFactory(BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig> factory) {
|
public SEInitializer registerFactory(BaseProviderFactory<? extends SmsBlend, ? extends SupplierConfig> factory) {
|
||||||
@ -118,15 +116,13 @@ public class SEInitializer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册DAO实例
|
* 注册DAO实例
|
||||||
|
*
|
||||||
* @param smsDao DAO实例
|
* @param smsDao DAO实例
|
||||||
*/
|
*/
|
||||||
public SEInitializer registerSmsDao(SmsDao smsDao) {
|
public SEInitializer registerSmsDao(SmsDao smsDao) {
|
||||||
if(smsDao == null) {
|
if (smsDao == null) {
|
||||||
throw new SmsBlendException("注册DAO实例失败,实例不能为空");
|
throw new SmsBlendException("注册DAO实例失败,实例不能为空");
|
||||||
}
|
}
|
||||||
RestrictedProcessDefaultImpl process = new RestrictedProcessDefaultImpl();
|
|
||||||
process.setSmsDao(smsDao);
|
|
||||||
SmsInvocationHandler.setRestrictedProcess(process);
|
|
||||||
this.smsDao = smsDao;
|
this.smsDao = smsDao;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -143,7 +139,7 @@ public class SEInitializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//注册默认DAO实例
|
//注册默认DAO实例
|
||||||
if(this.smsDao == null) {
|
if (this.smsDao == null) {
|
||||||
this.registerSmsDao(SmsDaoDefaultImpl.getInstance());
|
this.registerSmsDao(SmsDaoDefaultImpl.getInstance());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,15 +148,26 @@ public class SEInitializer {
|
|||||||
|
|
||||||
//初始化SmsConfig整体配置文件
|
//初始化SmsConfig整体配置文件
|
||||||
BeanUtil.copyProperties(smsConfig, BeanFactory.getSmsConfig());
|
BeanUtil.copyProperties(smsConfig, BeanFactory.getSmsConfig());
|
||||||
|
|
||||||
// 解析服务商配置
|
// 解析服务商配置
|
||||||
Map<String, Map<String, Object>> blends = smsConfig.getBlends();
|
Map<String, Map<String, Object>> blends = smsConfig.getBlends();
|
||||||
for(String configId : blends.keySet()) {
|
|
||||||
|
//持有初始化配置信息
|
||||||
|
EnvirmentHolder.frozenEnvirmet(smsConfig, blends);
|
||||||
|
|
||||||
|
//注册执行器实现
|
||||||
|
SmsProxyFactory.addProcessor(new RestrictedProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new BlackListProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new BlackListRecordingProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor());
|
||||||
|
for (String configId : blends.keySet()) {
|
||||||
Map<String, Object> configMap = blends.get(configId);
|
Map<String, Object> configMap = blends.get(configId);
|
||||||
Object supplierObj = configMap.get(Constant.SUPPLIER_KEY);
|
Object supplierObj = configMap.get(Constant.SUPPLIER_KEY);
|
||||||
String supplier = supplierObj == null ? "" : String.valueOf(supplierObj);
|
String supplier = supplierObj == null ? "" : String.valueOf(supplierObj);
|
||||||
supplier = StrUtil.isEmpty(supplier) ? configId : supplier;
|
supplier = StrUtil.isEmpty(supplier) ? configId : supplier;
|
||||||
BaseProviderFactory<SmsBlend, SupplierConfig> providerFactory = (BaseProviderFactory<SmsBlend, SupplierConfig>) ProviderFactoryHolder.requireForSupplier(supplier);
|
BaseProviderFactory<SmsBlend, SupplierConfig> providerFactory = (BaseProviderFactory<SmsBlend, SupplierConfig>) ProviderFactoryHolder.requireForSupplier(supplier);
|
||||||
if(providerFactory == null) {
|
if (providerFactory == null) {
|
||||||
log.warn("创建\"{}\"的短信服务失败,未找到服务商为\"{}\"的服务", configId, supplier);
|
log.warn("创建\"{}\"的短信服务失败,未找到服务商为\"{}\"的服务", configId, supplier);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -168,13 +175,9 @@ public class SEInitializer {
|
|||||||
SmsUtils.replaceKeysSeperator(configMap, "-", "_");
|
SmsUtils.replaceKeysSeperator(configMap, "-", "_");
|
||||||
JSONObject configJson = new JSONObject(configMap);
|
JSONObject configJson = new JSONObject(configMap);
|
||||||
SupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass());
|
SupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass());
|
||||||
if(Boolean.TRUE.equals(smsConfig.getRestricted())) {
|
|
||||||
SmsFactory.createRestrictedSmsBlend(supplierConfig);
|
|
||||||
} else {
|
|
||||||
SmsFactory.createSmsBlend(supplierConfig);
|
SmsFactory.createSmsBlend(supplierConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册默认工厂实例
|
* 注册默认工厂实例
|
||||||
@ -191,7 +194,7 @@ public class SEInitializer {
|
|||||||
ProviderFactoryHolder.registerFactory(YunPianFactory.instance());
|
ProviderFactoryHolder.registerFactory(YunPianFactory.instance());
|
||||||
ProviderFactoryHolder.registerFactory(ZhutongFactory.instance());
|
ProviderFactoryHolder.registerFactory(ZhutongFactory.instance());
|
||||||
ProviderFactoryHolder.registerFactory(LianLuFactory.instance());
|
ProviderFactoryHolder.registerFactory(LianLuFactory.instance());
|
||||||
if(SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) {
|
if (SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) {
|
||||||
ProviderFactoryHolder.registerFactory(JdCloudFactory.instance());
|
ProviderFactoryHolder.registerFactory(JdCloudFactory.instance());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -78,4 +78,9 @@ public abstract class BaseConfig implements SupplierConfig {
|
|||||||
}
|
}
|
||||||
this.maxRetries = maxRetries;
|
this.maxRetries = maxRetries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最大发送数量,默认integer上限
|
||||||
|
*/
|
||||||
|
private int maximum = Integer.MAX_VALUE;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,8 @@ package org.dromara.sms4j.provider.config;
|
|||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.dromara.sms4j.comm.enumerate.ConfigType;
|
import org.dromara.sms4j.comm.enumerate.ConfigType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class SmsConfig {
|
public class SmsConfig {
|
||||||
|
|
||||||
@ -53,4 +55,9 @@ public class SmsConfig {
|
|||||||
/** 是否打印http log*/
|
/** 是否打印http log*/
|
||||||
private Boolean HttpLog = false;
|
private Boolean HttpLog = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 黑名单配置
|
||||||
|
*/
|
||||||
|
private ArrayList<String> blackList = new ArrayList<>();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -10,8 +10,10 @@ import org.dromara.sms4j.api.universal.SupplierConfig;
|
|||||||
import org.dromara.sms4j.cloopen.config.CloopenFactory;
|
import org.dromara.sms4j.cloopen.config.CloopenFactory;
|
||||||
import org.dromara.sms4j.comm.constant.Constant;
|
import org.dromara.sms4j.comm.constant.Constant;
|
||||||
import org.dromara.sms4j.comm.utils.SmsUtils;
|
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.factory.SmsFactory;
|
||||||
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
|
import org.dromara.sms4j.core.proxy.processor.*;
|
||||||
|
import org.dromara.sms4j.core.proxy.SmsProxyFactory;
|
||||||
import org.dromara.sms4j.ctyun.config.CtyunFactory;
|
import org.dromara.sms4j.ctyun.config.CtyunFactory;
|
||||||
import org.dromara.sms4j.emay.config.EmayFactory;
|
import org.dromara.sms4j.emay.config.EmayFactory;
|
||||||
import org.dromara.sms4j.huawei.config.HuaweiFactory;
|
import org.dromara.sms4j.huawei.config.HuaweiFactory;
|
||||||
@ -21,7 +23,7 @@ import org.dromara.sms4j.netease.config.NeteaseFactory;
|
|||||||
import org.dromara.sms4j.provider.config.SmsConfig;
|
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||||
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
|
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
|
||||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
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.tencent.config.TencentFactory;
|
||||||
import org.dromara.sms4j.unisms.config.UniFactory;
|
import org.dromara.sms4j.unisms.config.UniFactory;
|
||||||
import org.dromara.sms4j.yunpian.config.YunPianFactory;
|
import org.dromara.sms4j.yunpian.config.YunPianFactory;
|
||||||
@ -56,6 +58,16 @@ public class SmsBlendsInitializer {
|
|||||||
this.registerDefaultFactory();
|
this.registerDefaultFactory();
|
||||||
// 注册短信对象工厂
|
// 注册短信对象工厂
|
||||||
ProviderFactoryHolder.registerFactory(factoryList);
|
ProviderFactoryHolder.registerFactory(factoryList);
|
||||||
|
//持有初始化配置信息
|
||||||
|
EnvirmentHolder.frozenEnvirmet(smsConfig, blends);
|
||||||
|
//框架依赖持有缓存扩展
|
||||||
|
new SolonSmsDaoHolder(context);
|
||||||
|
//注册执行器实现
|
||||||
|
SmsProxyFactory.addProcessor(new RestrictedProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new BlackListProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new BlackListRecordingProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor());
|
||||||
// 解析供应商配置
|
// 解析供应商配置
|
||||||
for(String configId : blends.keySet()) {
|
for(String configId : blends.keySet()) {
|
||||||
Map<String, Object> configMap = blends.get(configId);
|
Map<String, Object> configMap = blends.get(configId);
|
||||||
@ -71,15 +83,9 @@ public class SmsBlendsInitializer {
|
|||||||
SmsUtils.replaceKeysSeperator(configMap, "-", "_");
|
SmsUtils.replaceKeysSeperator(configMap, "-", "_");
|
||||||
JSONObject configJson = new JSONObject(configMap);
|
JSONObject configJson = new JSONObject(configMap);
|
||||||
SupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass());
|
SupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass());
|
||||||
if(Boolean.TRUE.equals(smsConfig.getRestricted())) {
|
|
||||||
SmsFactory.createRestrictedSmsBlend(supplierConfig);
|
|
||||||
} else {
|
|
||||||
SmsFactory.createSmsBlend(supplierConfig);
|
SmsFactory.createSmsBlend(supplierConfig);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//注册短信拦截实现
|
|
||||||
SmsInvocationHandler.setRestrictedProcess(new SolonRestrictedProcess(context));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,6 +1,8 @@
|
|||||||
sms:
|
sms:
|
||||||
# 标注从yml读取配置
|
# 标注从yml读取配置
|
||||||
config-type: yaml
|
config-type: yaml
|
||||||
|
# 账户上限
|
||||||
|
account-max: 1
|
||||||
blends:
|
blends:
|
||||||
# 阿里短信例子
|
# 阿里短信例子
|
||||||
ali:
|
ali:
|
||||||
@ -60,6 +62,8 @@ sms:
|
|||||||
template-id: pub_verif_short
|
template-id: pub_verif_short
|
||||||
# 模版名称
|
# 模版名称
|
||||||
templateName: code
|
templateName: code
|
||||||
|
# 渠道上限
|
||||||
|
maximum: 2
|
||||||
lianlu:
|
lianlu:
|
||||||
supplier: lianlu
|
supplier: lianlu
|
||||||
templateId: 模板id
|
templateId: 模板id
|
||||||
|
|||||||
@ -0,0 +1,138 @@
|
|||||||
|
package org.dromara.sms4j.example;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Assert;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.sms4j.api.SmsBlend;
|
||||||
|
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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() {
|
||||||
|
SmsBlend smsBlend = SmsFactory.getBySupplier(SupplierConstant.UNISMS);
|
||||||
|
//单黑名单添加
|
||||||
|
smsBlend.joinInBlacklist(PHONE);
|
||||||
|
SmsBlendException knowEx = null;
|
||||||
|
try {
|
||||||
|
smsBlend.sendMessage(PHONE, SmsUtils.getRandomInt(6));
|
||||||
|
} catch (SmsBlendException e) {
|
||||||
|
knowEx = e;
|
||||||
|
}
|
||||||
|
Assert.notNull(knowEx);
|
||||||
|
//单黑名单移除
|
||||||
|
smsBlend.removeFromBlacklist(PHONE);
|
||||||
|
SmsResponse smsResponse = smsBlend.sendMessage(PHONE, SmsUtils.getRandomInt(6));
|
||||||
|
Assert.isTrue(smsResponse.isSuccess());
|
||||||
|
//批量黑名单添加
|
||||||
|
smsBlend.batchJoinBlacklist(Collections.singletonList(PHONE));
|
||||||
|
knowEx = null;
|
||||||
|
try {
|
||||||
|
smsBlend.sendMessage(PHONE, SmsUtils.getRandomInt(6));
|
||||||
|
} catch (SmsBlendException e) {
|
||||||
|
knowEx = e;
|
||||||
|
}
|
||||||
|
Assert.notNull(knowEx);
|
||||||
|
//批量黑名单添加
|
||||||
|
smsBlend.removeFromBlacklist(PHONE);
|
||||||
|
smsResponse = smsBlend.sendMessage(PHONE, SmsUtils.getRandomInt(6));
|
||||||
|
Assert.isTrue(smsResponse.isSuccess());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//账号级上限测试、需成功发送一笔,特殊配置
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,8 +11,10 @@ import org.dromara.sms4j.cloopen.config.CloopenFactory;
|
|||||||
import org.dromara.sms4j.comm.constant.Constant;
|
import org.dromara.sms4j.comm.constant.Constant;
|
||||||
import org.dromara.sms4j.comm.enumerate.ConfigType;
|
import org.dromara.sms4j.comm.enumerate.ConfigType;
|
||||||
import org.dromara.sms4j.comm.utils.SmsUtils;
|
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.factory.SmsFactory;
|
||||||
import org.dromara.sms4j.core.proxy.SmsInvocationHandler;
|
import org.dromara.sms4j.core.proxy.processor.*;
|
||||||
|
import org.dromara.sms4j.core.proxy.SmsProxyFactory;
|
||||||
import org.dromara.sms4j.ctyun.config.CtyunFactory;
|
import org.dromara.sms4j.ctyun.config.CtyunFactory;
|
||||||
import org.dromara.sms4j.emay.config.EmayFactory;
|
import org.dromara.sms4j.emay.config.EmayFactory;
|
||||||
import org.dromara.sms4j.huawei.config.HuaweiFactory;
|
import org.dromara.sms4j.huawei.config.HuaweiFactory;
|
||||||
@ -22,7 +24,6 @@ import org.dromara.sms4j.netease.config.NeteaseFactory;
|
|||||||
import org.dromara.sms4j.provider.config.SmsConfig;
|
import org.dromara.sms4j.provider.config.SmsConfig;
|
||||||
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
|
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
|
||||||
import org.dromara.sms4j.provider.factory.ProviderFactoryHolder;
|
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.tencent.config.TencentFactory;
|
||||||
import org.dromara.sms4j.unisms.config.UniFactory;
|
import org.dromara.sms4j.unisms.config.UniFactory;
|
||||||
import org.dromara.sms4j.yunpian.config.YunPianFactory;
|
import org.dromara.sms4j.yunpian.config.YunPianFactory;
|
||||||
@ -53,7 +54,16 @@ public class SmsBlendsInitializer {
|
|||||||
this.registerDefaultFactory();
|
this.registerDefaultFactory();
|
||||||
// 注册短信对象工厂
|
// 注册短信对象工厂
|
||||||
ProviderFactoryHolder.registerFactory(factoryList);
|
ProviderFactoryHolder.registerFactory(factoryList);
|
||||||
|
|
||||||
if(ConfigType.YAML.equals(this.smsConfig.getConfigType())) {
|
if(ConfigType.YAML.equals(this.smsConfig.getConfigType())) {
|
||||||
|
//持有初始化配置信息
|
||||||
|
EnvirmentHolder.frozenEnvirmet(smsConfig, blends);
|
||||||
|
//注册执行器实现
|
||||||
|
SmsProxyFactory.addProcessor(new RestrictedProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new BlackListProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new BlackListRecordingProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor());
|
||||||
|
SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor());
|
||||||
// 解析供应商配置
|
// 解析供应商配置
|
||||||
for(String configId : blends.keySet()) {
|
for(String configId : blends.keySet()) {
|
||||||
Map<String, Object> configMap = blends.get(configId);
|
Map<String, Object> configMap = blends.get(configId);
|
||||||
@ -69,16 +79,11 @@ public class SmsBlendsInitializer {
|
|||||||
SmsUtils.replaceKeysSeperator(configMap, "-", "_");
|
SmsUtils.replaceKeysSeperator(configMap, "-", "_");
|
||||||
JSONObject configJson = new JSONObject(configMap);
|
JSONObject configJson = new JSONObject(configMap);
|
||||||
org.dromara.sms4j.api.universal.SupplierConfig supplierConfig = JSONUtil.toBean(configJson, providerFactory.getConfigClass());
|
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);
|
SmsFactory.createSmsBlend(supplierConfig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//注册短信拦截实现
|
|
||||||
SmsInvocationHandler.setRestrictedProcess(new SpringRestrictedProcess());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user