add 增加 ruoyi-sms 短信模块 整合 阿里云、腾讯云 短信功能
parent
781ae8d5c8
commit
e57d11d55a
@ -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>
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue