疯狂的狮子li
2022-05-06 e57d11d55a95ceafc793f714710d488bb1afc585
add 增加 ruoyi-sms 短信模块 整合 阿里云、腾讯云 短信功能
已添加8个文件
已修改3个文件
395 ■■■■■ 文件已修改
pom.xml 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-dev.yml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-admin/src/main/resources/application-prod.yml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/pom.xml 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/config/SmsConfig.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/config/properties/SmsProperties.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/core/AliyunSmsTemplate.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/core/SmsTemplate.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/core/TencentSmsTemplate.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/entity/SmsResult.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-sms/src/main/java/com/ruoyi/sms/exception/SmsException.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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>
ruoyi-admin/src/main/resources/application-dev.yml
@@ -178,3 +178,15 @@
  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:
ruoyi-admin/src/main/resources/application-prod.yml
@@ -181,3 +181,15 @@
  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:
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>
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);
        }
    }
}
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;
}
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());
        }
    }
}
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);
}
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());
        }
    }
}
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;
}
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);
    }
}