Browse Source

add 增加 ruoyi-sms 短信模块 整合 阿里云、腾讯云 短信功能

疯狂的狮子li 3 years ago
parent
commit
e57d11d55a

+ 24 - 0
pom.xml

@@ -50,6 +50,10 @@
         <qcloud.cos.version>5.6.72</qcloud.cos.version>
         <minio.version>8.3.8</minio.version>
 
+        <!-- SMS 配置 -->
+        <aliyun.sms.version>2.0.9</aliyun.sms.version>
+        <tencent.sms.version>3.1.500</tencent.sms.version>
+
         <!-- docker 配置 -->
         <docker.registry.url>localhost</docker.registry.url>
         <docker.registry.host>http://${docker.registry.url}:2375</docker.registry.host>
@@ -193,6 +197,18 @@
             </dependency>
 
             <dependency>
+                <groupId>com.aliyun</groupId>
+                <artifactId>dysmsapi20170525</artifactId>
+                <version>${aliyun.sms.version}</version>
+            </dependency>
+
+            <dependency>
+                <groupId>com.tencentcloudapi</groupId>
+                <artifactId>tencentcloud-sdk-java</artifactId>
+                <version>${tencent.sms.version}</version>
+            </dependency>
+
+            <dependency>
                 <groupId>de.codecentric</groupId>
                 <artifactId>spring-boot-admin-starter-server</artifactId>
                 <version>${spring-boot-admin.version}</version>
@@ -297,6 +313,13 @@
                 <version>${ruoyi-vue-plus.version}</version>
             </dependency>
 
+            <!-- SMS短信模块 -->
+            <dependency>
+                <groupId>com.ruoyi</groupId>
+                <artifactId>ruoyi-sms</artifactId>
+                <version>${ruoyi-vue-plus.version}</version>
+            </dependency>
+
             <!-- demo模块 -->
             <dependency>
                 <groupId>com.ruoyi</groupId>
@@ -317,6 +340,7 @@
         <module>ruoyi-demo</module>
         <module>ruoyi-extend</module>
         <module>ruoyi-oss</module>
+        <module>ruoyi-sms</module>
     </modules>
     <packaging>pom</packaging>
 

+ 12 - 0
ruoyi-admin/src/main/resources/application-dev.yml

@@ -178,3 +178,15 @@ mail:
   timeout: 0
   # Socket连接超时值,单位毫秒,缺省值不超时
   connectionTimeout: 0
+
+--- # sms 短信
+sms:
+  enabled: false
+  # 阿里云 dysmsapi.aliyuncs.com
+  # 腾讯云 sms.tencentcloudapi.com
+  endpoint: "dysmsapi.aliyuncs.com"
+  accessKeyId: xxxxxxx
+  accessKeySecret: xxxxxx
+  signName: 测试
+  # 腾讯专用
+  sdkAppId:

+ 12 - 0
ruoyi-admin/src/main/resources/application-prod.yml

@@ -181,3 +181,15 @@ mail:
   timeout: 0
   # Socket连接超时值,单位毫秒,缺省值不超时
   connectionTimeout: 0
+
+--- # sms 短信
+sms:
+  enabled: false
+  # 阿里云 dysmsapi.aliyuncs.com
+  # 腾讯云 sms.tencentcloudapi.com
+  endpoint: "dysmsapi.aliyuncs.com"
+  accessKeyId: xxxxxxx
+  accessKeySecret: xxxxxx
+  signName: 测试
+  # 腾讯专用
+  sdkAppId:

+ 40 - 0
ruoyi-sms/pom.xml

@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>ruoyi-vue-plus</artifactId>
+        <groupId>com.ruoyi</groupId>
+        <version>4.1.0</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+
+    <artifactId>ruoyi-sms</artifactId>
+
+    <description>
+        SMS短信模块
+    </description>
+
+    <dependencies>
+
+        <!-- 通用工具-->
+        <dependency>
+            <groupId>com.ruoyi</groupId>
+            <artifactId>ruoyi-common</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>com.aliyun</groupId>
+            <artifactId>dysmsapi20170525</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>com.tencentcloudapi</groupId>
+            <artifactId>tencentcloud-sdk-java</artifactId>
+            <optional>true</optional>
+        </dependency>
+
+    </dependencies>
+
+</project>

+ 44 - 0
ruoyi-sms/src/main/java/com/ruoyi/sms/config/SmsConfig.java

