From 436a41508f5876c6fb73c46a5ac6ac18a441ce04 Mon Sep 17 00:00:00 2001 From: Sh1yu <41428433@qq.com> Date: Fri, 27 Oct 2023 15:43:35 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E7=BB=8F=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sms4j/api/proxy/CoreMethodProcessor.java | 42 ++++++ .../org/dromara/sms4j/api/proxy/Order.java | 12 ++ .../sms4j/api/proxy/RestrictedProcess.java | 17 --- .../dromara/sms4j/api/proxy/SmsProcessor.java | 24 ++++ .../dromara/sms4j/api/proxy/SuppotFilter.java | 12 ++ .../api/proxy/aware/SmsBlendConfigAware.java | 13 ++ .../sms4j/api/proxy/aware/SmsConfigAware.java | 12 ++ .../sms4j/api/proxy/aware/SmsDaoAware.java | 12 ++ .../sms4j/core/factory/SmsFactory.java | 25 ++-- .../sms4j/core/proxy/EnvirmentHolder.java | 34 +++++ .../proxy/RestrictedProcessDefaultImpl.java | 58 --------- .../core/proxy/SmsInvocationHandler.java | 70 ++++++---- .../sms4j/core/proxy/SmsProxyFactory.java | 108 ++++++++++++++++ .../sms4j/javase/config/SEInitializer.java | 53 ++++---- .../sms4j/provider/config/BaseConfig.java | 5 + .../sms4j/provider/config/SmsConfig.java | 7 + .../solon/aop/SolonRestrictedProcess.java | 61 --------- .../solon/config/SmsBlendsInitializer.java | 26 ++-- .../sms4j/solon/holder/SolonSmsDaoHolder.java | 22 ++++ .../src/main/resources/application.yml | 7 + .../sms4j/example/SmsProcessorTest.java | 120 ++++++++++++++++++ .../starter/aop/SpringRestrictedProcess.java | 55 -------- .../starter/config/SmsBlendsInitializer.java | 25 ++-- .../starter/holder/SpringSmsDaoHolder.java | 12 ++ 24 files changed, 564 insertions(+), 268 deletions(-) create mode 100644 sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/CoreMethodProcessor.java create mode 100644 sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/Order.java delete mode 100644 sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/RestrictedProcess.java create mode 100644 sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SmsProcessor.java create mode 100644 sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SuppotFilter.java create mode 100644 sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsBlendConfigAware.java create mode 100644 sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsConfigAware.java create mode 100644 sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsDaoAware.java create mode 100644 sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/EnvirmentHolder.java delete mode 100644 sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/RestrictedProcessDefaultImpl.java create mode 100644 sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsProxyFactory.java delete mode 100644 sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/aop/SolonRestrictedProcess.java create mode 100644 sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/holder/SolonSmsDaoHolder.java create mode 100644 sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsProcessorTest.java delete mode 100644 sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/aop/SpringRestrictedProcess.java create mode 100644 sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/holder/SpringSmsDaoHolder.java diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/CoreMethodProcessor.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/CoreMethodProcessor.java new file mode 100644 index 00000000..6a71cd41 --- /dev/null +++ b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/CoreMethodProcessor.java @@ -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)param[2]); + return param; + } + } + if ("massTexting".equals(method.getName())) { + if (2 == parameterCount) { + massTextingPreProcess((List)param[0],(String)param[1]); + return param; + } + if (3 == parameterCount) { + massTextingByTemplatePreProcess((List)param[0],(String)param[1],(LinkedHashMap)param[2]); + return param; + } + } + return param; + } + void sendMessagePreProcess(String phone, String message); + void sendMessageByTemplatePreProcess(String phone, String templateId, LinkedHashMap messages); + void massTextingPreProcess(List phones, String message); + void massTextingByTemplatePreProcess(List phones, String templateId, LinkedHashMap messages); +} diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/Order.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/Order.java new file mode 100644 index 00000000..c6b49e91 --- /dev/null +++ b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/Order.java @@ -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; + } +} diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/RestrictedProcess.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/RestrictedProcess.java deleted file mode 100644 index 1d0457d9..00000000 --- a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/RestrictedProcess.java +++ /dev/null @@ -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); - -} diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SmsProcessor.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SmsProcessor.java new file mode 100644 index 00000000..2b584699 --- /dev/null +++ b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SmsProcessor.java @@ -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; + } +} diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SuppotFilter.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SuppotFilter.java new file mode 100644 index 00000000..58453afa --- /dev/null +++ b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SuppotFilter.java @@ -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 getSupports(); +} diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsBlendConfigAware.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsBlendConfigAware.java new file mode 100644 index 00000000..13e01430 --- /dev/null +++ b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsBlendConfigAware.java @@ -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> blends); +} diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsConfigAware.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsConfigAware.java new file mode 100644 index 00000000..34b6b38a --- /dev/null +++ b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsConfigAware.java @@ -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); +} diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsDaoAware.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsDaoAware.java new file mode 100644 index 00000000..f17a6297 --- /dev/null +++ b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/aware/SmsDaoAware.java @@ -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); +} diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/factory/SmsFactory.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/factory/SmsFactory.java index 478c5715..f9e1bf12 100644 --- a/sms4j-core/src/main/java/org/dromara/sms4j/core/factory/SmsFactory.java +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/factory/SmsFactory.java @@ -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 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 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 *

