Explorar o código

update sms 2.2.0 => 3.1.0

AprilWind %!s(int64=2) %!d(string=hai) anos
pai
achega
a6932f8c0a

+ 55 - 24
config/nacos/ruoyi-resource.yml

@@ -54,28 +54,59 @@ mail:
   connectionTimeout: 0
 
 # sms 短信 支持 阿里云 腾讯云 云片 等等各式各样的短信服务商
-# https://wind.kim/doc/start 文档地址 各个厂商可同时使用
+# https://sms4j.com/doc3/ 差异配置文档地址 支持单厂商多配置,可以配置多个同时使用
 sms:
-  # 阿里云 dysmsapi.aliyuncs.com
-  alibaba:
-    #请求地址 默认为 dysmsapi.aliyuncs.com 如无特殊改变可以不用设置
-    requestUrl: dysmsapi.aliyuncs.com
-    #阿里云的accessKey
-    accessKeyId: xxxxxxx
-    #阿里云的accessKeySecret
-    accessKeySecret: xxxxxxx
-    #短信签名
-    signature: 测试
-  tencent:
-    #请求地址默认为 sms.tencentcloudapi.com 如无特殊改变可不用设置
-    requestUrl: sms.tencentcloudapi.com
-    #腾讯云的accessKey
-    accessKeyId: xxxxxxx
-    #腾讯云的accessKeySecret
-    accessKeySecret: xxxxxxx
-    #短信签名
-    signature: 测试
-    #短信sdkAppId
-    sdkAppId: appid
-    #地域信息默认为 ap-guangzhou 如无特殊改变可不用设置
-    territory: ap-guangzhou
+  # 配置源类型用于标定配置来源(interface,yaml)
+  config-type: yaml
+  # 用于标定yml中的配置是否开启短信拦截,接口配置不受此限制
+  restricted: true
+  # 短信拦截限制单手机号每分钟最大发送,只对开启了拦截的配置有效
+  minute-max: 1
+  # 短信拦截限制单手机号每日最大发送量,只对开启了拦截的配置有效
+  account-max: 30
+  # 以下配置来自于 org.dromara.sms4j.provider.config.BaseConfig类中
+  blends:
+    # 自定义的标识,也就是configId这里可以是任意值(最好不要是中文)
+    tx1:
+      # 框架定义的厂商名称标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
+      supplier: tencent
+      # 有些称为accessKey有些称之为apiKey,也有称为sdkKey或者appId。
+      access-key-id: 您的accessKey
+      # 称为accessSecret有些称之为apiSecret
+      access-key-secret: 您的accessKeySecret
+      # 您的短信签名
+      signature: 您的短信签名
+      #模板ID 如果不需要简化的sendMessage方法可以不配置,如果使用sendMessage的快速发送需此配置
+      template-id: xxxxxxxx
+      #您的sdkAppId
+      sdk-app-id: 您的sdkAppId
+      # 随机权重,负载均衡的权重值依赖于此,默认为1,如不需要负载均衡可不进行配置
+      weight: 1
+      #短信自动重试间隔时间  默认五秒
+      retry-interval: 3
+      # 短信重试次数,默认0次不重试,如果你需要短信重试则根据自己的需求修改值即可
+      max-retries: 1
+      # 当前厂商的发送数量上限,默认不设置上限
+      maximum: 10000
+    # 自定义的标识,也就是configId这里可以是任意值(最好不要是中文)
+    tx2:
+      #厂商标识,标定此配置是哪个厂商,详细请看厂商标识介绍部分
+      supplier: tencent
+      #您的accessKey
+      access-key-id: 您的accessKey
+      #您的accessKeySecret
+      access-key-secret: 您的accessKeySecret
+      #您的短信签名
+      signature: 您的短信签名
+      #模板ID 非必须配置,如果使用sendMessage的快速发送需此配置
+      template-id: xxxxxxxx
+      #您的sdkAppId
+      sdk-app-id: 您的sdkAppId
+      # 随机权重,负载均衡的权重值依赖于此,默认为1,如不需要负载均衡可不进行配置
+      weight: 1
+      #短信自动重试间隔时间  默认五秒
+      retry-interval: 3
+      # 短信重试次数,默认0次不重试,如果你需要短信重试则根据自己的需求修改值即可
+      max-retries: 1
+      # 当前厂商的发送数量上限,默认不设置上限
+      maximum: 10000

+ 1 - 1
pom.xml

@@ -56,7 +56,7 @@
         <okhttp.version>4.10.0</okhttp.version>
 
         <!-- SMS 配置 -->
