parent
60460eb046
commit
2d2e1b3921
@ -0,0 +1,34 @@
|
|||||||
|
package com.ruoyi.common.config;
|
||||||
|
|
||||||
|
import com.ruoyi.common.config.properties.SmsProperties;
|
||||||
|
import com.ruoyi.common.core.sms.AliyunSmsTemplate;
|
||||||
|
import com.ruoyi.common.core.sms.SmsTemplate;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信配置类
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
* @version 4.2.0
|
||||||
|
*/
|
||||||
|
@EnableConfigurationProperties(SmsProperties.class)
|
||||||
|
public class SmsConfig {
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnProperty(value = "sms.enabled", havingValue = "true")
|
||||||
|
@ConditionalOnClass(com.aliyun.dysmsapi20170525.Client.class)
|
||||||
|
static class AliyunSmsConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SmsTemplate aliyunSmsTemplate(SmsProperties smsProperties) {
|
||||||
|
return new AliyunSmsTemplate(smsProperties);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
package com.ruoyi.common.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
|
||||||
|
@ConfigurationProperties(prefix = "sms")
|
||||||
|
@Component
|
||||||
|
public class SmsProperties {
|
||||||
|
|
||||||
|
private Boolean enabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置节点
|
||||||
|
* 阿里云 dysmsapi.aliyuncs.com
|
||||||
|
*/
|
||||||
|
private String endpoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* key
|
||||||
|
*/
|
||||||
|
private String accessKeyId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 密匙
|
||||||
|
*/
|
||||||
|
private String accessKeySecret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 短信签名
|
||||||
|
*/
|
||||||
|
private String signName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短信应用ID (腾讯专属)
|
||||||
|
*/
|
||||||
|
private String sdkAppId;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
package com.ruoyi.common.core.domain.model;
|
||||||
|
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传返回体
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Builder
|
||||||
|
public class SmsResult {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否成功
|
||||||
|
*/
|
||||||
|
private boolean isSuccess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应消息
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 实际响应体
|
||||||
|
* <p>
|
||||||
|
* 可自行转换为 SDK 对应的 SendSmsResponse
|
||||||
|
*/
|
||||||
|
private String response;
|
||||||
|
}
|
||||||
@ -0,0 +1,71 @@
|
|||||||
|
package com.ruoyi.common.core.sms;
|
||||||
|
|
||||||
|
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.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.ruoyi.common.config.properties.SmsProperties;
|
||||||
|
import com.ruoyi.common.core.domain.model.SmsResult;
|
||||||
|
import com.ruoyi.common.exception.sms.SmsException;
|
||||||
|
import com.ruoyi.common.utils.JsonUtils;
|
||||||
|
import com.ruoyi.common.utils.StringUtils;
|
||||||
|
import lombok.SneakyThrows;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aliyun 短信模板
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
* @version 4.2.0
|
||||||
|
*/
|
||||||
|
public class AliyunSmsTemplate implements SmsTemplate {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
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(JsonUtils.toJsonString(resp))
|
||||||
|
.build();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SmsException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
package com.ruoyi.common.core.sms;
|
||||||
|
|
||||||
|
import com.ruoyi.common.core.domain.model.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,17 @@
|
|||||||
|
package com.ruoyi.common.exception.sms;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sms异常类
|
||||||
|
*
|
||||||
|
* @author Lion Li
|
||||||
|
*/
|
||||||
|
public class SmsException extends RuntimeException {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
public SmsException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.lang.Dict;
|
||||||
|
import cn.hutool.core.util.ArrayUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
|
||||||
|
import com.ruoyi.common.utils.spring.SpringUtils;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON 工具类
|
||||||
|
*
|
||||||
|
* @author 芋道源码
|
||||||
|
*/
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class JsonUtils {
|
||||||
|
|
||||||
|
private static final ObjectMapper OBJECT_MAPPER = SpringUtils.getBean(ObjectMapper.class);
|
||||||
|
|
||||||
|
public static ObjectMapper getObjectMapper() {
|
||||||
|
return OBJECT_MAPPER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toJsonString(Object object) {
|
||||||
|
if (ObjectUtil.isNull(object)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.writeValueAsString(object);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(String text, Class<T> clazz) {
|
||||||
|
if (StringUtils.isEmpty(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.readValue(text, clazz);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(byte[] bytes, Class<T> clazz) {
|
||||||
|
if (ArrayUtil.isEmpty(bytes)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.readValue(bytes, clazz);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T parseObject(String text, TypeReference<T> typeReference) {
|
||||||
|
if (StringUtils.isBlank(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.readValue(text, typeReference);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Dict parseMap(String text) {
|
||||||
|
if (StringUtils.isBlank(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructType(Dict.class));
|
||||||
|
} catch (MismatchedInputException e) {
|
||||||
|
// 类型不匹配说明不是json
|
||||||
|
return null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Dict> parseArrayMap(String text) {
|
||||||
|
if (StringUtils.isBlank(text)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, Dict.class));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> parseArray(String text, Class<T> clazz) {
|
||||||
|
if (StringUtils.isEmpty(text)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return OBJECT_MAPPER.readValue(text, OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package com.ruoyi.common.utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Author: czc
|
||||||
|
* @Description: TODO
|
||||||
|
* @DateTime: 2023/6/16 17:50
|
||||||
|
**/
|
||||||
|
public class SmsUtils {
|
||||||
|
|
||||||
|
public static String createRandom(boolean numberFlag, int length) {
|
||||||
|
String retStr = "";
|
||||||
|
String strTable = numberFlag ? "1234567890" : "1234567890abcdefghijkmnpqrstuvwxyz";
|
||||||
|
int len = strTable.length();
|
||||||
|
boolean bDone = true;
|
||||||
|
do {
|
||||||
|
retStr = "";
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
double dblR = Math.random() * len;
|
||||||
|
int intR = (int) Math.floor(dblR);
|
||||||
|
char c = strTable.charAt(intR);
|
||||||
|
if (('0' <= c) && (c <= '9')) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
retStr += strTable.charAt(intR);
|
||||||
|
}
|
||||||
|
if (count >= 2) {
|
||||||
|
bDone = false;
|
||||||
|
}
|
||||||
|
} while (bDone);
|
||||||
|
return retStr;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in new issue