!123 重构并优化 SmsProcessor 体系

Merge pull request !123 from Createsequence/refactor/method-interceptor
This commit is contained in:
风如歌 2023-12-21 06:04:03 +00:00 committed by Gitee
commit 4c19e4fbe7
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
33 changed files with 1506 additions and 654 deletions

View File

@ -0,0 +1,471 @@
package org.dromara.sms4j.api.proxy;
import org.dromara.sms4j.api.callback.CallBack;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
/**
* {@link SmsMethodInterceptor}的通用实现
* 能够根据{@link SmsMethodType}将调用分发到某个具体的拦截方法上
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
public abstract class AbstractGenericMethodInterceptor implements SmsMethodInterceptor {
/**
* 前置拦截在方法执行前调用
*
* @param methodType 方法类型若不是{@link SmsMethodType}则可能为{@code null}
* @param method 方法
* @param target 调用对象
* @param params 调用参数
* @return 调用参数
* @implNote 若重写此方法则务必调用{@link #doBeforeInvoke}方法实现调用分发
*/
@Override
public Object[] beforeInvoke(SmsMethodType methodType, Method method, Object target, Object[] params) {
if (Objects.nonNull(methodType)) {
// 将方法分发到具体的调用
doBeforeInvoke(params, methodType);
}
return params;
}
@SuppressWarnings("unchecked")
protected final void doBeforeInvoke(Object[] params, SmsMethodType methodType) {
Objects.requireNonNull(methodType);
switch (methodType) {
case SEND_MESSAGE:
beforeSendMessage((String)params[0], (String)params[1]);
break;
case SEND_MESSAGE_WITH_TEMPLATE:
beforeSendMessageWithTemplate((String)params[0], (LinkedHashMap<String, String>)params[1]);
break;
case SEND_MESSAGE_WITH_CUSTOM_TEMPLATE:
beforeSendMessageWithCustomTemplate((String)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2]);
break;
case MASS_TEXTING:
beforeMassTexting((List<String>)params[0], (String)params[1]);
break;
case MASS_TEXTING_WITH_TEMPLATE:
beforeMassTextingWithTemplate((List<String>)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2]);
break;
case SEND_MESSAGE_ASYNC:
beforeSendMessageAsync((String)params[0], (String)params[1], (CallBack)params[2]);
break;
case SEND_MESSAGE_ASYNC_NO_CALLBACK:
beforeSendMessageAsyncNoCallback((String)params[0], (String)params[1]);
break;
case SEND_MESSAGE_ASYNC_WITH_TEMPLATE:
beforeSendMessageAsyncWithTemplate((String)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2], (CallBack)params[3]);
break;
case SEND_MESSAGE_ASYNC_WITH_TEMPLATE_NO_CALLBACK:
beforeSendMessageAsyncWithTemplateNoCallback((String)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2]);
break;
case DELAYED_MESSAGE:
beforeDelayedMessage((String)params[0], (String)params[1], (Long)params[2]);
break;
case DELAYED_MESSAGE_WITH_TEMPLATE:
beforeDelayedMessageWithTemplate((String)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2], (Long)params[3]);
break;
case DELAY_MASS_TEXTING:
beforeDelayMassTexting((List<String>)params[0], (String)params[1], (Long)params[2]);
break;
case DELAY_MASS_TEXTING_WITH_TEMPLATE:
beforeDelayMassTextingWithTemplate((List<String>)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2], (Long)params[3]);
break;
default: // do nothing
}
}
/**
* 后置拦截在方法执行后无论是否发生异常都会调用
*
* @param methodType 方法类型若不是{@link SmsMethodType}则可能为{@code null}
* @param method 调用方法
* @param params 调用参数
* @param result 返回值
* @param ex 调用过程捕获的异常可能为{@code null}
* @return 返回值不为{@code null}时将覆盖原有的返回值
* @implNote 若重写此方法则务必调用{@link ##doAfterCompletion}方法实现调用分发
*/
@Override
public Object afterCompletion(SmsMethodType methodType, Method method, Object[] params, Object result, Exception ex) {
if (Objects.nonNull(methodType)) {
// 将方法分发到具体的调用...
doAfterCompletion(params, result, ex, methodType);
}
return result;
}
@SuppressWarnings("unchecked")
private void doAfterCompletion(Object[] params, Object result, Exception ex, SmsMethodType methodType) {
Objects.requireNonNull(methodType);
switch (methodType) {
case SEND_MESSAGE:
afterSendMessage((String)params[0], (String)params[1], result, ex);
break;
case SEND_MESSAGE_WITH_TEMPLATE:
afterSendMessageWithTemplate((String)params[0], (LinkedHashMap<String, String>)params[1], result, ex);
break;
case SEND_MESSAGE_WITH_CUSTOM_TEMPLATE:
afterSendMessageWithCustomTemplate((String)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2], result, ex);
break;
case MASS_TEXTING:
afterMassTexting((List<String>)params[0], (String)params[1], result, ex);
break;
case MASS_TEXTING_WITH_TEMPLATE:
afterMassTextingWithTemplate((List<String>)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2], result, ex);
break;
case SEND_MESSAGE_ASYNC:
afterSendMessageAsync((String)params[0], (String)params[1], (CallBack)params[2], result, ex);
break;
case SEND_MESSAGE_ASYNC_NO_CALLBACK:
afterSendMessageAsyncNoCallback((String)params[0], (String)params[1], result, ex);
break;
case SEND_MESSAGE_ASYNC_WITH_TEMPLATE:
afterSendMessageAsyncWithTemplate((String)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2], (CallBack)params[3], result, ex);
break;
case SEND_MESSAGE_ASYNC_WITH_TEMPLATE_NO_CALLBACK:
afterSendMessageAsyncWithTemplateNoCallback((String)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2], result, ex);
break;
case DELAYED_MESSAGE:
afterDelayedMessage((String)params[0], (String)params[1], (Long)params[2], result, ex);
break;
case DELAYED_MESSAGE_WITH_TEMPLATE:
afterDelayedMessageWithTemplate((String)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2], (Long)params[3], result, ex);
break;
case DELAY_MASS_TEXTING:
afterDelayMassTexting((List<String>)params[0], (String)params[1], (Long)params[2], result, ex);
break;
case DELAY_MASS_TEXTING_WITH_TEMPLATE:
afterDelayMassTextingWithTemplate((List<String>)params[0], (String)params[1], (LinkedHashMap<String, String>)params[2], (Long)params[3], result, ex);
break;
default: // do nothing
}
}
// region ===== before =====
/**
* {@link org.dromara.sms4j.api.SmsBlend#sendMessage(String, String)}
*
* @param phone 接收短信的手机号
* @param message 内容
*/
protected void beforeSendMessage(String phone, String message) {
// do nothing
}
/**
* {@link org.dromara.sms4j.api.SmsBlend#sendMessage(String, LinkedHashMap)}
*
* @param phone 接收短信的手机号
* @param messages 模板内容
*/
protected void beforeSendMessageWithTemplate(String phone, LinkedHashMap<String, String> messages) {
// do nothing
}
/**
* {@link org.dromara.sms4j.api.SmsBlend#sendMessage(String, String, LinkedHashMap)}
*
* @param phone 接收短信的手机号
* @param templateId 模板ID
* @param messages 内容
*/
protected void beforeSendMessageWithCustomTemplate(
String phone, String templateId, LinkedHashMap<String, String> messages) {
// do nothing
}
/**
* {@link org.dromara.sms4j.api.SmsBlend#massTexting(List, String)}
*
* @param phones 接收短信的手机号
* @param message 内容
*/
protected void beforeMassTexting(List<String> phones, String message) {
// do nothing
}
/**
* {@link org.dromara.sms4j.api.SmsBlend#massTexting(List, String, LinkedHashMap)}
*
* @param phones 接收短信的手机号
* @param templateId 模板ID
* @param messages 内容
*/
protected void beforeMassTextingWithTemplate(
List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
// do nothing
}
/**
* 前置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessageAsync(String, String, CallBack)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param callBack 回调
*/
protected void beforeSendMessageAsync(String phone, String message, CallBack callBack) {
// do nothing
}
/**
* 前置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessageAsync(String, String)}
*
* @param phone 接收短信的手机号
* @param message 内容
*/
protected void beforeSendMessageAsyncNoCallback(String phone, String message) {
// do nothing
}
/**
* 前置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessageAsync(String, String, LinkedHashMap, CallBack)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param template 模板
* @param callBack 回调
*/
protected void beforeSendMessageAsyncWithTemplate(String phone, String message, LinkedHashMap<String, String> template, CallBack callBack) {
// do nothing
}
/**
* 前置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessageAsync(String, String, LinkedHashMap)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param template 模板
*/
protected void beforeSendMessageAsyncWithTemplateNoCallback(String phone, String message, LinkedHashMap<String, String> template) {
// do nothing
}
/**
* 前置处理 {@link org.dromara.sms4j.api.SmsBlend#delayedMessage(String, String, Long)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param delay 延迟时间
*/
protected void beforeDelayedMessage(String phone, String message, Long delay) {
// do nothing
}
/**
* 前置处理 {@link org.dromara.sms4j.api.SmsBlend#delayedMessage(String, String, LinkedHashMap, Long)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param template 模板
* @param delay 延迟时间
*/
protected void beforeDelayedMessageWithTemplate(String phone, String message, LinkedHashMap<String, String> template, Long delay) {
// do nothing
}
/**
* 前置处理 {@link org.dromara.sms4j.api.SmsBlend#delayMassTexting(List, String, Long)}
*
* @param phones 接收短信的手机号
* @param message 内容
* @param delay 延迟时间
*/
protected void beforeDelayMassTexting(List<String> phones, String message, Long delay) {
// do nothing
}
/**
* 前置处理 {@link org.dromara.sms4j.api.SmsBlend#delayMassTexting(List, String, LinkedHashMap, Long)}
*
* @param phones 接收短信的手机号
* @param message 内容
* @param template 模板
* @param delay 延迟时间
*/
protected void beforeDelayMassTextingWithTemplate(List<String> phones, String message, LinkedHashMap<String, String> template, Long delay) {
// do nothing
}
// endregion
// region ===== after =====
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessage(String, String)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterSendMessage(String phone, String message, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessage(String, LinkedHashMap)}
*
* @param phone 接收短信的手机号
* @param messages 模板内容
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterSendMessageWithTemplate(String phone, LinkedHashMap<String, String> messages, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessage(String, String, LinkedHashMap)}
*
* @param phone 接收短信的手机号
* @param templateId 模板ID
* @param messages 内容
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterSendMessageWithCustomTemplate(String phone, String templateId, LinkedHashMap<String, String> messages, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#massTexting(List, String)}
*
* @param phones 接收短信的手机号
* @param message 内容
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterMassTexting(List<String> phones, String message, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#massTexting(List, String, LinkedHashMap)}
*
* @param phones 接收短信的手机号
* @param templateId 模板ID
* @param messages 内容
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterMassTextingWithTemplate(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessageAsync(String, String, CallBack)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param callBack 回调
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterSendMessageAsync(String phone, String message, CallBack callBack, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessageAsync(String, String)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterSendMessageAsyncNoCallback(String phone, String message, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessageAsync(String, String, LinkedHashMap, CallBack)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param template 模板
* @param callBack 回调
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterSendMessageAsyncWithTemplate(String phone, String message, LinkedHashMap<String, String> template, CallBack callBack, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#sendMessageAsync(String, String, LinkedHashMap)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param template 模板
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterSendMessageAsyncWithTemplateNoCallback(String phone, String message, LinkedHashMap<String, String> template, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#delayedMessage(String, String, Long)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param delay 延迟时间
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterDelayedMessage(String phone, String message, Long delay, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#delayedMessage(String, String, LinkedHashMap, Long)}
*
* @param phone 接收短信的手机号
* @param message 内容
* @param template 模板
* @param delay 延迟时间
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterDelayedMessageWithTemplate(String phone, String message, LinkedHashMap<String, String> template, Long delay, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#delayMassTexting(List, String, Long)}
*
* @param phones 接收短信的手机号
* @param message 内容
* @param delay 延迟时间
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterDelayMassTexting(List<String> phones, String message, Long delay, Object result, Exception ex) {
// do nothing
}
/**
* 后置处理 {@link org.dromara.sms4j.api.SmsBlend#delayMassTexting(List, String, LinkedHashMap, Long)}
*
* @param phones 接收短信的手机号
* @param message 内容
* @param template 模板
* @param delay 延迟时间
* @param result 方法返回值
* @param ex 异常信息
*/
protected void afterDelayMassTextingWithTemplate(List<String> phones, String message, LinkedHashMap<String, String> template, Long delay, Object result, Exception ex) {
// do nothing
}
// endregion
}

View File

@ -1,44 +0,0 @@
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(name)) {
if (NumberOfParasmeters.TWO == NumberOfParasmeters.getNumberOfParasmetersEnum(parameterCount)) {
sendMessagePreProcess((String) param[0], 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(name)) {
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, Object message);
void sendMessageByTemplatePreProcess(String phone, String templateId, LinkedHashMap<String, String> messages);
void massTextingPreProcess(List<String> phones, String message);
void massTextingByTemplatePreProcess(List<String> phones, String templateId, LinkedHashMap<String, String> messages);
}

View File

@ -1,4 +1,5 @@
package org.dromara.sms4j.api.proxy; package org.dromara.sms4j.api.proxy;
/** /**
* 排序接口 * 排序接口
* *
@ -6,7 +7,13 @@ package org.dromara.sms4j.api.proxy;
* @since 2023/10/27 13:03 * @since 2023/10/27 13:03
*/ */
public interface Order { public interface Order {
default public int getOrder(){
return 999; /**
* 获取排序值排序值越大的对象则优先级越低
*
* @return 排序值
*/
default int getOrder(){
return Integer.MAX_VALUE;
} }
} }

View File

@ -0,0 +1,46 @@
package org.dromara.sms4j.api.proxy;
import org.dromara.sms4j.api.SmsBlend;
import java.lang.reflect.Method;
/**
* <p>方法拦截器用于拦截{@link SmsBlend}中的方法<br />
* 推荐基于{@link AbstractGenericMethodInterceptor}实现自定义拦截器
*
* @author sh1yu
* @since 2023/10/27 13:03
* @see AbstractGenericMethodInterceptor
* @see SmsMethodType
*/
public interface SmsMethodInterceptor extends Order {
/**
* 前置拦截在方法执行前调用
*
* @param methodType 方法类型若不是{@link SmsMethodType}则可能为{@code null}
* @param method 方法
* @param target 调用对象
* @param params 调用参数
* @return 调用参数
*/
default Object[] beforeInvoke(
SmsMethodType methodType, Method method, Object target, Object[] params) {
return params;
}
/**
* 后置拦截在方法执行后无论是否发生异常都会调用
*
* @param methodType 方法类型若不是{@link SmsMethodType}则可能为{@code null}
* @param method 调用方法
* @param params 调用参数
* @param result 返回值
* @param ex 调用过程捕获的异常可能为{@code null}
* @return 返回值不为{@code null}时将覆盖原有的返回值
*/
default Object afterCompletion(
SmsMethodType methodType, Method method, Object[] params, Object result, Exception ex) {
return result;
}
}

View File

@ -0,0 +1,140 @@
package org.dromara.sms4j.api.proxy;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import lombok.Getter;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.callback.CallBack;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
/**
* {@link SmsBlend}中的主要方法
*
* @author huangchengxing
*/
@Getter
public enum SmsMethodType {
/**
* {@link SmsBlend#sendMessage(String, String)}
*/
SEND_MESSAGE("sendMessage", String.class, String.class),
/**
* {@link SmsBlend#sendMessage(String, LinkedHashMap)}
*/
SEND_MESSAGE_WITH_TEMPLATE("sendMessage", String.class, LinkedHashMap.class),
/**
* {@link SmsBlend#sendMessage(String, String, LinkedHashMap)}
*/
SEND_MESSAGE_WITH_CUSTOM_TEMPLATE("sendMessage", String.class, String.class, LinkedHashMap.class),
/**
* {@link SmsBlend#massTexting(List, String)}
*/
MASS_TEXTING("massTexting", List.class, String.class),
/**
* {@link SmsBlend#massTexting(List, String, LinkedHashMap)}
*/
MASS_TEXTING_WITH_TEMPLATE("massTexting", List.class, String.class, LinkedHashMap.class),
/**
* {@link SmsBlend#sendMessageAsync(String, String, CallBack)}
*/
SEND_MESSAGE_ASYNC("sendMessageAsync", String.class, String.class, CallBack.class),
/**
* {@link SmsBlend#sendMessageAsync(String, String)}
*/
SEND_MESSAGE_ASYNC_NO_CALLBACK("sendMessageAsync", String.class, String.class),
/**
* {@link SmsBlend#sendMessageAsync(String, String, LinkedHashMap, CallBack)}
*/
SEND_MESSAGE_ASYNC_WITH_TEMPLATE("sendMessageAsync", String.class, String.class, LinkedHashMap.class, CallBack.class),
/**
* {@link SmsBlend#sendMessageAsync(String, String, LinkedHashMap)}
*/
SEND_MESSAGE_ASYNC_WITH_TEMPLATE_NO_CALLBACK("sendMessageAsync", String.class, String.class, LinkedHashMap.class),
/**
* {@link SmsBlend#delayedMessage(String, String, Long)}
*/
DELAYED_MESSAGE("delayedMessage", String.class, String.class, Long.class),
/**
* {@link SmsBlend#delayedMessage(String, String, LinkedHashMap, Long)}
*/
DELAYED_MESSAGE_WITH_TEMPLATE("delayedMessage", String.class, String.class, LinkedHashMap.class, Long.class),
/**
* {@link SmsBlend#delayMassTexting(List, String, Long)}
*/
DELAY_MASS_TEXTING("delayMassTexting", List.class, String.class, Long.class),
/**
* {@link SmsBlend#delayMassTexting(List, String, LinkedHashMap, Long)}
*/
DELAY_MASS_TEXTING_WITH_TEMPLATE("delayMassTexting", List.class, String.class, LinkedHashMap.class, Long.class);
/**
* 方法
*/
private final Method method;
/**
* 获取方法名称
*
* @return 方法名称
*/
public String getName() {
return method.getName();
}
/**
* 检查指定方法是否与{@link SmsBlend}中的方法匹配
*
* @param target 目标方法
* @return 是否
*/
public boolean isMatch(Method target) {
if (target == method) {
return true;
}
return Objects.nonNull(target)
&& Objects.equals(method.getName(), target.getName())
&& ClassUtil.isAllAssignableFrom(method.getParameterTypes(), target.getParameterTypes())
&& ClassUtil.isAssignable(method.getReturnType(), target.getReturnType());
}
/**
* 获取与目标方法对应的{@link SmsBlend}方法
*
* @param targetMethod 目标方法
* @return {@link SmsMethodType}
*/
public static SmsMethodType of(Method targetMethod) {
return Stream.of(values())
.filter(m -> m.isMatch(targetMethod))
.findFirst()
.orElse(null);
}
SmsMethodType(String methodName, Class<?>... parameterTypes) {
this.method = ReflectUtil.getMethod(SmsBlend.class, methodName, parameterTypes);
Assert.notNull(
method, "Cannot find best match method from SmsBlend: ({})[{}]",
methodName, Arrays.asList(parameterTypes)
);
}
}

View File

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

View File

@ -0,0 +1,19 @@
package org.dromara.sms4j.api.proxy;
import java.util.Set;
/**
* 限制拦截器仅针对哪些厂商生效如果拦截器需要根据支持厂商加载那可以实现此接口
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
public interface SupplierSupportedMethodInterceptor extends SmsMethodInterceptor {
/**
* 获取支持的供应商名称
*
* @return 供应商名称
*/
Set<String> getSupportedSuppliers();
}

View File

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

View File

@ -1,6 +1,5 @@
package org.dromara.sms4j.api.proxy.aware; package org.dromara.sms4j.api.proxy.aware;
/** /**
* 系统配置感知接口 * 系统配置感知接口
* *

View File

@ -1,6 +1,7 @@
package org.dromara.sms4j.api.proxy.aware; package org.dromara.sms4j.api.proxy.aware;
import org.dromara.sms4j.api.dao.SmsDao; import org.dromara.sms4j.api.dao.SmsDao;
/** /**
* 缓存感知接口 * 缓存感知接口
* *

View File

@ -5,6 +5,10 @@ package org.dromara.sms4j.comm.constant;
*/ */
public abstract class SupplierConstant { public abstract class SupplierConstant {
/**
* 用于测试
*/
public static final String LOCAL = "local";
/** /**
* 阿里 * 阿里
*/ */

View File

@ -142,8 +142,6 @@ public abstract class SmsFactory {
} }
/** /**
* renderWithRestricted * renderWithRestricted
* <p> 构建smsBlend对象的代理对象 * <p> 构建smsBlend对象的代理对象
@ -152,7 +150,7 @@ public abstract class SmsFactory {
*/ */
@Deprecated @Deprecated
private static SmsBlend renderWithProxy(SmsBlend sms) { private static SmsBlend renderWithProxy(SmsBlend sms) {
return SmsProxyFactory.getProxySmsBlend(sms); return SmsProxyFactory.getProxiedSmsBlend(sms);
} }
/** /**

View File

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

View File

@ -0,0 +1,50 @@
package org.dromara.sms4j.core.proxy;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.provider.config.SmsConfig;
import java.util.Map;
import java.util.Objects;
/**
* 环境信息持有
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class EnvironmentHolder {
private static SmsConfig smsConfig = null;
private static Map<String, Map<String, Object>> blends = null;
public static void frozen(SmsConfig smsConfig, Map<String, Map<String, Object>> blends) {
if (Objects.nonNull(EnvironmentHolder.smsConfig) || Objects.nonNull(EnvironmentHolder.blends)) {
log.warn("The environmental information has been loaded and cannot be overwritten!");
return;
}
EnvironmentHolder.smsConfig = smsConfig;
EnvironmentHolder.blends = blends;
}
/**
* 配置信息
*
* @return 配置
*/
static SmsConfig getSmsConfig() {
return smsConfig;
}
/**
* 获取短信配置信息
*
* @return 短信配置信息
*/
static Map<String, Map<String, Object>> getBlends() {
return blends;
}
}

View File

@ -1,69 +1,54 @@
package org.dromara.sms4j.core.proxy; package org.dromara.sms4j.core.proxy;
import lombok.RequiredArgsConstructor;
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.SmsProcessor; import org.dromara.sms4j.api.proxy.SmsMethodType;
import org.dromara.sms4j.api.proxy.SuppotFilter; import org.dromara.sms4j.api.proxy.SmsMethodInterceptor;
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.LinkedList;
import java.util.List; import java.util.List;
/** /**
* SmsBlend增强封装smsblend和执行器 * {@link SmsBlend}代理用于织入{@link SmsMethodInterceptor}实现前置和后置拦截
* *
* @author sh1yu * @author sh1yu
* @since 2023/10/27 13:03 * @since 2023/10/27 13:03
*/ */
@Slf4j @Slf4j
@RequiredArgsConstructor
public class SmsInvocationHandler implements InvocationHandler { public class SmsInvocationHandler implements InvocationHandler {
private final SmsBlend smsBlend;
private final LinkedList<SmsProcessor> processors;
private final SmsBlend delegate;
public SmsInvocationHandler(SmsBlend smsBlend, LinkedList<SmsProcessor> processors) { private final List<SmsMethodInterceptor> interceptors;
this.smsBlend = smsBlend;
this.processors = processors;
}
@Override @Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable { public Object invoke(Object target, Method method, Object[] params) {
SmsMethodType methodType = SmsMethodType.of(method);
Object result = null; Object result = null;
//前置执行器 // 前置拦截
objects = doPreProcess(smsBlend, method, objects); params = invokePreHandle(methodType, delegate, method, params);
Exception ex = null;
try { try {
result = method.invoke(smsBlend, objects); result = method.invoke(delegate, params);
} catch (Exception e) { } catch (Exception e) {
//错误执行器 ex = e;
doErrorHandleProcess(smsBlend, method, objects,e);
} }
//后置执行器 // 后置拦截
doPostrocess(smsBlend, method, objects, result); return invokeAfterCompletion(methodType, method, params, result, ex);
return result;
} }
public Object[] doPreProcess(Object o, Method method, Object[] objects) { private Object[] invokePreHandle(SmsMethodType methodType, Object o, Method method, Object[] objects) {
for (SmsProcessor processor : processors) { for (SmsMethodInterceptor interceptor : interceptors) {
objects = processor.preProcessor(method, o, objects); objects = interceptor.beforeInvoke(methodType, method, o, objects);
} }
return objects; return objects;
} }
public void doErrorHandleProcess(Object o, Method method, Object[] objects,Exception e) throws InvocationTargetException, IllegalAccessException { private Object invokeAfterCompletion(SmsMethodType methodType, Method method, Object[] params, Object result, Exception ex) {
for (SmsProcessor processor : processors) { for (SmsMethodInterceptor interceptor : interceptors) {
processor.exceptionHandleProcessor(method, o, objects,e); result = interceptor.afterCompletion(methodType, method, params, result, ex);
}
}
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; return result;
} }

View File

@ -1,12 +1,14 @@
package org.dromara.sms4j.core.proxy; package org.dromara.sms4j.core.proxy;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
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.dao.SmsDao; import org.dromara.sms4j.api.dao.SmsDao;
import org.dromara.sms4j.api.dao.SmsDaoDefaultImpl; import org.dromara.sms4j.api.dao.SmsDaoDefaultImpl;
import org.dromara.sms4j.api.proxy.Order; import org.dromara.sms4j.api.proxy.Order;
import org.dromara.sms4j.api.proxy.SmsProcessor; import org.dromara.sms4j.api.proxy.SmsMethodInterceptor;
import org.dromara.sms4j.api.proxy.SuppotFilter; import org.dromara.sms4j.api.proxy.SupplierSupportedMethodInterceptor;
import org.dromara.sms4j.api.proxy.aware.SmsBlendConfigAware; import org.dromara.sms4j.api.proxy.aware.SmsBlendConfigAware;
import org.dromara.sms4j.api.proxy.aware.SmsDaoAware; import org.dromara.sms4j.api.proxy.aware.SmsDaoAware;
import org.dromara.sms4j.api.proxy.aware.SmsConfigAware; import org.dromara.sms4j.api.proxy.aware.SmsConfigAware;
@ -14,9 +16,10 @@ import org.dromara.sms4j.api.proxy.aware.SmsConfigAware;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -24,68 +27,80 @@ import java.util.stream.Collectors;
* *
* @author sh1yu * @author sh1yu
* @since 2023/10/27 13:03 * @since 2023/10/27 13:03
* @see SmsMethodInterceptor
* @see SmsInvocationHandler
*/ */
@Slf4j @Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class SmsProxyFactory { public abstract class SmsProxyFactory {
private static final LinkedList<SmsProcessor> processors = new LinkedList<>();
public static SmsBlend getProxySmsBlend(SmsBlend smsBlend) { private static final List<SmsMethodInterceptor> INTERCEPTORS = new ArrayList<>();
LinkedList<SmsProcessor> ownerProcessors = processors.stream().filter(processor -> !shouldSkipProcess(processor,smsBlend)).collect(Collectors.toCollection(LinkedList::new)); private static final String SPRING_SMS_DAO_LOAD_PATH = "org.dromara.sms4j.starter.holder.SpringSmsDaoHolder";
return (SmsBlend) Proxy.newProxyInstance(smsBlend.getClass().getClassLoader(), new Class[]{SmsBlend.class}, new SmsInvocationHandler(smsBlend, ownerProcessors)); private static final String SOLON_SMS_DAO_LOAD_PATH = "org.dromara.sms4j.solon.holder.SolonSmsDaoHolder";
public static final int CORE_PARAM_VALIDATE_METHOD_INTERCEPTOR_ORDER = -1;
public static final int BLACK_LIST_METHOD_INTERCEPTOR_ORDER = 0;
public static final int BLACK_LIST_RECORDING_METHOD_INTERCEPTOR_ORDER = 1;
public static final int RESTRICTED_METHOD_INTERCEPTOR = 3;
public static final int SINGLE_BLEND_RESTRICTED_METHOD_INTERCEPTOR_ORDER = 2;
public static SmsBlend getProxiedSmsBlend(SmsBlend smsBlend) {
Objects.requireNonNull(smsBlend);
// 若已被代理则直接返回避免重复代理
if (smsBlend instanceof Proxied) {
return smsBlend;
}
List<SmsMethodInterceptor> appliedInterceptors = INTERCEPTORS.stream()
.filter(processor -> canApply(processor, smsBlend))
.collect(Collectors.toList());
return (SmsBlend) Proxy.newProxyInstance(
smsBlend.getClass().getClassLoader(),
new Class[]{ SmsBlend.class, Proxied.class },
new SmsInvocationHandler(smsBlend, appliedInterceptors)
);
} }
/** /**
* 增加拦截器 * 增加拦截器
*
* @param processor 处理器
* TODO 当在SpringBoot或Solon环境中使用时应当在完成加载后主动从IOC容器获取并注册用户自定义的方法拦截器
*/ */
public static void addProcessor(SmsProcessor processor) { public static void addProcessor(SmsMethodInterceptor processor) {
//校验拦截器是否正确 Objects.requireNonNull(processor);
processorValidate(processor); // 调用Aware接口将必要参数传递给处理器
awareTransfer(processor); awareTransfer(processor);
processors.add(processor); // 尝试移除旧拦截器避免重复添加
processors.sort(Comparator.comparingInt(Order::getOrder)); INTERCEPTORS.remove(processor);
INTERCEPTORS.add(processor);
INTERCEPTORS.sort(Comparator.comparingInt(Order::getOrder));
} }
/* private static boolean canApply(SmsMethodInterceptor processor, SmsBlend smsBlend) {
* @see SuppotFilter // 判断当前的执行器有没有开厂商过滤支不支持当前厂商
*/ return !(processor instanceof SupplierSupportedMethodInterceptor)
public static boolean shouldSkipProcess(SmsProcessor processor, SmsBlend smsBlend) { || ((SupplierSupportedMethodInterceptor)processor).getSupportedSuppliers().contains(smsBlend.getSupplier());
//判断当前的执行器有没有开厂商过滤支不支持当前厂商
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(SmsMethodInterceptor processor) {
private static void awareTransfer(SmsProcessor processor) {
if (processor instanceof SmsDaoAware){ if (processor instanceof SmsDaoAware){
((SmsDaoAware) processor).setSmsDao(getSmsDaoFromFramework()); ((SmsDaoAware) processor).setSmsDao(getSmsDaoFromFramework());
} }
if (processor instanceof SmsConfigAware){ if (processor instanceof SmsConfigAware){
((SmsConfigAware) processor).setSmsConfig(EnvirmentHolder.getSmsConfig()); ((SmsConfigAware) processor).setSmsConfig(EnvironmentHolder.getSmsConfig());
} }
if (processor instanceof SmsBlendConfigAware){ if (processor instanceof SmsBlendConfigAware){
((SmsBlendConfigAware) processor).setSmsBlendsConfig(EnvirmentHolder.getBlends()); ((SmsBlendConfigAware) processor).setSmsBlendsConfig(EnvironmentHolder.getBlends());
} }
} }
//校验拦截器是否正确
private static void processorValidate(SmsProcessor processor) {
}
//获取Sms的实现
private static SmsDao getSmsDaoFromFramework() { private static SmsDao getSmsDaoFromFramework() {
SmsDao smsDao; SmsDao smsDao;
smsDao = getSmsDaoFromFramework("org.dromara.sms4j.starter.holder.SpringSmsDaoHolder", "SpringBoot"); smsDao = getSmsDaoFromFramework(SPRING_SMS_DAO_LOAD_PATH, "SpringBoot");
if (null != smsDao) { if (null != smsDao) {
return smsDao; return smsDao;
} }
smsDao = getSmsDaoFromFramework("org.dromara.sms4j.solon.holder.SolonSmsDaoHolder", "Solon"); smsDao = getSmsDaoFromFramework(SOLON_SMS_DAO_LOAD_PATH, "Solon");
if (null != smsDao) { if (null != smsDao) {
return smsDao; return smsDao;
} }
@ -93,16 +108,16 @@ public abstract class SmsProxyFactory {
return SmsDaoDefaultImpl.getInstance(); return SmsDaoDefaultImpl.getInstance();
} }
//获取Sms的实现
private static SmsDao getSmsDaoFromFramework(String className, String frameworkName) { private static SmsDao getSmsDaoFromFramework(String className, String frameworkName) {
try { try {
Class<?> clazz = Class.forName(className); Class<?> clazz = Class.forName(className);
Method getSmsDao = clazz.getMethod("getSmsDao", null); Method getSmsDao = clazz.getMethod("getSmsDao");
return (SmsDao) getSmsDao.invoke(null, null); return (SmsDao) getSmsDao.invoke(null);
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
log.error("{}:加载SmsDao失败尝试其他框架加载......", frameworkName); log.error("{}:加载SmsDao失败尝试其他框架加载......", frameworkName);
} }
return null; return null;
} }
public interface Proxied {}
} }

View File

@ -0,0 +1,70 @@
package org.dromara.sms4j.core.proxy.interceptor;
import cn.hutool.core.collection.CollUtil;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.dao.SmsDao;
import org.dromara.sms4j.api.proxy.AbstractGenericMethodInterceptor;
import org.dromara.sms4j.api.proxy.aware.SmsDaoAware;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.core.proxy.SmsProxyFactory;
import java.util.*;
/**
* 黑名单前置拦截执行器
*
* @author sh1yu
* @since 2023/10/27 13:03
*/
@Slf4j
public class BlackListMethodInterceptor extends AbstractGenericMethodInterceptor implements SmsDaoAware {
private static final String CONFIG_PROPERTIES_PREFIX = "sms:blacklist:global";
@Setter
private SmsDao smsDao;
@Override
public int getOrder() {
return SmsProxyFactory.BLACK_LIST_METHOD_INTERCEPTOR_ORDER;
}
@Override
protected void beforeSendMessage(String phone, String message) {
doRestricted(Collections.singletonList(phone));
}
@Override
protected void beforeSendMessageWithTemplate(String phone, LinkedHashMap<String, String> messages) {
doRestricted(Collections.singletonList(phone));
}
@Override
protected void beforeSendMessageWithCustomTemplate(String phone, String templateId, LinkedHashMap<String, String> messages) {
doRestricted(Collections.singletonList(phone));
}
@Override
protected void beforeMassTexting(List<String> phones, String message) {
doRestricted(phones);
}
@Override
protected void beforeMassTextingWithTemplate(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
doRestricted(phones);
}
@SuppressWarnings("unchecked")
private void doRestricted(List<String> phones) {
List<String> blackList = (List<String>) smsDao.get(CONFIG_PROPERTIES_PREFIX);
if (CollUtil.isEmpty(blackList)) {
return;
}
for (String phone : phones) {
if (blackList.stream().anyMatch(black -> black.replace("-","").equals(phone))) {
throw new SmsBlendException("The phone:", phone + " hit global blacklist");
}
}
}
}

View File

@ -0,0 +1,105 @@
package org.dromara.sms4j.core.proxy.interceptor;
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.SmsMethodType;
import org.dromara.sms4j.api.proxy.SmsMethodInterceptor;
import org.dromara.sms4j.api.proxy.aware.SmsDaoAware;
import org.dromara.sms4j.core.proxy.SmsProxyFactory;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* 黑名单前置拦截执行器
*
* @author sh1yu
* @since 2023/10/27 13:03
* @see SmsBlend#batchJoinBlacklist
* @see SmsBlend#batchRemovalFromBlacklist
* @see SmsBlend#joinInBlacklist
* @see SmsBlend#removeFromBlacklist
*/
@Slf4j
public class BlackListRecordingMethodInterceptor implements SmsMethodInterceptor, SmsDaoAware {
private static final String REDIS_KEY_PREFIX = "sms:blacklist:global";
private static final String CONFIG_PROPERTIES_PREFIX = "sms:blacklist:global";
private static final String JOIN_IN_BLACKLIST_METHOD = "joinInBlacklist";
private static final String REMOVE_FROM_BLACKLIST_METHOD = "removeFromBlacklist";
private static final String BATCH_JOIN_BLACKLIST_METHOD = "batchJoinBlacklist";
private static final String BATCH_REMOVAL_FROM_BLACKLIST_METHOD = "batchRemovalFromBlacklist";
@Setter
private SmsDao smsDao;
@Override
public int getOrder(){
return SmsProxyFactory.BLACK_LIST_RECORDING_METHOD_INTERCEPTOR_ORDER;
}
@SuppressWarnings("unchecked")
@Override
public Object[] beforeInvoke(SmsMethodType methodType, Method method, Object target, Object[] params) {
// TODO 并发操作是否可以保证安全性
// TODO 不同的SmsBlend对象操作的确是同一份黑名单配置是否合理
// TODO 是否应该同时支持黑白名单
//添加到黑名单
String methodName = method.getName();
if (JOIN_IN_BLACKLIST_METHOD.equals(methodName)) {
String cacheKey = REDIS_KEY_PREFIX;
List<String> blackList = getBlackList(cacheKey);
blackList.add((String) params[0]);
flushBlackList(cacheKey,blackList);
}
//从黑名单移除
else if (REMOVE_FROM_BLACKLIST_METHOD.equals(methodName)) {
String cacheKey = REDIS_KEY_PREFIX;
List<String> blackList = getBlackList(cacheKey);
blackList.remove((String) params[0]);
flushBlackList(cacheKey,blackList);
}
//批量添加到黑名单
else if (BATCH_JOIN_BLACKLIST_METHOD.equals(methodName)) {
String cacheKey = REDIS_KEY_PREFIX;
List<String> blackList = getBlackList(cacheKey);
blackList.addAll((List<String>) params[0]);
flushBlackList(cacheKey,blackList);
}
//批量从黑名单移除
else if (BATCH_REMOVAL_FROM_BLACKLIST_METHOD.equals(methodName)) {
String cacheKey = REDIS_KEY_PREFIX;
List<String> blackList = getBlackList(cacheKey);
blackList.removeAll((List<String>) params[0]);
flushBlackList(cacheKey, blackList);
}
return params;
}
@SuppressWarnings("unchecked")
private List<String> getBlackList(String cacheKey) {
List<String> blackList;
Object cache = smsDao.get(cacheKey);
if (null != cache) {
blackList = (List<String>) cache;
return blackList;
}
blackList = new ArrayList<>();
smsDao.set(CONFIG_PROPERTIES_PREFIX, blackList);
return blackList;
}
/**
* 刷新黑名单
*
* @param cacheKey cacheKey
* @param blackList 新的黑名单列表
*/
private void flushBlackList(String cacheKey, List<String> blackList) {
smsDao.set(cacheKey, blackList);
}
}

View File

@ -0,0 +1,132 @@
package org.dromara.sms4j.core.proxy.interceptor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.dao.SmsDao;
import org.dromara.sms4j.api.proxy.AbstractGenericMethodInterceptor;
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.core.proxy.SmsProxyFactory;
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 RestrictedMethodInterceptor extends AbstractGenericMethodInterceptor implements SmsDaoAware {
/**
* 每分钟最多可发送条数
* TODO 每分钟最多可发送条数应当可配置
*/
private static final Long MINUTE_MAXIMUM_COUNT = 60 * 1000L;
/**
* 每个账号最多可发送条数
* TODO 每个账号最多可发送条数应当可配置
*/
private static final Long ACCOUNT_MAXIMUM_COUNT = 24 * 60 * 60 * 1000L;
/**
* redis缓存前缀
* TODO redis缓存前缀应当可配置
*/
private static final String REDIS_KEY = "sms:restricted:";
/**
* 缓存实例
*/
@Setter
private SmsDao smsDao;
@Override
public int getOrder() {
return SmsProxyFactory.RESTRICTED_METHOD_INTERCEPTOR;
}
@Override
public void beforeSendMessage(String phone, String message) {
restricted(Collections.singletonList(phone));
}
@Override
protected void beforeSendMessageWithTemplate(String phone, LinkedHashMap<String, String> messages) {
restricted(Collections.singletonList(phone));
}
@Override
public void beforeSendMessageWithCustomTemplate(String phone, String templateId, LinkedHashMap<String, String> messages) {
restricted(Collections.singletonList(phone));
}
@Override
public void beforeMassTexting(List<String> phones, String message) {
restricted(phones);
}
@Override
public void beforeMassTextingWithTemplate(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
restricted(phones);
}
public void restricted(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) {
doRestricted(accountMax, minuteMax, phone);
}
}
private void doRestricted(Integer accountMax, Integer minuteMax, String phone) {
// 是否配置了每日限制
if (SmsUtils.isNotEmpty(accountMax)) {
checkAccountMax(accountMax, phone);
}
// 是否配置了每分钟最大限制
if (SmsUtils.isNotEmpty(minuteMax)) {
Integer count = (Integer) smsDao.get(REDIS_KEY + phone);
checkMinuteMax(minuteMax, phone, count);
}
}
private void checkMinuteMax(Integer minuteMax, String phone, Integer count) {
if (SmsUtils.isEmpty(count)) {
smsDao.set(REDIS_KEY + phone, 1, MINUTE_MAXIMUM_COUNT / 1000);
return;
}
if (count < minuteMax) {
smsDao.set(REDIS_KEY + phone, count + 1, MINUTE_MAXIMUM_COUNT / 1000);
return;
}
log.info("The phone: {}, number of short messages reached the maximum today", phone);
throw new SmsBlendException("The phone:", phone + " Text messages are sent too often");
}
private void checkAccountMax(Integer accountMax, String phone) {
Integer i = (Integer) smsDao.get(REDIS_KEY + phone + "max");
if (SmsUtils.isEmpty(i)) {
smsDao.set(REDIS_KEY + phone + "max", 1, ACCOUNT_MAXIMUM_COUNT / 1000);
return;
}
if (i >= accountMax) {
log.info("The phone: {}, number of short messages reached the maximum today", phone);
throw new SmsBlendException("The phone:" + phone + ",number of short messages reached the maximum today");
}
smsDao.set(REDIS_KEY + phone + "max", i + 1, ACCOUNT_MAXIMUM_COUNT / 1000);
}
}

View File

@ -1,17 +1,16 @@
package org.dromara.sms4j.core.proxy.processor; package org.dromara.sms4j.core.proxy.interceptor;
import lombok.Setter; import lombok.Setter;
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.dao.SmsDao; import org.dromara.sms4j.api.dao.SmsDao;
import org.dromara.sms4j.api.proxy.CoreMethodProcessor; import org.dromara.sms4j.api.proxy.SmsMethodType;
import org.dromara.sms4j.api.proxy.SmsProcessor; import org.dromara.sms4j.api.proxy.SmsMethodInterceptor;
import org.dromara.sms4j.api.proxy.aware.SmsBlendConfigAware; import org.dromara.sms4j.api.proxy.aware.SmsBlendConfigAware;
import org.dromara.sms4j.api.proxy.aware.SmsDaoAware; import org.dromara.sms4j.api.proxy.aware.SmsDaoAware;
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.provider.config.BaseConfig; import org.dromara.sms4j.core.proxy.SmsProxyFactory;
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.*; import java.util.*;
@ -24,9 +23,11 @@ import java.util.*;
* @since 2023/10/27 13:03 * @since 2023/10/27 13:03
*/ */
@Slf4j @Slf4j
public class SingleBlendRestrictedProcessor implements SmsProcessor, SmsDaoAware, SmsBlendConfigAware { public class SingleBlendRestrictedMethodInterceptor implements SmsMethodInterceptor, SmsDaoAware, SmsBlendConfigAware {
private static final String REDIS_KEY = "sms:restricted:"; private static final String REDIS_KEY = "sms:restricted:";
private static final String SEND_MESSAGE_METHOD = "sendMessage";
private static final String MASS_TEXT_METHOD = "massText";
/** /**
* 缓存实例 * 缓存实例
@ -35,30 +36,31 @@ public class SingleBlendRestrictedProcessor implements SmsProcessor, SmsDaoAware
private SmsDao smsDao; private SmsDao smsDao;
@Setter @Setter
Map smsBlendsConfig; private Map<String, Map<String, Object>> smsBlendsConfig;
@Override @Override
public int getOrder() { public int getOrder() {
return 2; return SmsProxyFactory.SINGLE_BLEND_RESTRICTED_METHOD_INTERCEPTOR_ORDER;
} }
@Override @Override
public Object[] preProcessor(Method method, Object source, Object[] param) { public Object[] beforeInvoke(SmsMethodType methodType, Method method, Object target, Object[] params) {
String name = method.getName(); if (Objects.isNull(methodType)
if (!"sendMessage".equals(name) && !"massText".equals(name)) { || !Objects.equals(methodType.getName(), SEND_MESSAGE_METHOD)
return param; || !Objects.equals(methodType.getName(), MASS_TEXT_METHOD)) {
return params;
} }
SmsBlend smsBlend = (SmsBlend) source; SmsBlend smsBlend = (SmsBlend)target;
String configId = smsBlend.getConfigId(); String configId = smsBlend.getConfigId();
Map targetConfig = (Map) smsBlendsConfig.get(configId); Map<String, Object> targetConfig = smsBlendsConfig.get(configId);
Object maximumObj = targetConfig.get("maximum"); Object maximumObj = targetConfig.get("maximum");
if (SmsUtils.isEmpty(maximumObj)) { if (SmsUtils.isEmpty(maximumObj)) {
return param; return params;
} }
int maximum = 0; int maximum = 0;
try{ try {
maximum = (int) maximumObj ; maximum = (int)maximumObj;
}catch (Exception e){ } catch (Exception e) {
log.error("获取厂商级发送上限参数错误!请检查!"); log.error("获取厂商级发送上限参数错误!请检查!");
throw new IllegalArgumentException("获取厂商级发送上限参数错误"); throw new IllegalArgumentException("获取厂商级发送上限参数错误");
} }
@ -66,11 +68,11 @@ public class SingleBlendRestrictedProcessor implements SmsProcessor, SmsDaoAware
if (SmsUtils.isEmpty(i)) { if (SmsUtils.isEmpty(i)) {
smsDao.set(REDIS_KEY + configId + "maximum", 1); smsDao.set(REDIS_KEY + configId + "maximum", 1);
} else if (i >= maximum) { } else if (i >= maximum) {
log.info("The channel:" + configId + ",messages reached the maximum"); log.info("The channel: {}, messages reached the maximum", configId);
throw new SmsBlendException("The channel:" + configId + ",messages reached the maximum"); throw new SmsBlendException("The channel: " + configId + ", messages reached the maximum", configId);
} else { } else {
smsDao.set(REDIS_KEY + configId + "maximum", i + 1); smsDao.set(REDIS_KEY + configId + "maximum", i + 1);
} }
return param; return params;
} }
} }

View File

@ -0,0 +1,91 @@
package org.dromara.sms4j.core.proxy.interceptor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.CharSequenceUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.proxy.AbstractGenericMethodInterceptor;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.core.proxy.SmsProxyFactory;
import java.util.*;
/**
* 同步调用方法参数校验前置拦截执行器
*
* @author sh1yu
* @since 2023/10/27 13:03
* TODO 异步调用和延迟调用的参数是否需要校验
*/
@Slf4j
public class SyncMethodParamValidateMethodInterceptor extends AbstractGenericMethodInterceptor {
@Override
public int getOrder() {
return SmsProxyFactory.CORE_PARAM_VALIDATE_METHOD_INTERCEPTOR_ORDER;
}
@Override
public void beforeSendMessage(String phone, String message) {
validatePhone(phone);
validateMessage(message);
}
@Override
protected void beforeSendMessageWithTemplate(String phone, LinkedHashMap<String, String> messages) {
validatePhone(phone);
validateMessage(messages);
}
@Override
public void beforeSendMessageWithCustomTemplate(String phone, String templateId, LinkedHashMap<String, String> messages) {
validatePhone(phone);
validateMessages(templateId, messages);
}
@Override
public void beforeMassTexting(List<String> phones, String message) {
validateMessage(message);
validatePhones(phones);
}
@Override
public void beforeMassTextingWithTemplate(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
validatePhones(phones);
validateMessages(templateId, messages);
}
public void validateMessage(Object messageObj) {
if (messageObj instanceof String){
String message = (String) messageObj;
if (CharSequenceUtil.isEmpty(message)) {
throw new SmsBlendException("can't send a null message!");
}
}
if (messageObj instanceof Map){
Map<?, ?> message = (Map<?, ?>) messageObj;
if (CollUtil.isEmpty(message)) {
throw new SmsBlendException("can't send a null message!");
}
}
}
public void validatePhone(String phone) {
if (CharSequenceUtil.isEmpty(phone)) {
throw new SmsBlendException("can't send message to null!");
}
}
public void validatePhones(List<String> phones) {
if (CollUtil.isEmpty(phones)) {
throw new SmsBlendException("can't send message to null!");
}
phones.forEach(this::validatePhone);
}
public void validateMessages(String templateId, LinkedHashMap<String, String> messages) {
if (CharSequenceUtil.isNotEmpty(templateId) && CollUtil.isEmpty(messages)) {
throw new SmsBlendException("can't use template without template param");
}
}
}

View File

@ -1,61 +0,0 @@
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, Object 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");
if(null==blackList){
return;
}
for (String phone : phones) {
if (blackList.stream().filter(black -> black.replace("-","").equals(phone)).findAny().isPresent()) {
throw new SmsBlendException("The phone:", phone + " hit global blacklist");
}
}
}
}

View File

@ -1,87 +0,0 @@
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;
public int getOrder(){
return 1;
}
@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);
}
}

View File

@ -1,85 +0,0 @@
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, Object 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(Object messageObj) {
if (messageObj instanceof String){
String message = (String) messageObj;
if (null == message || "".equals(message)) {
throw new SmsBlendException("cant send a null message!");
}
}
if (messageObj instanceof Map){
Map message = (Map) messageObj;
if (message.size()<1) {
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");
}
}
}

View File

@ -1,96 +0,0 @@
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, Object 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);
}
}
}
}
}

View File

@ -19,9 +19,9 @@ 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.proxy.EnvironmentHolder;
import org.dromara.sms4j.core.factory.SmsFactory; import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.core.proxy.processor.*; import org.dromara.sms4j.core.proxy.interceptor.*;
import org.dromara.sms4j.core.proxy.SmsProxyFactory; 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;
@ -29,6 +29,7 @@ import org.dromara.sms4j.huawei.config.HuaweiFactory;
import org.dromara.sms4j.javase.util.YamlUtils; import org.dromara.sms4j.javase.util.YamlUtils;
import org.dromara.sms4j.jdcloud.config.JdCloudFactory; import org.dromara.sms4j.jdcloud.config.JdCloudFactory;
import org.dromara.sms4j.lianlu.config.LianLuFactory; import org.dromara.sms4j.lianlu.config.LianLuFactory;
import org.dromara.sms4j.local.LocalFactory;
import org.dromara.sms4j.netease.config.NeteaseFactory; 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;
@ -153,36 +154,36 @@ public class SEInitializer {
Map<String, Map<String, Object>> blends = smsConfig.getBlends(); Map<String, Map<String, Object>> blends = smsConfig.getBlends();
//持有初始化配置信息 //持有初始化配置信息
EnvirmentHolder.frozenEnvirmet(smsConfig, blends); EnvironmentHolder.frozen(smsConfig, blends);
//注册执行器实现 //注册执行器实现
SmsProxyFactory.addProcessor(new RestrictedProcessor()); SmsProxyFactory.addProcessor(new RestrictedMethodInterceptor());
SmsProxyFactory.addProcessor(new BlackListProcessor()); SmsProxyFactory.addProcessor(new BlackListMethodInterceptor());
SmsProxyFactory.addProcessor(new BlackListRecordingProcessor()); SmsProxyFactory.addProcessor(new BlackListRecordingMethodInterceptor());
SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor()); SmsProxyFactory.addProcessor(new SingleBlendRestrictedMethodInterceptor());
SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor()); SmsProxyFactory.addProcessor(new SyncMethodParamValidateMethodInterceptor());
for (String configId : blends.keySet()) { blends.forEach((configId, configMap) -> {
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; return;
} }
configMap.put("config-id", configId); configMap.put("config-id", configId);
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());
SmsFactory.createSmsBlend(supplierConfig); SmsFactory.createSmsBlend(supplierConfig);
} });
} }
/** /**
* 注册默认工厂实例 * 注册默认工厂实例
*/ */
private void registerDefaultFactory() { private void registerDefaultFactory() {
ProviderFactoryHolder.registerFactory(LocalFactory.instance());
ProviderFactoryHolder.registerFactory(AlibabaFactory.instance()); ProviderFactoryHolder.registerFactory(AlibabaFactory.instance());
ProviderFactoryHolder.registerFactory(CloopenFactory.instance()); ProviderFactoryHolder.registerFactory(CloopenFactory.instance());
ProviderFactoryHolder.registerFactory(CtyunFactory.instance()); ProviderFactoryHolder.registerFactory(CtyunFactory.instance());

View File

@ -0,0 +1,20 @@
package org.dromara.sms4j.local;
import lombok.Getter;
import lombok.Setter;
import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.comm.constant.SupplierConstant;
/**
* 用于测试的{@link SupplierConfig}实现
*
* @author huangchengxing
* @see SupplierConstant#LOCAL
*/
@Setter
@Getter
public class LocalConfig implements SupplierConfig {
private String configId = SupplierConstant.LOCAL;
private String supplier = SupplierConstant.LOCAL;
}

View File

@ -0,0 +1,39 @@
package org.dromara.sms4j.local;
import org.dromara.sms4j.aliyun.config.AlibabaFactory;
import org.dromara.sms4j.comm.constant.SupplierConstant;
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
/**
* 用于创建{@link LocalSmsImpl}的工厂实现
*
* @author huangchengxing
* @see SupplierConstant#LOCAL
*/
public class LocalFactory implements BaseProviderFactory<LocalSmsImpl, LocalConfig> {
private static final LocalFactory INSTANCE = new LocalFactory();
/**
* 获取建造者实例
* @return 建造者实例
*/
public static LocalFactory instance() {
return INSTANCE;
}
@Override
public LocalSmsImpl createSms(LocalConfig localConfig) {
return new LocalSmsImpl();
}
@Override
public Class<LocalConfig> getConfigClass() {
return LocalConfig.class;
}
@Override
public String getSupplier() {
return SupplierConstant.LOCAL;
}
}

View File

@ -0,0 +1,133 @@
package org.dromara.sms4j.local;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.SmsBlend;
import org.dromara.sms4j.api.callback.CallBack;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.comm.constant.SupplierConstant;
import java.util.LinkedHashMap;
import java.util.List;
/**
* 用于测试的{@link SmsBlend}实现
* 总是在日志中输出参数信息并成功返回调用结果
*
* @author huangchengxing
* @see SupplierConstant#LOCAL
*/
@Slf4j
public class LocalSmsImpl implements SmsBlend {
@Override
public String getConfigId() {
return SupplierConstant.LOCAL;
}
@Override
public String getSupplier() {
return SupplierConstant.LOCAL;
}
@Override
public SmsResponse sendMessage(String phone, String message) {
log.info("send message: phone={}, message={}", phone, message);
return getResponse(new JSONObject()
.set("phone", phone)
.set("message", message));
}
@Override
public SmsResponse sendMessage(String phone, LinkedHashMap<String, String> messages) {
log.info("send message: phone={}, messages={}", phone, messages);
return getResponse(new JSONObject()
.set("phone", phone)
.set("messages", new JSONObject(messages)));
}
@Override
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
log.info("send message: phone={}, templateId={}, messages={}", phone, templateId, messages);
return getResponse(new JSONObject()
.set("phone", phone)
.set("templateId", templateId)
.set("messages", new JSONObject(messages)));
}
@Override
public SmsResponse massTexting(List<String> phones, String message) {
log.info("mass texting: phones={}, message={}", phones, message);
return getResponse(new JSONObject()
.set("phones", phones)
.set("message", message));
}
@Override
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
log.info("mass texting: phones={}, templateId={}, messages={}", phones, templateId, messages);
return getResponse(new JSONObject()
.set("phones", phones)
.set("templateId", templateId)
.set("messages", new JSONObject(messages)));
}
@Override
public void sendMessageAsync(String phone, String message, CallBack callBack) {
log.info("send message asynchronously: phone={}, message={}", phone, message);
// do nothing
callBack.callBack(getResponse(new JSONObject()
.set("phone", phone)
.set("message", message)));
}
@Override
public void sendMessageAsync(String phone, String message) {
log.info("send message asynchronously: phone={}, message={}", phone, message);
// do nothing
}
@Override
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages, CallBack callBack) {
log.info("send message asynchronously: phone={}, templateId={}, messages={}", phone, templateId, messages);
// do nothing
callBack.callBack(getResponse(new JSONObject()
.set("phone", phone)
.set("templateId", templateId)
.set("messages", new JSONObject(messages))));
}
@Override
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
log.info("send message asynchronously: phone={}, templateId={}, messages={}", phone, templateId, messages);
// do nothing
}
@Override
public void delayedMessage(String phone, String message, Long delayedTime) {
log.info("delayed message: phone={}, message={}, delayedTime={}", phone, message, delayedTime);
}
@Override
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
log.info("delayed message: phone={}, templateId={}, messages={}, delayedTime={}", phone, templateId, messages, delayedTime);
}
@Override
public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
log.info("delayed mass texting: phones={}, message={}, delayedTime={}", phones, message, delayedTime);
}
@Override
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
log.info("delayed mass texting: phones={}, templateId={}, messages={}, delayedTime={}", phones, templateId, messages, delayedTime);
}
private SmsResponse getResponse(JSONObject resJson) {
SmsResponse smsResponse = new SmsResponse();
smsResponse.setSuccess(true);
smsResponse.setData(resJson);
smsResponse.setConfigId(getConfigId());
return smsResponse;
}
}