-        <sms4j.version>2.2.0</sms4j.version>
+        <sms4j.version>3.1.0</sms4j.version>
 
         <!-- 插件版本 -->
         <maven-compiler-plugin.verison>3.11.0</maven-compiler-plugin.verison>

+ 128 - 7
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteSmsService.java

@@ -1,24 +1,145 @@
 package org.dromara.resource.api;
 
-import org.dromara.common.core.exception.ServiceException;
 import org.dromara.resource.api.domain.RemoteSms;
 
 import java.util.LinkedHashMap;
+import java.util.List;
 
 /**
  * 短信服务
  *
- * @author Lion Li
+ * @author Feng
  */
 public interface RemoteSmsService {
 
     /**
-     * 发送短信
+     * 同步方法:发送固定消息模板短信
      *
-     * @param phones     电话号(多个逗号分割)
-     * @param templateId 模板id
-     * @param param      模板对应参数
+     * @param phone   目标手机号
+     * @param message 短信内容
+     * @return 封装了短信发送结果的 RemoteSms 对象
      */
-    RemoteSms send(String phones, String templateId, LinkedHashMap<String, String> param) throws ServiceException;
+    RemoteSms sendMessage(String phone, String message);
+
+    /**
+     * 同步方法:发送固定消息模板多模板参数短信
+     *
+     * @param phone    目标手机号
+     * @param messages 短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    RemoteSms sendMessage(String phone, LinkedHashMap<String, String> messages);
+
+    /**
+     * 同步方法:使用自定义模板发送短信
+     *
+     * @param phone      目标手机号
+     * @param templateId 短信模板ID
+     * @param messages   短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    RemoteSms sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages);
+
+    /**
+     * 同步方法:群发固定模板短信
+     *
+     * @param phones  目标手机号列表(1~1000)
+     * @param message 短信内容
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    RemoteSms massTexting(List<String> phones, String message);
+
+    /**
+     * 同步方法:使用自定义模板群发短信
+     *
+     * @param phones     目标手机号列表(1~1000)(1~1000)
+     * @param templateId 短信模板ID
+     * @param messages   短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    RemoteSms massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages);
+
+    /**
+     * 异步方法:发送固定消息模板短信
+     *
+     * @param phone   目标手机号
+     * @param message 短信内容
+     */
+    void sendMessageAsync(String phone, String message);
+
+    /**
+     * 异步方法:使用自定义模板发送短信
+     *
+     * @param phone      目标手机号
+     * @param templateId 短信模板ID
+     * @param messages   短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     */
+    void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages);
+
+    /**
+     * 延迟发送:发送固定消息模板短信
+     *
+     * @param phone       目标手机号
+     * @param message     短信内容
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    void delayedMessage(String phone, String message, Long delayedTime);
+
+    /**
+     * 延迟发送:使用自定义模板发送定时短信
+     *
+     * @param phone       目标手机号
+     * @param templateId  短信模板ID
+     * @param messages    短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
+
+    /**
+     * 延迟群发:群发延迟短信
+     *
+     * @param phones      目标手机号列表(1~1000)
+     * @param message     短信内容
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    void delayMassTexting(List<String> phones, String message, Long delayedTime);
+
+    /**
+     * 延迟群发:使用自定义模板发送群体延迟短信
+     *
+     * @param phones      目标手机号列表(1~1000)
+     * @param templateId  短信模板ID
+     * @param messages    短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime);
+
+    /**
+     * 加入黑名单
+     *
+     * @param phone 手机号
+     */
+    void joinInBlacklist(String phone);
+
+    /**
+     * 加入黑名单
+     *
+     * @param phones 手机号列表
+     */
+    void joinInBlacklist(List<String> phones);
+
+    /**
+     * 移除黑名单
+     *
+     * @param phone 手机号
+     */
+    void removeFromBlacklist(String phone);
+
+    /**
+     * 移除黑名单
+     *
+     * @param phones 手机号
+     */
+    void removeFromBlacklist(List<String> phones);
 
 }

+ 4 - 4
ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteSms.java

