diff --git a/pom.xml b/pom.xml
index 08dfa3a..699c78a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -61,6 +61,8 @@
2.48.1
3.3.5
+
+ 1.0.12
1.8.9
2.16.1.2
@@ -259,6 +261,20 @@
${sms4j.version}
+
+
+ com.aliyun
+ dysmsapi20180501
+ ${aliyun-dysmsapi.version}
+
+
+
+
+ com.aliyun
+ tea-openapi
+ 0.3.10
+
+
de.codecentric
diff --git a/ruoyi-admin/pom.xml b/ruoyi-admin/pom.xml
index 3377ef2..a608da7 100644
--- a/ruoyi-admin/pom.xml
+++ b/ruoyi-admin/pom.xml
@@ -62,6 +62,18 @@
ruoyi-common-mail
+
+
+ org.dromara
+ ruoyi-common-sms
+
+
+
+
+ com.aliyun
+ dysmsapi20180501
+
+
org.dromara
diff --git a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
index 9844b3f..d9a34cb 100644
--- a/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
+++ b/ruoyi-admin/src/main/java/org/dromara/web/controller/CaptchaController.java
@@ -6,6 +6,7 @@ import cn.hutool.captcha.generator.MathGenerator;
import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
+import com.aliyun.dysmsapi20180501.models.SendMessageToGlobeResponseBody;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -21,6 +22,7 @@ import org.dromara.common.mail.core.MailBuilder;
import org.dromara.common.redis.annotation.RateLimiter;
import org.dromara.common.redis.enums.LimitType;
import org.dromara.common.redis.utils.RedisUtils;
+import org.dromara.common.sms.core.AliyunInternationalSmsService;
import org.dromara.common.web.config.properties.CaptchaProperties;
import org.dromara.common.web.core.WaveAndCircleCaptcha;
import org.dromara.sms4j.api.SmsBlend;
@@ -51,6 +53,7 @@ public class CaptchaController {
private final CaptchaProperties captchaProperties;
private final MailProperties mailProperties;
+ private final AliyunInternationalSmsService aliyunInternationalSmsService;
/**
* 发送短信验证码。
@@ -80,6 +83,33 @@ public class CaptchaController {
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
return R.ok();
}
+ /**
+ * 发送国际短信(阿里云国际短信包,向境外号码发送)
+ *
+ * @param phoneNumber 国际号码,带国家码不含 +/00,例如 62895111666
+ */
+ @GetMapping("/sendInternational")
+ public R
+
+
+ com.aliyun
+ dysmsapi20180501
+
+
+
+
+ com.aliyun
+ tea-openapi
+
+
org.dromara
ruoyi-common-redis
+
+ com.mysql
+ mysql-connector-j
+
diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/AliyunInternationalSmsAutoConfiguration.java b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/AliyunInternationalSmsAutoConfiguration.java
new file mode 100644
index 0000000..65b8294
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/AliyunInternationalSmsAutoConfiguration.java
@@ -0,0 +1,37 @@
+package org.dromara.common.sms.config;
+
+import com.aliyun.dysmsapi20180501.Client;
+import com.aliyun.teaopenapi.models.Config;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.autoconfigure.AutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * 阿里云国际短信自动配置
+ *
+ * @author ruoyi
+ */
+@Slf4j
+@AutoConfiguration
+@EnableConfigurationProperties(AliyunInternationalSmsProperties.class)
+@ConditionalOnProperty(prefix = "aliyun.international-sms", name = "enabled", havingValue = "true")
+public class AliyunInternationalSmsAutoConfiguration {
+
+ /**
+ * 构建阿里云国际短信客户端
+ *
+ * @param properties 国际短信配置
+ * @return 短信客户端
+ */
+ @Bean
+ public Client dysmsapi20180501Client(AliyunInternationalSmsProperties properties) throws Exception {
+ Config config = new Config()
+ .setAccessKeyId(properties.getAccessKeyId())
+ .setAccessKeySecret(properties.getAccessKeySecret());
+ config.endpoint = properties.getEndpoint();
+ return new Client(config);
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/AliyunInternationalSmsProperties.java b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/AliyunInternationalSmsProperties.java
new file mode 100644
index 0000000..595ee20
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/config/AliyunInternationalSmsProperties.java
@@ -0,0 +1,40 @@
+package org.dromara.common.sms.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * 阿里云国际短信配置属性
+ *
+ * @author ruoyi
+ */
+@Data
+@ConfigurationProperties(prefix = "aliyun.international-sms")
+public class AliyunInternationalSmsProperties {
+
+ /**
+ * 是否启用
+ */
+ private Boolean enabled = false;
+
+ /**
+ * 阿里云国际站 AccessKeyId
+ */
+ private String accessKeyId;
+
+ /**
+ * 阿里云国际站 AccessKeySecret
+ */
+ private String accessKeySecret;
+
+ /**
+ * 接入点,国际短信固定为 dysmsapi.ap-southeast-1.aliyuncs.com
+ */
+ private String endpoint = "dysmsapi.ap-southeast-1.aliyuncs.com";
+
+ /**
+ * 发件人标识 SenderId(部分国家/地区需报备)
+ */
+ private String senderId;
+
+}
diff --git a/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/AliyunInternationalSmsService.java b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/AliyunInternationalSmsService.java
new file mode 100644
index 0000000..948c9ae
--- /dev/null
+++ b/ruoyi-common/ruoyi-common-sms/src/main/java/org/dromara/common/sms/core/AliyunInternationalSmsService.java
@@ -0,0 +1,60 @@
+package org.dromara.common.sms.core;
+
+import com.aliyun.dysmsapi20180501.Client;
+import com.aliyun.dysmsapi20180501.models.SendMessageToGlobeRequest;
+import com.aliyun.dysmsapi20180501.models.SendMessageToGlobeResponse;
+import com.aliyun.dysmsapi20180501.models.SendMessageToGlobeResponseBody;
+import com.aliyun.teautil.models.RuntimeOptions;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.exception.ServiceException;
+import org.dromara.common.sms.config.AliyunInternationalSmsProperties;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+/**
+ * 阿里云国际短信发送服务
+ *
+ * @author ruoyi
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class AliyunInternationalSmsService {
+
+ private final Client dysmsapi20180501Client;
+ private final AliyunInternationalSmsProperties properties;
+
+ /**
+ * 向境外手机号发送短信
+ *
+ * @param to 国际号码,带国家码且不含 + / 00 前缀,例如 62895111666
+ * @param message 短信内容(国际短信可自由编辑)
+ * @return 发送结果
+ */
+ public SendMessageToGlobeResponseBody sendMessageToGlobe(String to, String message) {
+ log.info("国际短信发送开始, 手机号={}, message={}", to, message);
+ SendMessageToGlobeRequest request = new SendMessageToGlobeRequest()
+ .setTo(to)
+ .setMessage("您的验证码为:" + message + ",请勿泄露于他人!");
+ if (StringUtils.hasText(properties.getSenderId())) {
+ request.setChannelId(properties.getSenderId());
+ }
+ try {
+ SendMessageToGlobeResponse response = dysmsapi20180501Client
+ .sendMessageToGlobeWithOptions(request, new RuntimeOptions());
+ SendMessageToGlobeResponseBody body = response.getBody();
+ if (body == null || !"OK".equalsIgnoreCase(body.getResponseCode())) {
+ log.error("国际短信发送失败, to={}, body={}", to, body);
+ throw new ServiceException("国际短信发送失败:" + (body == null ? "无响应" : body.getResponseCode()));
+ }
+ return body;
+ } catch (ServiceException e) {
+ throw e;
+ } catch (Exception e) {
+ log.error("国际短信发送异常, to={}", to, e);
+ throw new ServiceException("国际短信发送异常:" + e.getMessage());
+ }
+ }
+
+}
diff --git a/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
index 5919ce3..6bc495b 100644
--- a/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
+++ b/ruoyi-common/ruoyi-common-sms/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
@@ -1 +1,2 @@
org.dromara.common.sms.config.SmsAutoConfiguration
+org.dromara.common.sms.config.AliyunInternationalSmsAutoConfiguration