构建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); } /** diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/EnvirmentHolder.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/EnvirmentHolder.java new file mode 100644 index 00000000..57a19d1d --- /dev/null +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/EnvirmentHolder.java @@ -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> blends = null; + + public static void frozenEnvirmet(SmsConfig smsConfig, Map> blends) { + if (null!=EnvirmentHolder.smsConfig||null!=EnvirmentHolder.blends){ + return; + } + EnvirmentHolder.smsConfig = smsConfig; + EnvirmentHolder.blends = blends; + } + + //只有核心包执行器部分才能获取 + static SmsConfig getSmsConfig() { + return smsConfig; + } + + //只有核心包执行器部分才能获取 + static Map> getBlends() { + return blends; + } +} diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/RestrictedProcessDefaultImpl.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/RestrictedProcessDefaultImpl.java deleted file mode 100644 index 46d40ad2..00000000 --- a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/RestrictedProcessDefaultImpl.java +++ /dev/null @@ -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; - } -} diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsInvocationHandler.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsInvocationHandler.java index d22760f2..a8b3d86c 100644 --- a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsInvocationHandler.java +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsInvocationHandler.java @@ -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 processors; - private SmsInvocationHandler(SmsBlend smsBlend) { + + public SmsInvocationHandler(SmsBlend smsBlend, LinkedList 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); } - result = method.invoke(smsBlend, 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; } } diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsProxyFactory.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsProxyFactory.java new file mode 100644 index 00000000..5da1a4ba --- /dev/null +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsProxyFactory.java @@ -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 processors = new LinkedList<>(); + + public static SmsBlend getProxySmsBlend(SmsBlend smsBlend) { + LinkedList 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 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; + } + +} diff --git a/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java b/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java index 49f8f101..2bf03dd1 100644 --- a/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java +++ b/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java @@ -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; @@ -86,7 +90,7 @@ public class SEInitializer { /** * 从配置bean对象中加载配置 * - * @param smsConfig 短信公共配置 + * @param smsConfig 短信公共配置 * @param configList 各短信服务商配置列表 */ public void fromConfig(SmsConfig smsConfig, List configList) { @@ -95,20 +99,17 @@ public class SEInitializer { // 初始化SmsConfig整体配置文件 BeanUtil.copyProperties(smsConfig, BeanFactory.getSmsConfig()); // 创建短信服务对象 - if(CollUtil.isEmpty(configList)) { - return ; + if (CollUtil.isEmpty(configList)) { + return; } - for(SupplierConfig supplierConfig : configList) { - if(Boolean.TRUE.equals(smsConfig.getRestricted())) { - SmsFactory.createRestrictedSmsBlend(supplierConfig); - } else { - SmsFactory.createSmsBlend(supplierConfig); - } + for (SupplierConfig supplierConfig : configList) { + SmsFactory.createSmsBlend(supplierConfig); } } /** * 注册服务商工厂 + * * @param factory 服务商工厂 */ public SEInitializer registerFactory(BaseProviderFactory factory) { @@ -118,15 +119,13 @@ public class SEInitializer { /** * 注册DAO实例 + * * @param smsDao DAO实例 */ public SEInitializer registerSmsDao(SmsDao smsDao) { - if(smsDao == null) { + if (smsDao == null) { throw new SmsBlendException("注册DAO实例失败,实例不能为空"); } - RestrictedProcessDefaultImpl process = new RestrictedProcessDefaultImpl(); - process.setSmsDao(smsDao); - SmsInvocationHandler.setRestrictedProcess(process); this.smsDao = smsDao; return this; } @@ -143,7 +142,7 @@ public class SEInitializer { } //注册默认DAO实例 - if(this.smsDao == null) { + if (this.smsDao == null) { this.registerSmsDao(SmsDaoDefaultImpl.getInstance()); } @@ -152,15 +151,25 @@ public class SEInitializer { //初始化SmsConfig整体配置文件 BeanUtil.copyProperties(smsConfig, BeanFactory.getSmsConfig()); + // 解析服务商配置 Map> blends = smsConfig.getBlends(); - for(String configId : blends.keySet()) { + + //持有初始化配置信息 + 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 configMap = blends.get(configId); Object supplierObj = configMap.get(Constant.SUPPLIER_KEY); String supplier = supplierObj == null ? "" : String.valueOf(supplierObj); supplier = StrUtil.isEmpty(supplier) ? configId : supplier; BaseProviderFactory providerFactory = (BaseProviderFactory) ProviderFactoryHolder.requireForSupplier(supplier); - if(providerFactory == null) { + if (providerFactory == null) { log.warn("创建\"{}\"的短信服务失败,未找到服务商为\"{}\"的服务", configId, supplier); continue; } @@ -168,11 +177,7 @@ 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); - } + SmsFactory.createSmsBlend(supplierConfig); } } @@ -191,7 +196,7 @@ public class SEInitializer { ProviderFactoryHolder.registerFactory(YunPianFactory.instance()); ProviderFactoryHolder.registerFactory(ZhutongFactory.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()); } } diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/provider/config/BaseConfig.java b/sms4j-provider/src/main/java/org/dromara/sms4j/provider/config/BaseConfig.java index f88acbfc..9311eea6 100644 --- a/sms4j-provider/src/main/java/org/dromara/sms4j/provider/config/BaseConfig.java +++ b/sms4j-provider/src/main/java/org/dromara/sms4j/provider/config/BaseConfig.java @@ -73,4 +73,9 @@ public abstract class BaseConfig implements SupplierConfig { } this.maxRetries = maxRetries; } + + /** + * 最大发送数量,默认integer上限 + */ + private int maximum = Integer.MAX_VALUE; } diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/provider/config/SmsConfig.java b/sms4j-provider/src/main/java/org/dromara/sms4j/provider/config/SmsConfig.java index 7c3d05c0..d20b8171 100644 --- a/sms4j-provider/src/main/java/org/dromara/sms4j/provider/config/SmsConfig.java +++ b/sms4j-provider/src/main/java/org/dromara/sms4j/provider/config/SmsConfig.java @@ -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 blackList = new ArrayList<>(); + } diff --git a/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/aop/SolonRestrictedProcess.java b/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/aop/SolonRestrictedProcess.java deleted file mode 100644 index 29aca011..00000000 --- a/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/aop/SolonRestrictedProcess.java +++ /dev/null @@ -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; - } -} diff --git a/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java b/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java index 8224ce15..b860fce5 100644 --- a/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java +++ b/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java @@ -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 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); - } + SmsFactory.createSmsBlend(supplierConfig); } - //注册短信拦截实现 - SmsInvocationHandler.setRestrictedProcess(new SolonRestrictedProcess(context)); } /** diff --git a/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/holder/SolonSmsDaoHolder.java b/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/holder/SolonSmsDaoHolder.java new file mode 100644 index 00000000..cef3c875 --- /dev/null +++ b/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/holder/SolonSmsDaoHolder.java @@ -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; + } +} diff --git a/sms4j-spring-boot-example/src/main/resources/application.yml b/sms4j-spring-boot-example/src/main/resources/application.yml index e16e3ce6..93d33b91 100644 --- a/sms4j-spring-boot-example/src/main/resources/application.yml +++ b/sms4j-spring-boot-example/src/main/resources/application.yml @@ -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 diff --git a/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsProcessorTest.java b/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsProcessorTest.java new file mode 100644 index 00000000..b858d5d8 --- /dev/null +++ b/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsProcessorTest.java @@ -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(), "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); + } + +} diff --git a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/aop/SpringRestrictedProcess.java b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/aop/SpringRestrictedProcess.java deleted file mode 100644 index 387a0f92..00000000 --- a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/aop/SpringRestrictedProcess.java +++ /dev/null @@ -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; - } -} diff --git a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java index bccca3ab..ab03ed19 100644 --- a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java +++ b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java @@ -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 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); - } + SmsFactory.createSmsBlend(supplierConfig); } } - //注册短信拦截实现 - SmsInvocationHandler.setRestrictedProcess(new SpringRestrictedProcess()); + } /** diff --git a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/holder/SpringSmsDaoHolder.java b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/holder/SpringSmsDaoHolder.java new file mode 100644 index 00000000..b149f0f3 --- /dev/null +++ b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/holder/SpringSmsDaoHolder.java @@ -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); + } +} From bd35558532dbcd9b42a66662612882360d9453ea Mon Sep 17 00:00:00 2001 From: Sh1yu <41428433@qq.com> Date: Mon, 30 Oct 2023 17:02:04 +0800 Subject: [PATCH 2/3] =?UTF-8?q?BlackListProcessor=20=E9=BB=91=E5=90=8D?= =?UTF-8?q?=E5=8D=95=E6=8B=A6=E6=88=AA=20CoreMethodParamValidateProcessor?= =?UTF-8?q?=20=20=E6=A0=B8=E5=BF=83=E6=96=B9=E6=B3=95=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=E6=8B=A6=E6=88=AA=E5=99=A8=20RestrictedProce?= =?UTF-8?q?ssor=20=20=E8=B4=A6=E5=8F=B7=E4=B8=8A=E9=99=90=E6=8B=A6?= =?UTF-8?q?=E6=88=AA=E5=99=A8=20SingleBlendRestrictedProcessor=20=20?= =?UTF-8?q?=E5=8E=82=E5=95=86=E4=B8=8A=E9=99=90=E6=8B=A6=E6=88=AA=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../proxy/processor/BlackListProcessor.java | 58 +++++++++++ .../CoreMethodParamValidateProcessor.java | 76 +++++++++++++++ .../proxy/processor/RestrictedProcessor.java | 96 +++++++++++++++++++ .../SingleBlendRestrictedProcessor.java | 66 +++++++++++++ 4 files changed, 296 insertions(+) create mode 100644 sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListProcessor.java create mode 100644 sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/CoreMethodParamValidateProcessor.java create mode 100644 sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/RestrictedProcessor.java create mode 100644 sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/SingleBlendRestrictedProcessor.java diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListProcessor.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListProcessor.java new file mode 100644 index 00000000..06c5abd6 --- /dev/null +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListProcessor.java @@ -0,0 +1,58 @@ +package org.dromara.sms4j.core.proxy.processor; + +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.dromara.sms4j.api.proxy.CoreMethodProcessor; +import org.dromara.sms4j.api.proxy.aware.SmsConfigAware; +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, SmsConfigAware { + @Setter + Object smsConfig; + + @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 messages) { + doRestricted(Collections.singletonList(phone)); + } + + @Override + public void massTextingPreProcess(List phones, String message) { + doRestricted(phones); + } + + @Override + public void massTextingByTemplatePreProcess(List phones, String templateId, LinkedHashMap messages) { + doRestricted(phones); + } + + public void doRestricted(List phones) { + ArrayList blackList = ((SmsConfig)smsConfig).getBlackList(); + for (String phone : phones) { + if (blackList.stream().filter(black -> black.replace("-","").equals(phone)).findAny().isPresent()) { + throw new SmsBlendException("The phone:", phone + " hit blacklist!"); + } + } + + } + +} diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/CoreMethodParamValidateProcessor.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/CoreMethodParamValidateProcessor.java new file mode 100644 index 00000000..c15abcd1 --- /dev/null +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/CoreMethodParamValidateProcessor.java @@ -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 messages) { + validatePhone(phone); + validateMessages(templateId, messages); + } + + @Override + public void massTextingPreProcess(List phones, String message) { + validateMessage(message); + validatePhones(phones); + } + + @Override + public void massTextingByTemplatePreProcess(List phones, String templateId, LinkedHashMap 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 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 messages) { + if (null != templateId && !"".equals(templateId) && (messages == null || messages.size() < 1)) { + throw new SmsBlendException("cant use template without template param"); + } + } +} diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/RestrictedProcessor.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/RestrictedProcessor.java new file mode 100644 index 00000000..609b7194 --- /dev/null +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/RestrictedProcessor.java @@ -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 messages) { + doRestricted(Collections.singletonList(phone)); + } + + @Override + public void massTextingPreProcess(List phones, String message) { + doRestricted(phones); + } + + @Override + public void massTextingByTemplatePreProcess(List phones, String templateId, LinkedHashMap messages) { + doRestricted(phones); + } + + public void doRestricted(List 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); + } + } + } + } +} diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/SingleBlendRestrictedProcessor.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/SingleBlendRestrictedProcessor.java new file mode 100644 index 00000000..3e93ecab --- /dev/null +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/SingleBlendRestrictedProcessor.java @@ -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; + } +} From 3d5644211539bc00d6b781d76ffc3293fe29adb5 Mon Sep 17 00:00:00 2001 From: Sh1yu <41428433@qq.com> Date: Wed, 1 Nov 2023 14:34:48 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E9=BB=91=E5=90=8D=E5=8D=95=E7=9A=84?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E4=BF=AE=E6=94=B9=EF=BC=8C=E9=AD=94=E6=B3=95?= =?UTF-8?q?=E5=80=BC=E4=BF=AE=E6=94=B9=EF=BC=9B=E5=90=8C=E6=97=B6=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=89=80=E4=BD=BF=E7=94=A8=E7=9A=84sms4j=E7=9A=84?= =?UTF-8?q?=E5=94=AF=E4=B8=80=E6=8E=A5=E5=8F=A3=E5=BA=94=E8=AF=A5=E5=B0=B1?= =?UTF-8?q?=E6=98=AFSmsblend,=E6=89=80=E4=BB=A5=E9=BB=91=E5=90=8D=E5=8D=95?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E7=9A=84=E6=96=B9=E6=B3=95=E5=BB=BA=E7=AB=8B?= =?UTF-8?q?=E5=9C=A8Smsblend=E4=B8=8A=EF=BC=8C=E4=BD=86=E6=98=AF=E5=9B=A0?= =?UTF-8?q?=E4=B8=BA=E6=B6=89=E5=8F=8A=E5=85=A8=E5=B1=80=E5=92=8C=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E7=AD=89=EF=BC=8C=E9=9C=80=E8=A6=81=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=EF=BC=8C=E5=A2=9E=E5=8A=A0BlackListRecording?= =?UTF-8?q?Processor.java?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/org/dromara/sms4j/api/SmsBlend.java | 43 ++++++++++ .../sms4j/api/proxy/CoreMethodProcessor.java | 10 ++- .../dromara/sms4j/api/proxy/SmsProcessor.java | 2 +- .../comm/constant/NumberOfParasmeters.java | 35 ++++++++ .../core/proxy/SmsInvocationHandler.java | 6 +- .../proxy/processor/BlackListProcessor.java | 12 +-- .../BlackListRecordingProcessor.java | 84 +++++++++++++++++++ .../sms4j/javase/config/SEInitializer.java | 6 +- .../solon/config/SmsBlendsInitializer.java | 6 +- .../src/main/resources/application.yml | 3 - .../sms4j/example/SmsProcessorTest.java | 28 +++++-- .../starter/config/SmsBlendsInitializer.java | 6 +- 12 files changed, 207 insertions(+), 34 deletions(-) create mode 100644 sms4j-comm/src/main/java/org/dromara/sms4j/comm/constant/NumberOfParasmeters.java create mode 100644 sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListRecordingProcessor.java diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/SmsBlend.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/SmsBlend.java index fd341541..880d867d 100644 --- a/sms4j-api/src/main/java/org/dromara/sms4j/api/SmsBlend.java +++ b/sms4j-api/src/main/java/org/dromara/sms4j/api/SmsBlend.java @@ -3,12 +3,14 @@ package org.dromara.sms4j.api; import org.dromara.sms4j.api.callback.CallBack; import org.dromara.sms4j.api.entity.SmsResponse; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; /** * SmsBlend *

通用接口,定义国内短信方法 + * * @author :Wind * 2023/5/16 16:03 **/ @@ -16,12 +18,14 @@ public interface SmsBlend { /** * 获取短信实例唯一标识 + * * @return */ String getConfigId(); /** * 获取供应商标识 + * * @return */ String getSupplier(); @@ -151,4 +155,43 @@ public interface SmsBlend { */ void delayMassTexting(List phones, String templateId, LinkedHashMap messages, Long delayedTime); + /** + *

说明:加入黑名单【这个需要有全局操作的同时需要操作缓存,那么不给smsblend实际处理,代理部分处理】 + * joinInBlacklist + * + * @param phone 需要加入黑名单的手机号 + * @author :sh1yu + */ + default void joinInBlacklist(String phone) { + } + + /** + *

说明:从黑名单移除【为了sms4j组件有统一入口,同时这个需要有全局操作的同时需要操作缓存,那么不给smsblend实际处理,代理部分处理】 + * removeFromBlacklist + * + * @param phone 需要加入黑名单的手机号 + * @author :sh1yu + */ + default void removeFromBlacklist(String phone) { + } + + /** + *

说明:批量加入黑名单【为了sms4j组件有统一入口,同时这个需要有全局操作的同时需要操作缓存,那么不给smsblend实际处理,代理部分处理】 + * batchJoinBlacklist + * + * @param phones 需要加入黑名单的手机号数组 + * @author :sh1yu + */ + default void batchJoinBlacklist(List phones) { + } + + /** + *

说明:批量从黑名单移除【为了sms4j组件有统一入口,同时这个需要有全局操作的同时需要操作缓存,那么不给smsblend实际处理,代理部分处理】 + * batchRemovalFromBlacklist + * + * @param phones 需要移除黑名单的手机号数组 + * @author :sh1yu + */ + default void batchRemovalFromBlacklist(List phones) { + } } diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/CoreMethodProcessor.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/CoreMethodProcessor.java index 6a71cd41..effa18fe 100644 --- a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/CoreMethodProcessor.java +++ b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/CoreMethodProcessor.java @@ -1,5 +1,7 @@ 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; @@ -14,21 +16,21 @@ public interface CoreMethodProcessor extends SmsProcessor { String name = method.getName(); int parameterCount = method.getParameterCount(); if ("sendMessage".equals(method.getName())) { - if (2 == parameterCount) { + if (NumberOfParasmeters.TWO == NumberOfParasmeters.getNumberOfParasmetersEnum(parameterCount)) { sendMessagePreProcess((String) param[0],(String) param[1]); return param; } - if (3 == parameterCount) { + if (NumberOfParasmeters.THREE == NumberOfParasmeters.getNumberOfParasmetersEnum(parameterCount)) { sendMessageByTemplatePreProcess((String)param[0],(String) param[1],(LinkedHashMap)param[2]); return param; } } if ("massTexting".equals(method.getName())) { - if (2 == parameterCount) { + if (NumberOfParasmeters.TWO == NumberOfParasmeters.getNumberOfParasmetersEnum(parameterCount)) { massTextingPreProcess((List)param[0],(String)param[1]); return param; } - if (3 == parameterCount) { + if (NumberOfParasmeters.THREE == NumberOfParasmeters.getNumberOfParasmetersEnum(parameterCount)) { massTextingByTemplatePreProcess((List)param[0],(String)param[1],(LinkedHashMap)param[2]); return param; } diff --git a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SmsProcessor.java b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SmsProcessor.java index 2b584699..8f506597 100644 --- a/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SmsProcessor.java +++ b/sms4j-api/src/main/java/org/dromara/sms4j/api/proxy/SmsProcessor.java @@ -18,7 +18,7 @@ public interface SmsProcessor extends Order { return null; } - default Object exceptionHandleProcessor(Method method, Object source, Object[] param) throws InvocationTargetException, IllegalAccessException { + default Object exceptionHandleProcessor(Method method, Object source, Object[] param,Exception exception) throws InvocationTargetException, IllegalAccessException { return null; } } diff --git a/sms4j-comm/src/main/java/org/dromara/sms4j/comm/constant/NumberOfParasmeters.java b/sms4j-comm/src/main/java/org/dromara/sms4j/comm/constant/NumberOfParasmeters.java new file mode 100644 index 00000000..fa5def49 --- /dev/null +++ b/sms4j-comm/src/main/java/org/dromara/sms4j/comm/constant/NumberOfParasmeters.java @@ -0,0 +1,35 @@ +package org.dromara.sms4j.comm.constant; + + +/** + * NumberOfParasmeters + *

重载方法的参数个数 + * + * @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"); + } +} diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsInvocationHandler.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsInvocationHandler.java index a8b3d86c..493d5893 100644 --- a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsInvocationHandler.java +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/SmsInvocationHandler.java @@ -38,7 +38,7 @@ public class SmsInvocationHandler implements InvocationHandler { result = method.invoke(smsBlend, objects); } catch (Exception e) { //错误执行器 - doErrorHandleProcess(smsBlend, method, objects); + doErrorHandleProcess(smsBlend, method, objects,e); } //后置执行器 doPostrocess(smsBlend, method, objects, result); @@ -52,9 +52,9 @@ public class SmsInvocationHandler implements InvocationHandler { return objects; } - public void doErrorHandleProcess(Object o, Method method, Object[] objects) throws InvocationTargetException, IllegalAccessException { + public void doErrorHandleProcess(Object o, Method method, Object[] objects,Exception e) throws InvocationTargetException, IllegalAccessException { for (SmsProcessor processor : processors) { - processor.exceptionHandleProcessor(method, o, objects); + processor.exceptionHandleProcessor(method, o, objects,e); } } diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListProcessor.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListProcessor.java index 06c5abd6..b349704a 100644 --- a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListProcessor.java +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListProcessor.java @@ -2,8 +2,10 @@ 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; @@ -16,9 +18,9 @@ import java.util.*; * @since 2023/10/27 13:03 */ @Slf4j -public class BlackListProcessor implements CoreMethodProcessor, SmsConfigAware { +public class BlackListProcessor implements CoreMethodProcessor, SmsDaoAware { @Setter - Object smsConfig; + SmsDao smsDao; @Override public int getOrder() { @@ -46,13 +48,11 @@ public class BlackListProcessor implements CoreMethodProcessor, SmsConfigAware { } public void doRestricted(List phones) { - ArrayList blackList = ((SmsConfig)smsConfig).getBlackList(); + ArrayList blackList = (ArrayList) 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 blacklist!"); + throw new SmsBlendException("The phone:", phone + " hit global blacklist!"); } } - } - } diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListRecordingProcessor.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListRecordingProcessor.java new file mode 100644 index 00000000..eb2a60c6 --- /dev/null +++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/proxy/processor/BlackListRecordingProcessor.java @@ -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 blackList = getBlackList(cacheKey); + blackList.add((String) param[0]); + flushBlackList(cacheKey,blackList); + } + //从黑名单移除 + if (method.getName().equals("removeFromBlacklist")) { + String cacheKey = getCacheKey(); + ArrayList blackList = getBlackList(cacheKey); + blackList.remove((String) param[0]); + flushBlackList(cacheKey,blackList); + } + //批量添加到黑名单 + if (method.getName().equals("batchJoinBlacklist")) { + String cacheKey = getCacheKey(); + ArrayList blackList = getBlackList(cacheKey); + blackList.addAll((List) param[0]); + flushBlackList(cacheKey,blackList); + } + //批量从黑名单移除 + if (method.getName().equals("batchRemovalFromBlacklist")) { + String cacheKey = getCacheKey(); + ArrayList blackList = getBlackList(cacheKey); + blackList.removeAll((List) param[0]); + flushBlackList(cacheKey,blackList); + } + return param; + } + + //构建cachekey + public String getCacheKey(){ + return "sms:blacklist:global"; + } + + //获取黑名单,没有就新建 + public ArrayList getBlackList(String cacheKey) { + ArrayList blackList; + Object cache = smsDao.get(cacheKey); + if (null != cache) { + blackList = (ArrayList) cache; + return blackList; + } + blackList = new ArrayList<>(); + smsDao.set("sms:blacklist:global", blackList); + return blackList; + } + + //让黑名单生效 + public void flushBlackList(String cacheKey ,ArrayList blackList) { + smsDao.set(cacheKey, blackList); + } +} diff --git a/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java b/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java index 2bf03dd1..c8409f50 100644 --- a/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java +++ b/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java @@ -21,11 +21,8 @@ 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.processor.BlackListProcessor; -import org.dromara.sms4j.core.proxy.processor.CoreMethodParamValidateProcessor; -import org.dromara.sms4j.core.proxy.processor.RestrictedProcessor; +import org.dromara.sms4j.core.proxy.processor.*; 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; @@ -161,6 +158,7 @@ public class SEInitializer { //注册执行器实现 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()) { diff --git a/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java b/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java index b860fce5..da47f9af 100644 --- a/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java +++ b/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java @@ -12,11 +12,8 @@ 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.processor.BlackListProcessor; -import org.dromara.sms4j.core.proxy.processor.CoreMethodParamValidateProcessor; -import org.dromara.sms4j.core.proxy.processor.RestrictedProcessor; +import org.dromara.sms4j.core.proxy.processor.*; 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; @@ -68,6 +65,7 @@ public class SmsBlendsInitializer { //注册执行器实现 SmsProxyFactory.addProcessor(new RestrictedProcessor()); SmsProxyFactory.addProcessor(new BlackListProcessor()); + SmsProxyFactory.addProcessor(new BlackListRecordingProcessor()); SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor()); SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor()); // 解析供应商配置 diff --git a/sms4j-spring-boot-example/src/main/resources/application.yml b/sms4j-spring-boot-example/src/main/resources/application.yml index 93d33b91..62604c42 100644 --- a/sms4j-spring-boot-example/src/main/resources/application.yml +++ b/sms4j-spring-boot-example/src/main/resources/application.yml @@ -1,9 +1,6 @@ sms: # 标注从yml读取配置 config-type: yaml - # 黑名单 - black-list: - -13899998888 # 账户上限 account-max: 1 blends: diff --git a/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsProcessorTest.java b/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsProcessorTest.java index b858d5d8..ce7a8a02 100644 --- a/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsProcessorTest.java +++ b/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/SmsProcessorTest.java @@ -1,11 +1,9 @@ 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.SmsBlend; 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; @@ -16,7 +14,6 @@ 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 @@ -79,13 +76,34 @@ public class SmsProcessorTest { //黑名单测试 黑名单手机号13899998888 @Test public void testBlackList() { + SmsBlend smsBlend = SmsFactory.getBySupplier(SupplierConstant.UNISMS); + //单黑名单添加 + smsBlend.joinInBlacklist(PHONE); SmsBlendException knowEx = null; try { - SmsFactory.getBySupplier(SupplierConstant.ALIBABA).sendMessage(PHONE, SmsUtils.getRandomInt(6)); + 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()); + } //账号级上限测试、需成功发送一笔,特殊配置 diff --git a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java index ab03ed19..bfec9afc 100644 --- a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java +++ b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java @@ -13,11 +13,8 @@ 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.processor.BlackListProcessor; -import org.dromara.sms4j.core.proxy.processor.CoreMethodParamValidateProcessor; -import org.dromara.sms4j.core.proxy.processor.RestrictedProcessor; +import org.dromara.sms4j.core.proxy.processor.*; 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; @@ -64,6 +61,7 @@ public class SmsBlendsInitializer { //注册执行器实现 SmsProxyFactory.addProcessor(new RestrictedProcessor()); SmsProxyFactory.addProcessor(new BlackListProcessor()); + SmsProxyFactory.addProcessor(new BlackListRecordingProcessor()); SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor()); SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor()); // 解析供应商配置