diff --git a/pom.xml b/pom.xml
index 46fe23d182..ccc6b21da3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,6 +43,7 @@
3.17.4
4.40.54.ALL
4.2.2
+ 4.0.1
@@ -266,6 +267,12 @@
${aliyun-imagesearch.version}
+
+ com.aliyun
+ alibabacloud-dysmsapi20170525
+ ${alibabacloud-dysmsapi.version}
+
+
co.elastic.clients
diff --git a/ruoyi-common/pom.xml b/ruoyi-common/pom.xml
index 99ead9a186..f2dcaa1611 100644
--- a/ruoyi-common/pom.xml
+++ b/ruoyi-common/pom.xml
@@ -50,6 +50,11 @@
imagesearch20201214
+
+ com.aliyun
+ alibabacloud-dysmsapi20170525
+
+
com.baomidou
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/SmsClientWrapper.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/SmsClientWrapper.java
new file mode 100644
index 0000000000..9ee646eaa1
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/SmsClientWrapper.java
@@ -0,0 +1,34 @@
+package com.ruoyi.framework.sms;
+
+import com.ruoyi.framework.sms.ali.AliSmsServer;
+import com.ruoyi.framework.sms.ali.entity.AliSmsResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Slf4j
+@Component
+public class SmsClientWrapper {
+
+ @Autowired
+ private AliSmsServer aliSmsServer;
+
+ @Value("${sms.send:true}")
+ private Boolean doSend;
+
+ public boolean sendSms(String signName, String phoneNumber, String templateCode, String templateParams) {
+ boolean sendResult;
+ if (doSend) {
+ AliSmsResponse response = aliSmsServer.sendSms(signName, phoneNumber, templateCode, templateParams);
+ sendResult = response.success();
+ }
+ // 测试的时候不发短信
+ else {
+ sendResult = true;
+ }
+ log.info("发送短信{},{},{},{}:{},doSend:{}", signName, phoneNumber, templateCode, templateParams, sendResult, doSend);
+ return sendResult;
+ }
+
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/SmsConfiguration.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/SmsConfiguration.java
new file mode 100644
index 0000000000..270a8cb6e7
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/SmsConfiguration.java
@@ -0,0 +1,26 @@
+package com.ruoyi.framework.sms;
+
+import com.ruoyi.framework.sms.ali.AliSmsServer;
+import lombok.Getter;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Getter
+@Configuration
+public class SmsConfiguration {
+
+ @Value("${sms.accessKeyId:}")
+ private String accessKeyId;
+
+ @Value("${sms.accessKeySecret:}")
+ private String accessKeySecret;
+
+ @Value("${sms.regionId:}")
+ private String regionId;
+
+ @Bean
+ public AliSmsServer aliSmsServer() {
+ return new AliSmsServer(accessKeyId, accessKeySecret, regionId);
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/AliSmsServer.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/AliSmsServer.java
new file mode 100644
index 0000000000..628f227e7c
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/AliSmsServer.java
@@ -0,0 +1,116 @@
+package com.ruoyi.framework.sms.ali;
+
+import com.alibaba.fastjson2.JSON;
+import com.aliyuncs.CommonRequest;
+import com.aliyuncs.CommonResponse;
+import com.aliyuncs.DefaultAcsClient;
+import com.aliyuncs.IAcsClient;
+import com.aliyuncs.http.MethodType;
+import com.aliyuncs.profile.DefaultProfile;
+import com.aliyuncs.profile.IClientProfile;
+import com.ruoyi.framework.sms.ali.entity.AliSmsResponse;
+import com.ruoyi.framework.sms.ali.entity.Result;
+import com.ruoyi.framework.sms.ali.entity.TemplateInfo;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.List;
+
+/**
+ * 阿里短信服务
+ * 官方文档地址 https://help.aliyun.com/document_detail/121206.html
+ *
+ * @author liangyq
+ */
+@Slf4j
+public class AliSmsServer {
+
+ private IAcsClient client;
+
+ private String accessKeyId;
+
+ public AliSmsServer(String accessKeyId, String accessKeySecret, String regionId) {
+ IClientProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
+ client = new DefaultAcsClient(profile);
+ this.accessKeyId = accessKeyId;
+ }
+
+ /**
+ * 发送短信
+ *
+ * @param signName 短信签名
+ * @param phoneNumber 手机号
+ * @param templateCode 模版编号
+ * @param templateParams 模版参数(json)
+ */
+ public AliSmsResponse sendSms(String signName, String phoneNumber, String templateCode, String templateParams) {
+ try {
+ CommonRequest request = newCommonRequest("SendSms");
+ request.putQueryParameter("PhoneNumbers", phoneNumber);
+ request.putQueryParameter("SignName", signName);
+ request.putQueryParameter("TemplateCode", templateCode);
+ request.putQueryParameter("TemplateParam", templateParams);
+ String params = JSON.toJSONString(request);
+ CommonResponse response = client.getCommonResponse(request);
+ return new AliSmsResponse(params, response);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * 批量发短信 最多100个手机号
+ *
+ * @param signNameList 短信签名
+ * @param phoneNumberList 手机号
+ * @param templateCode 模版编号
+ * @param templateParamList 模版参数 json
+ */
+ public Result sendBatchSms(List signNameList, List phoneNumberList, String templateCode, List templateParamList) {
+ try {
+ CommonRequest request = newCommonRequest("SendBatchSms");
+ request.putQueryParameter("PhoneNumberJson", JSON.toJSONString(phoneNumberList));
+ request.putQueryParameter("SignNameJson", JSON.toJSONString(signNameList));
+ request.putQueryParameter("TemplateCode", templateCode);
+ request.putQueryParameter("TemplateParamJson", JSON.toJSONString(templateParamList));
+
+ CommonResponse response = client.getCommonResponse(request);
+ return handlerResponse(response);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public TemplateInfo querySmsTemplate(String templateCode) {
+ CommonRequest request = newCommonRequest("QuerySmsTemplate");
+ request.putQueryParameter("TemplateCode", templateCode);
+ request.putQueryParameter("AccessKeyId", this.accessKeyId);
+ try {
+ CommonResponse response = client.getCommonResponse(request);
+ if (response.getHttpResponse().isSuccess()) {
+ return JSON.parseObject(response.getData(), TemplateInfo.class);
+ }
+ return null;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+ private CommonRequest newCommonRequest(String action) {
+ CommonRequest request = new CommonRequest();
+ request.setSysMethod(MethodType.POST);
+ request.setSysDomain("dysmsapi.aliyuncs.com");
+ request.setSysVersion("2017-05-25");
+ request.setSysAction(action);
+ return request;
+ }
+
+ private Result handlerResponse(CommonResponse response) {
+ if (!response.getHttpResponse().isSuccess()) {
+ return new Result("异常");
+ }
+ log.info(response.getData());
+ return JSON.parseObject(response.getData(), Result.class);
+ }
+
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/AliSmsResponse.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/AliSmsResponse.java
new file mode 100644
index 0000000000..79b707e842
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/AliSmsResponse.java
@@ -0,0 +1,34 @@
+package com.ruoyi.framework.sms.ali.entity;
+
+import com.alibaba.fastjson2.JSON;
+import com.aliyuncs.CommonResponse;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * @author liangyq
+ */
+@Slf4j
+@Data
+public class AliSmsResponse extends Response {
+
+ private CommonResponse response;
+
+ public AliSmsResponse(String params, CommonResponse response) {
+ super("ALI-SMS", "ALI", params, JSON.toJSONString(response));
+ this.response = response;
+ }
+
+ @Override
+ public boolean success() {
+ return response.getHttpResponse().isSuccess();
+ }
+
+ public Result get() {
+ if (!response.getHttpResponse().isSuccess()) {
+ return new Result("异常");
+ }
+ log.info(response.getData());
+ return JSON.parseObject(response.getData(), Result.class);
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/Response.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/Response.java
new file mode 100644
index 0000000000..426d6c7087
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/Response.java
@@ -0,0 +1,74 @@
+package com.ruoyi.framework.sms.ali.entity;
+
+import cn.hutool.core.lang.Assert;
+import lombok.Getter;
+
+/**
+ * @author liangyq
+ */
+@Getter
+public abstract class Response {
+ /**
+ * api名称
+ */
+ private final String apiName;
+ /**
+ * 平台信息
+ */
+ private final String platform;
+ /**
+ * 参数
+ */
+ private final String params;
+ /**
+ * 返回信息
+ */
+ private final String responseText;
+ /**
+ * 结果记录数
+ */
+ private int count;
+
+ public Response(String apiName, String platform, String params, String responseText) {
+ Assert.notEmpty(apiName);
+ Assert.notNull(platform);
+ this.apiName = apiName;
+ this.platform = platform;
+ this.responseText = responseText;
+ this.params = params;
+ }
+
+ /**
+ * @return 是否成功
+ */
+ public abstract boolean success();
+
+ /**
+ * @return 是否调用的是本地历史结果(默认否)
+ */
+ public boolean isLocalCall() {
+ return false;
+ }
+
+ /**
+ * 增加结果记录数
+ */
+ public void increaseCount() {
+ this.count++;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ @Override
+ public String toString() {
+ return "Response{" +
+ "apiName='" + apiName + '\'' +
+ ", platform='" + platform + '\'' +
+ ", params='" + params + '\'' +
+ ", responseText='" + responseText + '\'' +
+ ", count=" + count +
+ '}';
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/Result.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/Result.java
new file mode 100644
index 0000000000..04154b802f
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/Result.java
@@ -0,0 +1,23 @@
+package com.ruoyi.framework.sms.ali.entity;
+
+import lombok.Data;
+
+@Data
+public class Result {
+
+ private String code;
+
+ private String message;
+
+ public Result() {
+
+ }
+
+ public Result(String message) {
+ this.message = message;
+ }
+
+ public boolean isOk() {
+ return "OK".equals(code);
+ }
+}
diff --git a/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/TemplateInfo.java b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/TemplateInfo.java
new file mode 100644
index 0000000000..38fb578d96
--- /dev/null
+++ b/ruoyi-framework/src/main/java/com/ruoyi/framework/sms/ali/entity/TemplateInfo.java
@@ -0,0 +1,24 @@
+package com.ruoyi.framework.sms.ali.entity;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * 短信模板信息
+ *
+ * @author liangyq
+ */
+@Data
+public class TemplateInfo {
+ private String templateCode;
+ private String templateContent;
+ private String templateName;
+ private Integer templateType;
+ private Date createDate;
+ private String reason;
+ /**
+ * 审核状态[0:审核中,1:通过,2:审核失败]
+ */
+ private Integer templateStatus;
+}