@@ -19,15 +19,15 @@ public class RemoteSms implements Serializable {
     /**
      * 是否成功
      */
-    private Boolean isSuccess;
+    private Boolean success;
 
     /**
-     * 响应消息
+     * 配置标识名 如未配置取对应渠道名例如 Alibaba
      */
-    private String message;
+    private String configId;
 
     /**
-     * 实际响应
+     * 厂商原返回
      * <p>
      * 可自行转换为 SDK 对应的 SendSmsResponse
      */

+ 6 - 7
ruoyi-common/ruoyi-common-sms/pom.xml

@@ -20,13 +20,12 @@
         <dependency>
             <groupId>org.dromara.sms4j</groupId>
             <artifactId>sms4j-spring-boot-starter</artifactId>
-            <exclusions>
-                <!-- 排除京东短信内存在的fastjson等待作者后续修复 -->
-                <exclusion>
-                    <groupId>com.alibaba</groupId>
-                    <artifactId>fastjson</artifactId>
-                </exclusion>
-            </exclusions>
+        </dependency>
+
+        <!-- RuoYi Common Redis-->
+        <dependency>
+            <groupId>org.dromara</groupId>
+            <artifactId>ruoyi-common-redis</artifactId>
         </dependency>
 
     </dependencies>

+ 14 - 4
ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/SmsAutoConfiguration.java

@@ -1,14 +1,24 @@
 package org.dromara.common.sms.config;
 
+import org.dromara.common.redis.config.RedisConfiguration;
+import org.dromara.common.sms.core.dao.PlusSmsDao;
+import org.dromara.sms4j.api.dao.SmsDao;
 import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Primary;
 
 /**
- * 短信配置类(暂时没用 预留扩展)
+ * 短信配置类
  *
- * @author Lion Li
- * @version 4.2.0
+ * @author Feng
  */
-@AutoConfiguration
+@AutoConfiguration(after = {RedisConfiguration.class})
 public class SmsAutoConfiguration {
 
+    @Primary
+    @Bean
+    public SmsDao smsDao() {
+        return new PlusSmsDao();
+    }
+
 }

+ 59 - 0
ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/dao/PlusSmsDao.java

@@ -0,0 +1,59 @@
+package org.dromara.common.sms.core.dao;
+
+import org.dromara.common.core.constant.GlobalConstants;
+import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.sms4j.api.dao.SmsDao;
+
+import java.time.Duration;
+
+/**
+ * SmsDao缓存配置 (使用框架自带RedisUtils实现 协议统一)
+ * <p>主要用于短信重试和拦截的缓存
+ *
+ * @author Feng
+ */
+public class PlusSmsDao implements SmsDao {
+
+    /**
+     * 存储
+     *
+     * @param key       键
+     * @param value     值
+     * @param cacheTime 缓存时间(单位:秒)
+     */
+    @Override
+    public void set(String key, Object value, long cacheTime) {
+        RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, Duration.ofSeconds(cacheTime));
+    }
+
+    /**
+     * 存储
+     *
+     * @param key   键
+     * @param value 值
+     */
+    @Override
+    public void set(String key, Object value) {
+        RedisUtils.setCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key, value, true);
+    }
+
+    /**
+     * 读取
+     *
+     * @param key 键
+     * @return 值
+     */
+    @Override
+    public Object get(String key) {
+        return RedisUtils.getCacheObject(GlobalConstants.GLOBAL_REDIS_KEY + key);
+    }
+
+    /**
+     * 清空
+     */
+    @Override
+    public void clean() {
+        RedisUtils.deleteObject(GlobalConstants.GLOBAL_REDIS_KEY + "sms:");
+    }
+
+}

+ 2 - 3
ruoyi-example/ruoyi-demo/src/main/java/org/dromara/demo/controller/SmsController.java

@@ -5,7 +5,6 @@ import org.dromara.common.core.domain.R;
 import org.dromara.sms4j.api.SmsBlend;
 import org.dromara.sms4j.api.entity.SmsResponse;
 import org.dromara.sms4j.core.factory.SmsFactory;
-import org.dromara.sms4j.provider.enumerate.SupplierType;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -35,7 +34,7 @@ public class SmsController {
     public R<Object> sendAliyun(String phones, String templateId) {
         LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
         map.put("code", "1234");
-        SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA);
+        SmsBlend smsBlend = SmsFactory.getSmsBlend("tx1");
         SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map);
         return R.ok(smsResponse);
     }
@@ -51,7 +50,7 @@ public class SmsController {
         LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
 //        map.put("2", "测试测试");
         map.put("1", "1234");
-        SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.TENCENT);
+        SmsBlend smsBlend = SmsFactory.getSmsBlend("tx1");
         SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, map);
         return R.ok(smsResponse);
     }

+ 3 - 4
ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/controller/SysSmsController.java

@@ -14,7 +14,6 @@ import org.dromara.common.web.core.BaseController;
 import org.dromara.sms4j.api.SmsBlend;
 import org.dromara.sms4j.api.entity.SmsResponse;
 import org.dromara.sms4j.core.factory.SmsFactory;