@@ -0,0 +1,44 @@
+package com.ruoyi.sms.config;
+
+import com.ruoyi.sms.config.properties.SmsProperties;
+import com.ruoyi.sms.core.AliyunSmsTemplate;
+import com.ruoyi.sms.core.SmsTemplate;
+import com.ruoyi.sms.core.TencentSmsTemplate;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * 短信配置类
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+@Configuration
+@ConditionalOnProperty(value = "sms.enabled", havingValue = "true")
+public class SmsConfig {
+
+    @Configuration
+    @ConditionalOnClass(com.aliyun.dysmsapi20170525.Client.class)
+    static class AliyunSmsConfig {
+
+        @Bean
+        public SmsTemplate aliyunSmsTemplate(SmsProperties smsProperties) {
+            return new AliyunSmsTemplate(smsProperties);
+        }
+
+    }
+
+    @Configuration
+    @ConditionalOnClass(com.tencentcloudapi.sms.v20190711.SmsClient.class)
+    static class TencentSmsConfig {
+
+        @Bean
+        public SmsTemplate tencentSmsTemplate(SmsProperties smsProperties) {
+            return new TencentSmsTemplate(smsProperties);
+        }
+
+    }
+
+}

+ 47 - 0
ruoyi-sms/src/main/java/com/ruoyi/sms/config/properties/SmsProperties.java

@@ -0,0 +1,47 @@
+package com.ruoyi.sms.config.properties;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * SMS短信 配置属性
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+@Data
+@Component
+@ConfigurationProperties(prefix = "sms")
+public class SmsProperties {
+
+    private Boolean enabled;
+
+    /**
+     * 配置节点
+     * 阿里云 dysmsapi.aliyuncs.com
+     * 腾讯云 sms.tencentcloudapi.com
+     */
+    private String endpoint;
+
+    /**
+     * key
+     */
+    private String accessKeyId;
+
+    /**
+     * 密匙
+     */
+    private String accessKeySecret;
+
+    /*
+     * 短信签名
+     */
+    private String signName;
+
+    /**
+     * 短信应用ID (腾讯专属)
+     */
+    private String sdkAppId;
+
+}

+ 65 - 0
ruoyi-sms/src/main/java/com/ruoyi/sms/core/AliyunSmsTemplate.java

@@ -0,0 +1,65 @@
+package com.ruoyi.sms.core;
+
+import com.aliyun.dysmsapi20170525.Client;
+import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
+import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
+import com.aliyun.teaopenapi.models.Config;
+import com.ruoyi.common.utils.JsonUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.sms.config.properties.SmsProperties;
+import com.ruoyi.sms.entity.SmsResult;
+import com.ruoyi.sms.exception.SmsException;
+import lombok.SneakyThrows;
+
+import java.util.Map;
+
+/**
+ * Aliyun 短信模板
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+public class AliyunSmsTemplate implements SmsTemplate {
+
+    private SmsProperties properties;
+
+    private Client client;
+
+    @SneakyThrows(Exception.class)
+    public AliyunSmsTemplate(SmsProperties smsProperties) {
+        this.properties = smsProperties;
+        Config config = new Config()
+            // 您的AccessKey ID
+            .setAccessKeyId(smsProperties.getAccessKeyId())
+            // 您的AccessKey Secret
+            .setAccessKeySecret(smsProperties.getAccessKeySecret())
+            // 访问的域名
+            .setEndpoint(smsProperties.getEndpoint());
+        this.client = new Client(config);
+    }
+
+    public SmsResult send(String phones, String templateId, Map<String, String> param) {
+        if (StringUtils.isBlank(phones)) {
+            throw new SmsException("手机号不能为空");
+        }
+        if (StringUtils.isBlank(templateId)) {
+            throw new SmsException("模板ID不能为空");
+        }
+        SendSmsRequest req = new SendSmsRequest()
+            .setPhoneNumbers(phones)
+            .setSignName(properties.getSignName())
+            .setTemplateCode(templateId)
+            .setTemplateParam(JsonUtils.toJsonString(param));
+        try {
+            SendSmsResponse resp = client.sendSms(req);
+            return SmsResult.builder()
+                .isSuccess("OK".equals(resp.getBody().getCode()))
+                .message(resp.getBody().getMessage())
+                .response(resp)
+                .build();
+        } catch (Exception e) {
+            throw new SmsException(e.getMessage());
+        }
+    }
+
+}

+ 26 - 0
ruoyi-sms/src/main/java/com/ruoyi/sms/core/SmsTemplate.java

@@ -0,0 +1,26 @@
+package com.ruoyi.sms.core;
+
+import com.ruoyi.sms.entity.SmsResult;
+
+import java.util.Map;
+
+/**
+ * 短信模板
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+public interface SmsTemplate {
+
+    /**
+     * 发送短信
+     *
+     * @param phones     电话号(多个逗号分割)
+     * @param templateId 模板id
+     * @param param      模板对应参数
+     *                   阿里 需使用 模板变量名称对应内容 例如: code=1234
+     *                   腾讯 需使用 模板变量顺序对应内容 例如: 1=1234, 1为模板内第一个参数
+     */
+    SmsResult send(String phones, String templateId, Map<String, String> param);
+
+}

+ 80 - 0
ruoyi-sms/src/main/java/com/ruoyi/sms/core/TencentSmsTemplate.java

@@ -0,0 +1,80 @@
+package com.ruoyi.sms.core;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ArrayUtil;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.sms.config.properties.SmsProperties;
+import com.ruoyi.sms.entity.SmsResult;
+import com.ruoyi.sms.exception.SmsException;
+import com.tencentcloudapi.common.Credential;
+import com.tencentcloudapi.common.profile.ClientProfile;
+import com.tencentcloudapi.common.profile.HttpProfile;
+import com.tencentcloudapi.sms.v20190711.SmsClient;
+import com.tencentcloudapi.sms.v20190711.models.SendSmsRequest;
+import com.tencentcloudapi.sms.v20190711.models.SendSmsResponse;
+import com.tencentcloudapi.sms.v20190711.models.SendStatus;
+import lombok.SneakyThrows;
+
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Tencent 短信模板
+ *
+ * @author Lion Li
+ * @version 4.2.0
+ */
+public class TencentSmsTemplate implements SmsTemplate {
+
+    private SmsProperties properties;
+
+    private SmsClient client;
+
+    @SneakyThrows(Exception.class)
+    public TencentSmsTemplate(SmsProperties smsProperties) {
+        this.properties = smsProperties;
+        Credential credential = new Credential(smsProperties.getAccessKeyId(), smsProperties.getAccessKeySecret());
+        HttpProfile httpProfile = new HttpProfile();
+        httpProfile.setEndpoint(smsProperties.getEndpoint());
+        ClientProfile clientProfile = new ClientProfile();
+        clientProfile.setHttpProfile(httpProfile);
+        this.client = new SmsClient(credential, "", clientProfile);
+    }
+
+    public SmsResult send(String phones, String templateId, Map<String, String> param) {
+        if (StringUtils.isBlank(phones)) {
+            throw new SmsException("手机号不能为空");
+        }
+        if (StringUtils.isBlank(templateId)) {
+            throw new SmsException("模板ID不能为空");
+        }
+        SendSmsRequest req = new SendSmsRequest();
+        Set<String> set = Arrays.stream(phones.split(",")).map(p -> "+86" + p).collect(Collectors.toSet());
+        req.setPhoneNumberSet(ArrayUtil.toArray(set, String.class));
+        if (CollUtil.isNotEmpty(param)) {
+            req.setTemplateParamSet(ArrayUtil.toArray(param.values(), String.class));
+        }
+        req.setTemplateID(templateId);
+        req.setSign(properties.getSignName());
+        req.setSmsSdkAppid(properties.getSdkAppId());
+        try {
+            SendSmsResponse resp = client.SendSms(req);
+            SmsResult.SmsResultBuilder builder = SmsResult.builder()
+                .isSuccess(true)
+                .message("send success")
+                .response(resp);
+            for (SendStatus sendStatus : resp.getSendStatusSet()) {
+                if (!"Ok".equals(sendStatus.getCode())) {
+                    builder.isSuccess(false).message(sendStatus.getMessage());
+                    break;
+                }
+            }
+            return builder.build();
+        } catch (Exception e) {
+            throw new SmsException(e.getMessage());
+        }
+    }
+
+}

+ 29 - 0
ruoyi-sms/src/main/java/com/ruoyi/sms/entity/SmsResult.java

@@ -0,0 +1,29 @@
+package com.ruoyi.sms.entity;
+
+import lombok.Builder;
+import lombok.Data;
+
+/**
+ * 上传返回体
+ *
+ * @author Lion Li
+ */
+@Data
+@Builder
+public class SmsResult {
+
+    /**
+     * 是否成功
+     */
+    private boolean isSuccess;
+
+    /**
+     * 响应消息
+     */
+    private String message;
+
+    /**
+     * 实际响应体
+     */
+    private Object response;
+}

+ 16 - 0
ruoyi-sms/src/main/java/com/ruoyi/sms/exception/SmsException.java

@@ -0,0 +1,16 @@
+package com.ruoyi.sms.exception;
+
+/**
+ * Sms异常类
+ *
+ * @author Lion Li
+ */
+public class SmsException extends RuntimeException {
+
+    private static final long serialVersionUID = 1L;
+
+    public SmsException(String msg) {
+        super(msg);
+    }
+
+}