View File

@ -10,15 +10,16 @@ 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.proxy.EnvironmentHolder;
import org.dromara.sms4j.core.factory.SmsFactory; import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.core.proxy.processor.*; import org.dromara.sms4j.core.proxy.interceptor.*;
import org.dromara.sms4j.core.proxy.SmsProxyFactory; 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;
import org.dromara.sms4j.jdcloud.config.JdCloudFactory; import org.dromara.sms4j.jdcloud.config.JdCloudFactory;
import org.dromara.sms4j.lianlu.config.LianLuFactory; import org.dromara.sms4j.lianlu.config.LianLuFactory;
import org.dromara.sms4j.local.LocalFactory;
import org.dromara.sms4j.netease.config.NeteaseFactory; 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;
@ -59,15 +60,15 @@ public class SmsBlendsInitializer {
// 注册短信对象工厂 // 注册短信对象工厂
ProviderFactoryHolder.registerFactory(factoryList); ProviderFactoryHolder.registerFactory(factoryList);
//持有初始化配置信息 //持有初始化配置信息
EnvirmentHolder.frozenEnvirmet(smsConfig, blends); EnvironmentHolder.frozen(smsConfig, blends);
//框架依赖持有缓存扩展 //框架依赖持有缓存扩展
new SolonSmsDaoHolder(context); new SolonSmsDaoHolder(context);
//注册执行器实现 //注册执行器实现
SmsProxyFactory.addProcessor(new RestrictedProcessor()); SmsProxyFactory.addProcessor(new RestrictedMethodInterceptor());
SmsProxyFactory.addProcessor(new BlackListProcessor()); SmsProxyFactory.addProcessor(new BlackListMethodInterceptor());
SmsProxyFactory.addProcessor(new BlackListRecordingProcessor()); SmsProxyFactory.addProcessor(new BlackListRecordingMethodInterceptor());
SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor()); SmsProxyFactory.addProcessor(new SingleBlendRestrictedMethodInterceptor());
SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor()); SmsProxyFactory.addProcessor(new SyncMethodParamValidateMethodInterceptor());
// 解析供应商配置 // 解析供应商配置
for(String configId : blends.keySet()) { for(String configId : blends.keySet()) {
Map<String, Object> configMap = blends.get(configId); Map<String, Object> configMap = blends.get(configId);
@ -92,6 +93,7 @@ public class SmsBlendsInitializer {
* 注册默认工厂实例 * 注册默认工厂实例
*/ */
private void registerDefaultFactory() { private void registerDefaultFactory() {
ProviderFactoryHolder.registerFactory(LocalFactory.instance());
ProviderFactoryHolder.registerFactory(AlibabaFactory.instance()); ProviderFactoryHolder.registerFactory(AlibabaFactory.instance());
ProviderFactoryHolder.registerFactory(CloopenFactory.instance()); ProviderFactoryHolder.registerFactory(CloopenFactory.instance());
ProviderFactoryHolder.registerFactory(CtyunFactory.instance()); ProviderFactoryHolder.registerFactory(CtyunFactory.instance());

View File

@ -2,8 +2,11 @@ sms:
# 标注从yml读取配置 # 标注从yml读取配置
config-type: yaml config-type: yaml
# 账户上限 # 账户上限
account-max: 1 account-max: 5
blends: blends:
# 本地测试
local:
supplier: local
# 阿里短信例子 # 阿里短信例子
ali: ali:
#厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分 #厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分

View File

@ -8,6 +8,7 @@ import org.dromara.sms4j.comm.constant.SupplierConstant;
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.factory.SmsFactory; import org.dromara.sms4j.core.factory.SmsFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
@ -20,7 +21,7 @@ import java.util.LinkedHashMap;
*/ */
@Slf4j @Slf4j
@SpringBootTest @SpringBootTest
public class SmsProcessorTest { public class SmsMethodInterceptorTest {
/** /**
* 填测试手机号 * 填测试手机号
*/ */
@ -31,7 +32,7 @@ public class SmsProcessorTest {
@Test @Test
public void test1() { public void test1() {
System.out.println("------------"); System.out.println("------------");
SmsBlend smsBlend = SmsFactory.getBySupplier(SupplierConstant.UNISMS); SmsBlend smsBlend = SmsFactory.getBySupplier(SupplierConstant.LOCAL);
SmsResponse smsResponse = smsBlend.sendMessage(PHONE, SmsUtils.getRandomInt(6)); SmsResponse smsResponse = smsBlend.sendMessage(PHONE, SmsUtils.getRandomInt(6));
Assert.isTrue(smsResponse.isSuccess()); Assert.isTrue(smsResponse.isSuccess());
System.out.println(smsResponse.getData()); System.out.println(smsResponse.getData());
@ -44,7 +45,7 @@ public class SmsProcessorTest {
public void test2() { public void test2() {
System.out.println("------------"); System.out.println("------------");
SmsBlend smsBlend = SmsFactory.getBySupplier(SupplierConstant.HUAWEI); SmsBlend smsBlend = SmsFactory.getBySupplier(SupplierConstant.LOCAL);
SmsResponse smsResponse = smsBlend.sendMessage(PHONE1, SmsUtils.getRandomInt(6)); SmsResponse smsResponse = smsBlend.sendMessage(PHONE1, SmsUtils.getRandomInt(6));
Assert.isTrue(smsResponse.isSuccess()); Assert.isTrue(smsResponse.isSuccess());
System.out.println(smsResponse.getData()); System.out.println(smsResponse.getData());
@ -56,62 +57,28 @@ public class SmsProcessorTest {
public void test3() { public void test3() {
System.out.println("------------"); System.out.println("------------");
SmsBlendException knowEx = null; SmsBlend sb = SmsFactory.getBySupplier(SupplierConstant.LOCAL);
try { Assertions.assertThrows(SmsBlendException.class,
SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE, new LinkedHashMap<>()); () -> sb.sendMessage(PHONE, new LinkedHashMap<>())
} catch (SmsBlendException e) { );
knowEx = e; Assertions.assertThrows(SmsBlendException.class,
System.out.println(knowEx.getMessage()); () -> sb.sendMessage("", SmsUtils.getRandomInt(6))
} );
Assert.notNull(knowEx); Assertions.assertThrows(SmsBlendException.class,
knowEx = null; () -> sb.sendMessage(PHONE, "")
try { );
SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage("", SmsUtils.getRandomInt(6)); Assertions.assertThrows(SmsBlendException.class,
} catch (SmsBlendException e) { () -> sb.sendMessage(PHONE, "111", new LinkedHashMap<>())
knowEx = e; );
System.out.println(knowEx.getMessage()); Assertions.assertThrows(SmsBlendException.class,
} () -> sb.massTexting(Collections.singletonList(PHONE), "")
Assert.notNull(knowEx); );
knowEx = null; Assertions.assertThrows(SmsBlendException.class,
try { () -> sb.massTexting(Collections.singletonList(PHONE), "2222", new LinkedHashMap<>())
SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE, ""); );
} catch (SmsBlendException e) { Assertions.assertThrows(SmsBlendException.class,
knowEx = e; () -> sb.massTexting(new ArrayList<>(), "321321")
System.out.println(knowEx.getMessage()); );
}
Assert.notNull(knowEx);
knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE, "111", new LinkedHashMap<>());
} catch (SmsBlendException e) {
knowEx = e;
System.out.println(knowEx.getMessage());
}
Assert.notNull(knowEx);
knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.UNISMS).massTexting(Collections.singletonList(PHONE), "");
} catch (SmsBlendException e) {
knowEx = e;
System.out.println(knowEx.getMessage());
}
Assert.notNull(knowEx);
knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.UNISMS).massTexting(Collections.singletonList(PHONE), "2222", new LinkedHashMap<>());
} catch (SmsBlendException e) {
knowEx = e;
System.out.println(knowEx.getMessage());
}
Assert.notNull(knowEx);
knowEx = null;
try {
SmsFactory.getBySupplier(SupplierConstant.UNISMS).massTexting(new ArrayList<String>(), "321321");
} catch (SmsBlendException e) {
knowEx = e;
System.out.println(knowEx.getMessage());
}
Assert.notNull(knowEx);
} }
//黑名单测试 //黑名单测试
@ -119,7 +86,7 @@ public class SmsProcessorTest {
public void test4() { public void test4() {
System.out.println("------------"); System.out.println("------------");
SmsBlend smsBlend = SmsFactory.getBySupplier(SupplierConstant.UNISMS); SmsBlend smsBlend = SmsFactory.getBySupplier(SupplierConstant.LOCAL);
//单黑名单添加 //单黑名单添加
smsBlend.joinInBlacklist(PHONE); smsBlend.joinInBlacklist(PHONE);
SmsBlendException knowEx = null; SmsBlendException knowEx = null;
@ -155,17 +122,10 @@ public class SmsProcessorTest {
@Test @Test
public void test5() { public void test5() {
System.out.println("------------"); System.out.println("------------");
SmsBlend sb = SmsFactory.getBySupplier(SupplierConstant.LOCAL);
SmsResponse smsResponse = SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE, SmsUtils.getRandomInt(6)); SmsResponse smsResponse = sb.sendMessage(PHONE, SmsUtils.getRandomInt(6));
Assert.isTrue(smsResponse.isSuccess()); Assert.isTrue(smsResponse.isSuccess());
SmsBlendException knowEx = null; Assertions.assertThrows(SmsBlendException.class, () -> sb.sendMessage(PHONE, SmsUtils.getRandomInt(6)));
try {
SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE, SmsUtils.getRandomInt(6));
} catch (SmsBlendException e) {
knowEx = e;
System.out.println(knowEx.getMessage());
}
Assert.notNull(knowEx);
} }
//渠道级上限测试需成功发送6笔再发就会报错 参数配置 6 //渠道级上限测试需成功发送6笔再发就会报错 参数配置 6
@ -173,12 +133,13 @@ public class SmsProcessorTest {
public void test6() { public void test6() {
System.out.println("------------"); System.out.println("------------");
SmsResponse smsResponse = SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE1, SmsUtils.getRandomInt(6)); SmsResponse smsResponse = SmsFactory.getBySupplier(SupplierConstant.LOCAL)
.sendMessage(PHONE1, SmsUtils.getRandomInt(6));
Assert.isTrue(smsResponse.isSuccess()); Assert.isTrue(smsResponse.isSuccess());
SmsBlendException knowEx = null; SmsBlendException knowEx = null;
try { try {
SmsFactory.getBySupplier(SupplierConstant.UNISMS).sendMessage(PHONE1, SmsUtils.getRandomInt(6)); SmsFactory.getBySupplier(SupplierConstant.LOCAL).sendMessage(PHONE1, SmsUtils.getRandomInt(6));
} catch (SmsBlendException e) { } catch (SmsBlendException e) {
knowEx = e; knowEx = e;
System.out.println(knowEx.getMessage()); System.out.println(knowEx.getMessage());

View File

@ -11,15 +11,16 @@ 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.proxy.EnvironmentHolder;
import org.dromara.sms4j.core.factory.SmsFactory; import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.core.proxy.processor.*; import org.dromara.sms4j.core.proxy.interceptor.*;
import org.dromara.sms4j.core.proxy.SmsProxyFactory; 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;
import org.dromara.sms4j.jdcloud.config.JdCloudFactory; import org.dromara.sms4j.jdcloud.config.JdCloudFactory;
import org.dromara.sms4j.lianlu.config.LianLuFactory; import org.dromara.sms4j.lianlu.config.LianLuFactory;
import org.dromara.sms4j.local.LocalFactory;
import org.dromara.sms4j.netease.config.NeteaseFactory; 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;
@ -57,30 +58,29 @@ public class SmsBlendsInitializer {
if(ConfigType.YAML.equals(this.smsConfig.getConfigType())) { if(ConfigType.YAML.equals(this.smsConfig.getConfigType())) {
//持有初始化配置信息 //持有初始化配置信息
EnvirmentHolder.frozenEnvirmet(smsConfig, blends); EnvironmentHolder.frozen(smsConfig, blends);
//注册执行器实现 //注册执行器实现
SmsProxyFactory.addProcessor(new RestrictedProcessor()); SmsProxyFactory.addProcessor(new RestrictedMethodInterceptor());
SmsProxyFactory.addProcessor(new BlackListProcessor()); SmsProxyFactory.addProcessor(new BlackListMethodInterceptor());
SmsProxyFactory.addProcessor(new BlackListRecordingProcessor()); SmsProxyFactory.addProcessor(new BlackListRecordingMethodInterceptor());
SmsProxyFactory.addProcessor(new SingleBlendRestrictedProcessor()); SmsProxyFactory.addProcessor(new SingleBlendRestrictedMethodInterceptor());
SmsProxyFactory.addProcessor(new CoreMethodParamValidateProcessor()); SmsProxyFactory.addProcessor(new SyncMethodParamValidateMethodInterceptor());
// 解析供应商配置 // 解析供应商配置
for(String configId : blends.keySet()) { blends.forEach((configId, configMap) -> {
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, org.dromara.sms4j.api.universal.SupplierConfig>) ProviderFactoryHolder.requireForSupplier(supplier); BaseProviderFactory<SmsBlend, SupplierConfig> providerFactory = (BaseProviderFactory<SmsBlend, org.dromara.sms4j.api.universal.SupplierConfig>) ProviderFactoryHolder.requireForSupplier(supplier);
if(providerFactory == null) { if(providerFactory == null) {
log.warn("创建\"{}\"的短信服务失败,未找到供应商为\"{}\"的服务", configId, supplier); log.warn("创建'{}'的短信服务失败,未找到供应商为'{}'的服务", configId, supplier);
continue; return;
} }
configMap.put("config-id", configId); configMap.put("config-id", configId);
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());
SmsFactory.createSmsBlend(supplierConfig); SmsFactory.createSmsBlend(supplierConfig);
} });
} }
@ -90,6 +90,7 @@ public class SmsBlendsInitializer {
* 注册默认工厂实例 * 注册默认工厂实例
*/ */
private void registerDefaultFactory() { private void registerDefaultFactory() {
ProviderFactoryHolder.registerFactory(LocalFactory.instance());
ProviderFactoryHolder.registerFactory(AlibabaFactory.instance()); ProviderFactoryHolder.registerFactory(AlibabaFactory.instance());
ProviderFactoryHolder.registerFactory(CloopenFactory.instance()); ProviderFactoryHolder.registerFactory(CloopenFactory.instance());
ProviderFactoryHolder.registerFactory(CtyunFactory.instance()); ProviderFactoryHolder.registerFactory(CtyunFactory.instance());