-import org.dromara.sms4j.provider.enumerate.SupplierType;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -50,11 +49,11 @@ public class SysSmsController extends BaseController {
         String templateId = "";
         LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
         map.put("code", code);
-        SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA);
+        SmsBlend smsBlend = SmsFactory.getSmsBlend("tx1");
         SmsResponse smsResponse = smsBlend.sendMessage(phonenumber, templateId, map);
-        if (!"OK".equals(smsResponse.getCode())) {
+        if (!smsResponse.isSuccess()) {
             log.error("验证码短信发送异常 => {}", smsResponse);
-            return R.fail(smsResponse.getMessage());
+            return R.fail(smsResponse.getData().toString());
         }
         return R.ok();
     }

+ 1 - 1
ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/dubbo/RemoteMessageServiceImpl.java

@@ -8,7 +8,7 @@ import org.dromara.resource.api.RemoteMessageService;
 import org.springframework.stereotype.Service;
 
 /**
- * 短信服务
+ * 消息服务
  *
  * @author Lion Li
  */

+ 205 - 16
ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/dubbo/RemoteSmsServiceImpl.java

@@ -3,22 +3,20 @@ package org.dromara.resource.dubbo;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.dubbo.config.annotation.DubboService;
-import org.dromara.common.core.exception.ServiceException;
-import org.dromara.common.json.utils.JsonUtils;
 import org.dromara.resource.api.RemoteSmsService;
 import org.dromara.resource.api.domain.RemoteSms;
 import org.dromara.sms4j.api.SmsBlend;
 import org.dromara.sms4j.api.entity.SmsResponse;
 import org.dromara.sms4j.core.factory.SmsFactory;
-import org.dromara.sms4j.provider.enumerate.SupplierType;
 import org.springframework.stereotype.Service;
 
 import java.util.LinkedHashMap;
+import java.util.List;
 
 /**
  * 短信服务
  *
- * @author Lion Li
+ * @author Feng
  */
 @Slf4j
 @RequiredArgsConstructor
@@ -27,20 +25,211 @@ import java.util.LinkedHashMap;
 public class RemoteSmsServiceImpl implements RemoteSmsService {
 
     /**
-     * 发送短信
+     * 获取特定供应商类型的 SmsBlend 实例
      *
-     * @param phones     电话号(多个逗号分割)
-     * @param templateId 模板id
-     * @param param      模板对应参数
+     * @return SmsBlend 实例,代表指定供应商类型
      */
-    public RemoteSms send(String phones, String templateId, LinkedHashMap<String, String> param) throws ServiceException {
-        SmsBlend smsBlend = SmsFactory.createSmsBlend(SupplierType.ALIBABA);
-        SmsResponse smsResponse = smsBlend.sendMessage(phones, templateId, param);
-        RemoteSms sysSms = new RemoteSms();
-        sysSms.setIsSuccess(smsResponse.isSuccess());
-        sysSms.setMessage(smsResponse.getMessage());
-        sysSms.setResponse(JsonUtils.toJsonString(smsResponse));
-        return sysSms;
+    private SmsBlend getSmsBlend() {
+        return SmsFactory.getSmsBlend("tx1");
+    }
+
+    /**
+     * 根据给定的 SmsResponse 对象创建并返回一个 RemoteSms 对象,封装短信发送的响应信息
+     *
+     * @param smsResponse 短信发送的响应信息
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    private RemoteSms getRemoteSms(SmsResponse smsResponse) {
+        // 创建一个 RemoteSms 对象,封装响应信息
+        RemoteSms sms = new RemoteSms();
+        sms.setSuccess(smsResponse.isSuccess());
+        sms.setResponse(smsResponse.getData().toString());
+        sms.setConfigId(smsResponse.getConfigId());
+        return sms;
+    }
+
+    /**
+     * 同步方法:发送简单文本短信
+     *
+     * @param phone   目标手机号
+     * @param message 短信内容
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    @Override
+    public RemoteSms sendMessage(String phone, String message) {
+        // 调用 getSmsBlend 方法获取对应短信供应商的 SmsBlend 实例
+        SmsResponse smsResponse = getSmsBlend().sendMessage(phone, message);
+        return getRemoteSms(smsResponse);
+    }
+
+    /**
+     * 同步方法:发送固定消息模板多模板参数短信
+     *
+     * @param phone    目标手机号
+     * @param messages 短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    @Override
+    public RemoteSms sendMessage(String phone, LinkedHashMap<String, String> messages) {
+        SmsResponse smsResponse = getSmsBlend().sendMessage(phone, messages);
+        return getRemoteSms(smsResponse);
+    }
+
+    /**
+     * 同步方法:发送带参数的短信
+     *
+     * @param phone      目标手机号
+     * @param templateId 短信模板ID
+     * @param messages   短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    @Override
+    public RemoteSms sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
+        // 调用 getSmsBlend 方法获取对应短信供应商的 SmsBlend 实例
+        SmsResponse smsResponse = getSmsBlend().sendMessage(phone, templateId, messages);
+        return getRemoteSms(smsResponse);
+    }
+
+    /**
+     * 同步方法:群发简单文本短信
+     *
+     * @param phones  目标手机号列表
+     * @param message 短信内容
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    @Override
+    public RemoteSms massTexting(List<String> phones, String message) {
+        // 调用 getSmsBlend 方法获取对应短信供应商的 SmsBlend 实例
+        SmsResponse smsResponse = getSmsBlend().massTexting(phones, message);
+        return getRemoteSms(smsResponse);
+    }
+
+    /**
+     * 同步方法:群发带参数的短信
+     *
+     * @param phones     目标手机号列表
+     * @param templateId 短信模板ID
+     * @param messages   短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @return 封装了短信发送结果的 RemoteSms 对象
+     */
+    @Override
+    public RemoteSms massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
+        // 调用 getSmsBlend 方法获取对应短信供应商的 SmsBlend 实例
+        SmsResponse smsResponse = getSmsBlend().massTexting(phones, templateId, messages);
+        return getRemoteSms(smsResponse);
+    }
+
+    /**
+     * 异步方法:发送简单文本短信
+     *
+     * @param phone   目标手机号
+     * @param message 短信内容
+     */
+    @Override
+    public void sendMessageAsync(String phone, String message) {
+        getSmsBlend().sendMessageAsync(phone, message);
+    }
+
+    /**
+     * 异步方法:发送带参数的短信
+     *
+     * @param phone      目标手机号
+     * @param templateId 短信模板ID
+     * @param messages   短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     */
+    @Override
+    public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
+        getSmsBlend().sendMessageAsync(phone, templateId, messages);
+    }
+
+    /**
+     * 延迟发送简单文本短信
+     *
+     * @param phone       目标手机号
+     * @param message     短信内容
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    @Override
+    public void delayedMessage(String phone, String message, Long delayedTime) {
+        getSmsBlend().delayedMessage(phone, message, delayedTime);
+    }
+
+    /**
+     * 延迟发送带参数的短信
+     *
+     * @param phone       目标手机号
+     * @param templateId  短信模板ID
+     * @param messages    短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    @Override
+    public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
+        getSmsBlend().delayedMessage(phone, templateId, messages, delayedTime);
+    }
+
+    /**
+     * 延迟群发简单文本短信
+     *
+     * @param phones      目标手机号列表
+     * @param message     短信内容
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    @Override
+    public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
+        getSmsBlend().delayMassTexting(phones, message, delayedTime);
+    }
+
+    /**
+     * 延迟批量发送带参数的短信
+     *
+     * @param phones      目标手机号列表
+     * @param templateId  短信模板ID
+     * @param messages    短信模板参数,使用 LinkedHashMap 以保持参数顺序
+     * @param delayedTime 延迟发送时间(毫秒)
+     */
+    @Override
+    public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
+        getSmsBlend().delayMassTexting(phones, templateId, messages, delayedTime);
+    }
+
+    /**
+     * 加入黑名单
+     *
+     * @param phone 手机号
+     */
+    @Override
+    public void joinInBlacklist(String phone) {
+        getSmsBlend().joinInBlacklist(phone);
+    }
+
+    /**
+     * 加入黑名单
+     *
+     * @param phones 手机号列表
+     */
+    @Override
+    public void joinInBlacklist(List<String> phones) {
+        getSmsBlend().batchJoinBlacklist(phones);
+    }
+
+    /**
+     * 移除黑名单
+     *
+     * @param phone 手机号
+     */
+    @Override
+    public void removeFromBlacklist(String phone) {
+        getSmsBlend().removeFromBlacklist(phone);
+    }
+
+    /**
+     * 移除黑名单
+     *
+     * @param phones 手机号
+     */
+    @Override
+    public void removeFromBlacklist(List<String> phones) {
+        getSmsBlend().batchRemovalFromBlacklist(phones);
     }
 
 }