mirror of
https://gitee.com/dromara/sms4j.git
synced 2025-12-06 17:08:40 +08:00
添加阿里云国内短信支持
This commit is contained in:
parent
2d9892f1f6
commit
d874a4b633
84
pom.xml
84
pom.xml
@ -49,6 +49,9 @@
|
|||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
<spring.boot.version>2.7.10</spring.boot.version>
|
<spring.boot.version>2.7.10</spring.boot.version>
|
||||||
<modules.version>1.0.0</modules.version>
|
<modules.version>1.0.0</modules.version>
|
||||||
|
<aliyun.version>2.0.23</aliyun.version>
|
||||||
|
<json.version>2.0.15</json.version>
|
||||||
|
<okhttp.version>3.14.9</okhttp.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
@ -67,11 +70,6 @@
|
|||||||
<artifactId>spring-boot-starter</artifactId>
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
<version>${spring.boot.version}</version>
|
<version>${spring.boot.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>kim.wind</groupId>
|
|
||||||
<artifactId>sms-aggregation-api</artifactId>
|
|
||||||
<version>${modules.version}</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>kim.wind</groupId>
|
<groupId>kim.wind</groupId>
|
||||||
@ -96,11 +94,67 @@
|
|||||||
<artifactId>sms-aggregation-yunpian</artifactId>
|
<artifactId>sms-aggregation-yunpian</artifactId>
|
||||||
<version>${modules.version}</version>
|
<version>${modules.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>kim.wind</groupId>
|
<groupId>kim.wind</groupId>
|
||||||
<artifactId>sms-aggregation-spring-boot-starter</artifactId>
|
<artifactId>sms-aggregation-spring-boot-starter</artifactId>
|
||||||
<version>${modules.version}</version>
|
<version>${modules.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>kim.wind</groupId>
|
||||||
|
<artifactId>sms-aggregation-comm</artifactId>
|
||||||
|
<version>${modules.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>kim.wind</groupId>
|
||||||
|
<artifactId>sms-aggregation-api</artifactId>
|
||||||
|
<version>${modules.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 阿里云短信依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.aliyun</groupId>
|
||||||
|
<artifactId>dysmsapi20170525</artifactId>
|
||||||
|
<version>${aliyun.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- 阿里JSON解析器 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
<version>${json.version}</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- okhttp依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
<version>${okhttp.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--aop依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- redis 缓存操作 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
<version>${spring.boot.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
|
|
||||||
@ -112,10 +166,11 @@
|
|||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>kim.wind</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>sms-aggregation-comm</artifactId>
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
<version>${modules.version}</version>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<distributionManagement>
|
<distributionManagement>
|
||||||
@ -202,6 +257,19 @@
|
|||||||
<artifactId>maven-release-plugin</artifactId>
|
<artifactId>maven-release-plugin</artifactId>
|
||||||
<version>2.5.1</version>
|
<version>2.5.1</version>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
|
<version>3.2.2</version>
|
||||||
|
<configuration>
|
||||||
|
<archive>
|
||||||
|
<manifest>
|
||||||
|
<addDefaultImplementationEntries>true</addDefaultImplementationEntries>
|
||||||
|
</manifest>
|
||||||
|
</archive>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@ -11,13 +11,25 @@
|
|||||||
<artifactId>sms-aggregation-aliyun</artifactId>
|
<artifactId>sms-aggregation-aliyun</artifactId>
|
||||||
<name>sms-aggregation-aliyun</name>
|
<name>sms-aggregation-aliyun</name>
|
||||||
<description>sms-aggregation-aliyun</description>
|
<description>sms-aggregation-aliyun</description>
|
||||||
<version>${modules.version}</version>
|
<version>1.0.0</version>
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<!-- 阿里云短信依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.aliyun</groupId>
|
||||||
|
<artifactId>dysmsapi20170525</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>kim.wind</groupId>
|
||||||
|
<artifactId>sms-aggregation-comm</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>kim.wind</groupId>
|
||||||
|
<artifactId>sms-aggregation-api</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2,6 +2,8 @@ package kim.wind.sms.aliyun.config;
|
|||||||
|
|
||||||
import com.aliyun.dysmsapi20170525.Client;
|
import com.aliyun.dysmsapi20170525.Client;
|
||||||
import com.aliyun.teaopenapi.models.Config;
|
import com.aliyun.teaopenapi.models.Config;
|
||||||
|
import kim.wind.sms.aliyun.service.AlibabaSmsImpl;
|
||||||
|
import kim.wind.sms.api.SmsBlend;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
@ -10,10 +12,9 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties(prefix = "sms-blend.alibaba") //指定配置文件注入属性前缀
|
@ConfigurationProperties(prefix = "sms.alibaba") //指定配置文件注入属性前缀
|
||||||
@Data
|
@Data
|
||||||
@AutoConfigureAfter({SmsBlendMainConfig.class})
|
@ConditionalOnProperty(prefix = "sms", name = "supplier", havingValue = "alibaba")
|
||||||
@ConditionalOnProperty(prefix = "sms-blend", name = "supplier", havingValue = "alibaba")
|
|
||||||
public class AlibabaSmsConfig {
|
public class AlibabaSmsConfig {
|
||||||
|
|
||||||
private String accessKeyId;
|
private String accessKeyId;
|
||||||
@ -26,6 +27,8 @@ public class AlibabaSmsConfig {
|
|||||||
/** 模板变量名称*/
|
/** 模板变量名称*/
|
||||||
private String templateName;
|
private String templateName;
|
||||||
|
|
||||||
|
private String requestUrl = "dysmsapi.aliyuncs.com";
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Client config() throws Exception {
|
public Client config() throws Exception {
|
||||||
@ -35,7 +38,12 @@ public class AlibabaSmsConfig {
|
|||||||
// AccessKey Secret
|
// AccessKey Secret
|
||||||
.setAccessKeySecret(accessKeySecret);
|
.setAccessKeySecret(accessKeySecret);
|
||||||
// 访问的域名
|
// 访问的域名
|
||||||
config.endpoint = "dysmsapi.aliyuncs.com";
|
config.endpoint = requestUrl;
|
||||||
return new Client(config);
|
return new Client(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Bean
|
||||||
|
// public SmsBlend smsBlend(){
|
||||||
|
// return new AlibabaSmsImpl();
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,140 @@
|
|||||||
|
package kim.wind.sms.aliyun.service;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.aliyun.dysmsapi20170525.Client;
|
||||||
|
import com.aliyun.dysmsapi20170525.models.SendBatchSmsRequest;
|
||||||
|
import com.aliyun.dysmsapi20170525.models.SendBatchSmsResponse;
|
||||||
|
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
|
||||||
|
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
|
||||||
|
import com.aliyun.tea.TeaException;
|
||||||
|
import com.aliyun.teautil.models.RuntimeOptions;
|
||||||
|
import kim.wind.sms.aliyun.config.AlibabaSmsConfig;
|
||||||
|
import kim.wind.sms.api.SmsBlend;
|
||||||
|
import kim.wind.sms.api.callback.CallBack;
|
||||||
|
import kim.wind.sms.comm.annotation.Restricted;
|
||||||
|
import kim.wind.sms.comm.entity.SmsResponse;
|
||||||
|
import kim.wind.sms.comm.exception.SmsBlendException;
|
||||||
|
import kim.wind.sms.comm.utils.HTTPUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Qualifier;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.scheduling.annotation.Async;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
@EnableConfigurationProperties({AlibabaSmsConfig.class})
|
||||||
|
@Slf4j
|
||||||
|
public class AlibabaSmsImpl implements SmsBlend {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private Client client;
|
||||||
|
@Autowired
|
||||||
|
private AlibabaSmsConfig alibabaSmsConfig;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
@Qualifier("smsExecutor")
|
||||||
|
private Executor pool;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Restricted
|
||||||
|
public SmsResponse sendMessage(String phone, String message) {
|
||||||
|
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||||
|
map.put(alibabaSmsConfig.getTemplateName(), message);
|
||||||
|
return sendMessage(phone, alibabaSmsConfig.getTemplateId(), map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Restricted
|
||||||
|
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||||
|
SendSmsRequest sendSmsRequest = new SendSmsRequest();
|
||||||
|
String s = JSONObject.toJSONString(messages);
|
||||||
|
sendSmsRequest.setPhoneNumbers(phone)
|
||||||
|
.setTemplateCode(alibabaSmsConfig.getTemplateId())
|
||||||
|
.setTemplateParam(s)
|
||||||
|
.setSignName(alibabaSmsConfig.getSignature());
|
||||||
|
RuntimeOptions runtime = new RuntimeOptions();
|
||||||
|
SmsResponse smsResponse = new SmsResponse();
|
||||||
|
try {
|
||||||
|
SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime);
|
||||||
|
smsResponse.setBizId(sendSmsResponse.body.getBizId());
|
||||||
|
smsResponse.setData(sendSmsResponse.body);
|
||||||
|
smsResponse.setCode(sendSmsResponse.statusCode);
|
||||||
|
if (!"OK".equals(sendSmsResponse.body.code)) {
|
||||||
|
smsResponse.setErrMessage((sendSmsResponse.body.message));
|
||||||
|
smsResponse.setErrorCode(sendSmsResponse.body.code);
|
||||||
|
} else {
|
||||||
|
smsResponse.setMessage(sendSmsResponse.body.message);
|
||||||
|
}
|
||||||
|
} catch (TeaException error) {
|
||||||
|
throw new SmsBlendException(error.message);
|
||||||
|
// 如有需要,请打印 error
|
||||||
|
} catch (Exception _error) {
|
||||||
|
TeaException error = new TeaException(_error.getMessage(), _error);
|
||||||
|
// 如有需要,请打印 error
|
||||||
|
throw new SmsBlendException(error.message);
|
||||||
|
}
|
||||||
|
return smsResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Restricted
|
||||||
|
public SmsResponse massTexting(List<String> phones, String message) {
|
||||||
|
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||||
|
map.put(alibabaSmsConfig.getTemplateName(), message);
|
||||||
|
return massTexting(phones, alibabaSmsConfig.getTemplateId(), map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Restricted
|
||||||
|
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||||
|
SendBatchSmsRequest sendBatchSmsRequest = new SendBatchSmsRequest();
|
||||||
|
sendBatchSmsRequest.setPhoneNumberJson(JSONObject.toJSONString(phones))//群发的手机号
|
||||||
|
.setTemplateCode(alibabaSmsConfig.getTemplateId())//模板id
|
||||||
|
.setTemplateParamJson(JSONObject.toJSONString(messages))//消息内容
|
||||||
|
.setSignNameJson(alibabaSmsConfig.getSignature());//短信签名
|
||||||
|
RuntimeOptions runtime = new RuntimeOptions();
|
||||||
|
SmsResponse smsResponse = new SmsResponse();
|
||||||
|
try {
|
||||||
|
SendBatchSmsResponse sendBatchSmsResponse = client.sendBatchSmsWithOptions(sendBatchSmsRequest, runtime);
|
||||||
|
smsResponse.setBizId(sendBatchSmsResponse.body.getBizId());
|
||||||
|
smsResponse.setData(HTTPUtils.getJSONObject(sendBatchSmsResponse.body));
|
||||||
|
smsResponse.setCode(sendBatchSmsResponse.statusCode);
|
||||||
|
if (!"OK".equals(sendBatchSmsResponse.body.code)) {
|
||||||
|
smsResponse.setErrMessage((sendBatchSmsResponse.body.message));
|
||||||
|
smsResponse.setErrorCode(sendBatchSmsResponse.body.code);
|
||||||
|
} else {
|
||||||
|
smsResponse.setMessage(sendBatchSmsResponse.body.message);
|
||||||
|
}
|
||||||
|
} catch (TeaException error) {
|
||||||
|
throw new SmsBlendException(error.message);
|
||||||
|
// 如有需要,请打印 error
|
||||||
|
} catch (Exception _error) {
|
||||||
|
TeaException error = new TeaException(_error.getMessage(), _error);
|
||||||
|
// 如有需要,请打印 error
|
||||||
|
throw new SmsBlendException(error.message);
|
||||||
|
}
|
||||||
|
return smsResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Restricted
|
||||||
|
public void sendMessageAsync(String phone, String message, CallBack callBack) {
|
||||||
|
pool.execute(() -> {
|
||||||
|
SmsResponse smsResponse = sendMessage(phone, message);
|
||||||
|
callBack.callBack(smsResponse);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Restricted
|
||||||
|
public void sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages, CallBack callBack) {
|
||||||
|
pool.execute(()->{
|
||||||
|
SmsResponse smsResponse = sendMessage(phone,templateId,messages);
|
||||||
|
callBack.callBack(smsResponse);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,13 +11,18 @@
|
|||||||
<artifactId>sms-aggregation-api</artifactId>
|
<artifactId>sms-aggregation-api</artifactId>
|
||||||
<name>sms-aggregation-api</name>
|
<name>sms-aggregation-api</name>
|
||||||
<description>sms-aggregation-api</description>
|
<description>sms-aggregation-api</description>
|
||||||
<version>${modules.version}</version>
|
<version>1.0.0</version>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>kim.wind</groupId>
|
||||||
|
<artifactId>sms-aggregation-comm</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
package kim.wind.sms.api;
|
package kim.wind.sms.api;
|
||||||
|
|
||||||
|
import kim.wind.sms.api.callback.CallBack;
|
||||||
|
import kim.wind.sms.comm.annotation.Restricted;
|
||||||
import kim.wind.sms.comm.entity.SmsResponse;
|
import kim.wind.sms.comm.entity.SmsResponse;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@ -17,6 +19,7 @@ public interface SmsBlend {
|
|||||||
* message 消息内容
|
* message 消息内容
|
||||||
* @author :Wind
|
* @author :Wind
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SmsResponse sendMessage(String phone,String message);
|
SmsResponse sendMessage(String phone,String message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -26,6 +29,7 @@ public interface SmsBlend {
|
|||||||
* @param messages key为模板变量名称 value为模板变量值
|
* @param messages key为模板变量名称 value为模板变量值
|
||||||
* @author :Wind
|
* @author :Wind
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String,String> messages);
|
SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String,String> messages);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,6 +37,7 @@ public interface SmsBlend {
|
|||||||
* massTexting
|
* massTexting
|
||||||
* @author :Wind
|
* @author :Wind
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SmsResponse massTexting(List<String> phones, String message);
|
SmsResponse massTexting(List<String> phones, String message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,7 +45,28 @@ public interface SmsBlend {
|
|||||||
* massTexting
|
* massTexting
|
||||||
* @author :Wind
|
* @author :Wind
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SmsResponse massTexting(List<String> phones,String templateId, LinkedHashMap<String, String> messages);
|
SmsResponse massTexting(List<String> phones,String templateId, LinkedHashMap<String, String> messages);
|
||||||
|
|
||||||
void sendMessageAsync(String phone,String message);
|
/**
|
||||||
|
* <p>说明:异步短信发送,固定消息模板短信
|
||||||
|
* sendMessageAsync
|
||||||
|
* @param phone 要发送的号码
|
||||||
|
* @param message 发送内容
|
||||||
|
* @param callBack 回调
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
|
||||||
|
void sendMessageAsync(String phone, String message, CallBack callBack);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:异步短信发送,使用自定义模板发送短信
|
||||||
|
* sendMessage
|
||||||
|
* @param templateId 模板id
|
||||||
|
* @param messages key为模板变量名称 value为模板变量值
|
||||||
|
* @param callBack 回调
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
|
||||||
|
void sendMessage(String phone, String templateId, LinkedHashMap<String,String> messages, CallBack callBack);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
package kim.wind.sms.api.callback;
|
package kim.wind.sms.api.callback;
|
||||||
|
|
||||||
|
import kim.wind.sms.comm.entity.SmsResponse;
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface CallBack {
|
public interface CallBack {
|
||||||
void callBack();
|
void callBack(SmsResponse smsResponse);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<artifactId>sms-aggregation-comm</artifactId>
|
<artifactId>sms-aggregation-comm</artifactId>
|
||||||
<name>sms-aggregation-comm</name>
|
<name>sms-aggregation-comm</name>
|
||||||
<description>sms-aggregation-comm</description>
|
<description>sms-aggregation-comm</description>
|
||||||
<version>${modules.version}</version>
|
<version>1.0.0</version>
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
</properties>
|
</properties>
|
||||||
@ -21,6 +21,21 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter</artifactId>
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.alibaba</groupId>
|
||||||
|
<artifactId>fastjson</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
package kim.wind.sms.comm.annotation;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface Restricted {
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package kim.wind.sms.comm.exception;
|
||||||
|
|
||||||
|
public class SmsBlendException extends RuntimeException{
|
||||||
|
public String code;
|
||||||
|
public String message;
|
||||||
|
public String requestId;
|
||||||
|
|
||||||
|
public SmsBlendException(String message) {
|
||||||
|
super(message);
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmsBlendException(String code, String message) {
|
||||||
|
super("[" + code + "] " + message);
|
||||||
|
this.message = message;
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SmsBlendException(String code, String message, String requestId) {
|
||||||
|
super("[" + code + "] " + message);
|
||||||
|
this.message = message;
|
||||||
|
this.code = code;
|
||||||
|
this.code = requestId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,496 @@
|
|||||||
|
package kim.wind.sms.comm.utils;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import okhttp3.*;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>类名: HTTPUtils
|
||||||
|
* <p>说明: 封装okhttp3,简单化请求创建流程,并且可以当做工具类进行自动装配使用
|
||||||
|
* <p>构建请求只需要获得OKHTTPUtils的对象,然后逐步调用即可
|
||||||
|
* <p>如:
|
||||||
|
* <p>http.setBaseURL("http://www.baidu.com").builder().get("/wenku").sync();
|
||||||
|
* <p>构建一个post请求只需要:
|
||||||
|
* <p>http.setBaseURL("http://www.baidu.com").builder().post("/wenku",object).sync();
|
||||||
|
* <p>如果在Spring Boot中作为组件自动装配使用,{@code baseURL}将读取配置文件中的{@code okhttp.url}获取默认的请求路径,则不需要再次调用 setBaseURL()方法进行设置
|
||||||
|
* <p>依赖于 {@code okhttp3} {@code alibaba.fastjson} {@code lombok.slf4j}
|
||||||
|
* <p>{@code
|
||||||
|
* <dependency>
|
||||||
|
* <groupId>com.squareup.okhttp3</groupId>
|
||||||
|
* <artifactId>okhttp</artifactId>
|
||||||
|
* <version>3.14.9</version>
|
||||||
|
* </dependency>}
|
||||||
|
* <p>{@code
|
||||||
|
* <dependency>
|
||||||
|
* <groupId>com.alibaba</groupId>
|
||||||
|
* <artifactId>fastjson</artifactId>
|
||||||
|
* <version>1.2.74</version>
|
||||||
|
*</dependency>}
|
||||||
|
*
|
||||||
|
* @author :Wind
|
||||||
|
* @date :2022/11/07 14:10
|
||||||
|
**/
|
||||||
|
@Slf4j
|
||||||
|
public class HTTPUtils {
|
||||||
|
/**
|
||||||
|
* 默认路径
|
||||||
|
*/
|
||||||
|
|
||||||
|
private String baseURL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存的默认地址
|
||||||
|
*/
|
||||||
|
|
||||||
|
private String defaultURL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求对象
|
||||||
|
*/
|
||||||
|
private volatile Request request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求构建对象
|
||||||
|
*/
|
||||||
|
private Request.Builder builder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* okhttp客户端
|
||||||
|
*/
|
||||||
|
private volatile OkHttpClient client;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最终返回的对象
|
||||||
|
*/
|
||||||
|
private OKResponse okResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 最终请求的url
|
||||||
|
*/
|
||||||
|
private String url = "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求头类型标注
|
||||||
|
*/
|
||||||
|
public MediaType json = MediaType.parse("application/json;charset=utf-8");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:请求头类型标注
|
||||||
|
* @name: setMediaType
|
||||||
|
* @param
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils setMediaType(String json) {
|
||||||
|
this.json = MediaType.parse(json);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记子线程处理状态
|
||||||
|
*/
|
||||||
|
private boolean isOK = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:构建一个请求对象,该方法将返回对象本身,可以连锁调用,只有调用该方法后才可以调用get post等方法
|
||||||
|
*
|
||||||
|
* @name: builder
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils builder() {
|
||||||
|
this.builder = new Request.Builder();
|
||||||
|
this.client = new OkHttpClient();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:设置一个通用的请求地址
|
||||||
|
* <p>如果不设置该地址则以输入的地址作为请求地址,如设置了改地址,会自动拼接之后builder时设置的地址
|
||||||
|
* <p>一旦设置该地址后,在重新获取对象或者调用{@link #defaultURL()}之前,配置文件中的默认地址将会被覆盖
|
||||||
|
*
|
||||||
|
* @param baseURL 设置一个通用的请求地址
|
||||||
|
* @name: setBaseURL
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils setBaseURL(String baseURL) {
|
||||||
|
this.baseURL = baseURL;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:设置回默认的请求路径(配置文件中路径)
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @name: defaultURL
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils defaultURL() {
|
||||||
|
this.baseURL = this.defaultURL;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:向请求中设置heard
|
||||||
|
*
|
||||||
|
* @param key header的名称
|
||||||
|
* @param value header的值
|
||||||
|
* @name: headers
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils headers(String key, String value) {
|
||||||
|
this.builder = builder.header(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:向请求中设置heard
|
||||||
|
*
|
||||||
|
* @param map Map形式的header
|
||||||
|
* @name: headers
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils headers(Map<String, String> map) {
|
||||||
|
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||||
|
this.builder = builder.header(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:发送get请求
|
||||||
|
*
|
||||||
|
* @param url 要发送请求的url
|
||||||
|
* @param data 请求的参数
|
||||||
|
* @name: get
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils get(String url, Map<String, String> data) {
|
||||||
|
Response response;
|
||||||
|
url = extracted(url);
|
||||||
|
url = getString(url, data);
|
||||||
|
log.info("请求路径:" + url);
|
||||||
|
this.request = builder.url(url).get().build();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:发送get请求
|
||||||
|
*
|
||||||
|
* @param url 要发送请求的url
|
||||||
|
* @name: get
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils get(String url) {
|
||||||
|
return get(url, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:以json为参数发送post请求
|
||||||
|
* 该方法使用了JSON进行序列化,一定确保传入的data为可序列化的
|
||||||
|
*
|
||||||
|
* @param url 请求路径
|
||||||
|
* @param data 请求数据尽量使用Map、Array、或实体类
|
||||||
|
* @name: post
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils post(String url, Object data) {
|
||||||
|
url = extracted(url);
|
||||||
|
log.info("请求路径:" + url);
|
||||||
|
String s = JSON.toJSONString(data);
|
||||||
|
//将数据封装到RequestBody中
|
||||||
|
RequestBody fromBody = RequestBody.create(json, s);
|
||||||
|
this.request = builder.post(fromBody).url(url).build();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:发送格式为application/x-www-form-urlencoded的post请求
|
||||||
|
* @name: postOrBody
|
||||||
|
* @param
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils postOrBody(String url, Map<String,String> data) {
|
||||||
|
url = extracted(url);
|
||||||
|
log.info("请求路径:" + url);
|
||||||
|
//将数据封装到RequestBody中
|
||||||
|
RequestBody fromBody = getPostRequestBody(data);
|
||||||
|
this.request = builder.post(fromBody).url(url).build();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private RequestBody getPostRequestBody(Map<String,String> data){
|
||||||
|
FormBody.Builder builder1 = new FormBody.Builder();
|
||||||
|
data.forEach(builder1::add);
|
||||||
|
return builder1.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:post请求,第三个参数为true时则请求的参数在query中,此时只接受 Map<String,String> 类型的参数
|
||||||
|
*
|
||||||
|
* @param url 请求路径
|
||||||
|
* @param query 在url中的参数
|
||||||
|
* @param isQuery 参数是否在query中
|
||||||
|
* @name: post
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils post(String url, Map<String, String> query, boolean isQuery) {
|
||||||
|
if (isQuery) {
|
||||||
|
url = extracted(url);
|
||||||
|
String string = getString(url, query);
|
||||||
|
log.info("请求参数:" + string);
|
||||||
|
RequestBody fromBody = RequestBody.create(json, "");
|
||||||
|
this.request = builder.post(fromBody).url(url).build();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
return post(url, query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:使用同步形式发送请求
|
||||||
|
*
|
||||||
|
* @return OKResponse 返回参数
|
||||||
|
* @name: sync
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public OKResponse sync() {
|
||||||
|
isBuild();
|
||||||
|
Response response;
|
||||||
|
try {
|
||||||
|
response = client.newCall(this.request).execute();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
url = "";
|
||||||
|
okResponse = null;
|
||||||
|
}
|
||||||
|
return new OKResponse().setBody(response.body()).setCode(response.code()).setHeaders(response.headers());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:使用异步形式发送请求
|
||||||
|
* <p>该方法会启动一个高优先级子线程处理请求任务,但不会等待处理结果,直接返回调用对象,如需获取结果可以调用异步回调方法
|
||||||
|
* <p>{@link #asyncCallback()}
|
||||||
|
* <p>回调方法将会始终阻塞线程直至子线程处理完成返回结果</p>
|
||||||
|
*
|
||||||
|
* @return {@code OKResponse}
|
||||||
|
* @name: async
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public HTTPUtils async() {
|
||||||
|
HTTPUtils that = this;
|
||||||
|
Thread t = new Thread(() -> {
|
||||||
|
log.info("子线程开始执行");
|
||||||
|
that.okResponse = sync();
|
||||||
|
that.isOK = true;
|
||||||
|
log.info("子线程请求任务结束");
|
||||||
|
});
|
||||||
|
t.setPriority(Thread.MAX_PRIORITY);
|
||||||
|
t.start();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:使用异步形式发送请求
|
||||||
|
* <p>该方法会启动一个高优先级子线程处理请求任务,但不会等待处理结果,同时不会返回任何对象,处理完成的结果将会放置在调用对象的 {@link #okResponse}对象中</p>
|
||||||
|
* <p>回调方法将会始终阻塞线程直至子线程处理完成返回结果</p>
|
||||||
|
*
|
||||||
|
* @param NotWait
|
||||||
|
* @name: async
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public void async(boolean NotWait) {
|
||||||
|
async();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:每200毫秒检测一次异步线程是否处理完成,否则将阻塞至此,直至尝试100次后抛出{@code RuntimeException("等待超时!")}
|
||||||
|
*
|
||||||
|
* @param
|
||||||
|
* @name: asyncCallback
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public OKResponse asyncCallback() {
|
||||||
|
OKResponse okResponse1 = null;
|
||||||
|
for (int i = 0; i <= 100; i++) {
|
||||||
|
if (isOK) break;
|
||||||
|
if (i == 100){
|
||||||
|
throw new RuntimeException("等待超时!");
|
||||||
|
}
|
||||||
|
log.info("第"+i+"次尝试获取数据");
|
||||||
|
try {
|
||||||
|
Thread.sleep(200);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
okResponse1 = getOkResponse();
|
||||||
|
} finally {
|
||||||
|
isOK = false;
|
||||||
|
okResponse = null;
|
||||||
|
url = "";
|
||||||
|
}
|
||||||
|
return okResponse1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:将map中的数据拼接到请求地址之后
|
||||||
|
*
|
||||||
|
* @param add 地址
|
||||||
|
* @param map 请求参数
|
||||||
|
* @name: getString
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
private String getString(String add, Map<String, String> map) {
|
||||||
|
if (map != null) {
|
||||||
|
StringBuilder addBuilder = new StringBuilder(add);
|
||||||
|
addBuilder.append("?");
|
||||||
|
for (Map.Entry<String, String> entry : map.entrySet()) {
|
||||||
|
addBuilder.append("&").append(entry.getKey()).append("=").append(entry.getValue());
|
||||||
|
}
|
||||||
|
add = addBuilder.toString();
|
||||||
|
}
|
||||||
|
return add;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:拼接验证url地址
|
||||||
|
*
|
||||||
|
* @param url URL地址
|
||||||
|
* @name: extracted
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
private String extracted(String url) {
|
||||||
|
this.url = "";
|
||||||
|
if (StringUtils.isEmpty(baseURL)) {
|
||||||
|
return this.url = url;
|
||||||
|
}
|
||||||
|
return this.url = baseURL + url;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OKResponse getOkResponse() {
|
||||||
|
return this.okResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void isBuild(){
|
||||||
|
if(this.builder == null){
|
||||||
|
throw new RuntimeException("非法调用!未构建请求对象!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:将返回结果序列化到实体类中
|
||||||
|
* <p>传入对象必须实现了getter和setter方法,否则将序列化失败
|
||||||
|
* @name: getJSONBody
|
||||||
|
* @param t 要序列化的对象
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public static<T> T getJSONBody(OKResponse response, Class<T> t) {
|
||||||
|
try {
|
||||||
|
return JSONObject.parseObject(response.getBody().string(), t);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:将返回结果序列化为一个json对象
|
||||||
|
* <p>
|
||||||
|
* @name: getJSONObject
|
||||||
|
* @param response
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public static JSONObject getJSONObject(OKResponse response){
|
||||||
|
try {
|
||||||
|
return JSONObject.parseObject(response.getBody().string());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public static JSONObject getJSONObject(Object obj){
|
||||||
|
return JSONObject.parseObject(obj.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>类名: OkHTTPUtils
|
||||||
|
* <p>说明: 用于封装请求后返回的参数
|
||||||
|
*
|
||||||
|
* @author :Wind
|
||||||
|
* @date :2022/7/11 16:12
|
||||||
|
**/
|
||||||
|
public class OKResponse {
|
||||||
|
private ResponseBody body;
|
||||||
|
private Headers headers;
|
||||||
|
private Integer code;
|
||||||
|
|
||||||
|
public ResponseBody getBody() {
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OKResponse setBody(ResponseBody body) {
|
||||||
|
this.body = body;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Headers getHeaders() {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OKResponse setHeaders(Headers headers) {
|
||||||
|
this.headers = headers;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getCode() {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OKResponse setCode(Integer code) {
|
||||||
|
this.code = code;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:将返回结果序列化到实体类中
|
||||||
|
* <p>传入对象必须实现了getter和setter方法,否则将序列化失败
|
||||||
|
* @name: getJSONBody
|
||||||
|
* @param t 要序列化的对象
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public<T> T getJSONBody(Class<T> t) {
|
||||||
|
try {
|
||||||
|
return JSONObject.parseObject(this.getBody().string(), t);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* <p>说明:将返回结果序列化为一个json对象
|
||||||
|
* <p>
|
||||||
|
* @name: getJSONObject
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public JSONObject getJSONObject(){
|
||||||
|
try {
|
||||||
|
return JSONObject.parseObject(this.getBody().string());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "OKResponse{" +
|
||||||
|
"body=" + body +
|
||||||
|
", headers=" + headers +
|
||||||
|
", code=" + code +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,445 @@
|
|||||||
|
package kim.wind.sms.comm.utils;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.annotation.Resource;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class RedisUtils {
|
||||||
|
|
||||||
|
private final RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
public RedisUtils(RedisTemplate<String, Object> redisTemplate) {
|
||||||
|
this.redisTemplate = redisTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:设置redis的key的到期时间
|
||||||
|
*
|
||||||
|
* @param key redis的key
|
||||||
|
* @param time 到期时间
|
||||||
|
* @name: setTimeByKey
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public boolean setTimeByKey(String key, Long time) {
|
||||||
|
try {
|
||||||
|
if (time > 0) {
|
||||||
|
redisTemplate.expire(key, time, TimeUnit.SECONDS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:放入redis
|
||||||
|
*
|
||||||
|
* @param key 要放入的key
|
||||||
|
* @param value 要放入的value
|
||||||
|
* @name: set
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public boolean set(String key, Object value) {
|
||||||
|
|
||||||
|
redisTemplate.opsForValue().set(key, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:放入带过期时间的缓存
|
||||||
|
*
|
||||||
|
* @param time 到期时间(秒)
|
||||||
|
* @name: setOrTime
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public boolean setOrTime(String key, Object value, Long time) {
|
||||||
|
try {
|
||||||
|
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:将Map中的数据批量放置到redis中
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param valueMap 要放入的数据
|
||||||
|
* @name: multiSet
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public boolean multiSet(Map valueMap) {
|
||||||
|
try {
|
||||||
|
redisTemplate.opsForValue().multiSet(valueMap);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.toString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:获取key对应的值
|
||||||
|
*
|
||||||
|
* @param key 要查询的key
|
||||||
|
* @name: getByKey
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Object getByKey(String key) {
|
||||||
|
return redisTemplate.opsForValue().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:获取字符串型值
|
||||||
|
* @name: getKyeString
|
||||||
|
* @param
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public String getKyeString(String key){
|
||||||
|
return (String) getByKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:判断key是否存在
|
||||||
|
*
|
||||||
|
* @param key 要判断的key
|
||||||
|
* @name: hasKey
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Boolean hasKey(String key) {
|
||||||
|
return redisTemplate.hasKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 说明:根据key删除redis缓存可以批量删除
|
||||||
|
*
|
||||||
|
* @param key 要删除的key
|
||||||
|
* @name: deleteKey
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Boolean deleteKey(String... key) {
|
||||||
|
if (key != null && key.length > 0) {
|
||||||
|
if (key.length == 1) {
|
||||||
|
return redisTemplate.delete(key[0]);
|
||||||
|
} else {
|
||||||
|
Long delete = redisTemplate.delete(Arrays.asList(key));
|
||||||
|
return delete >= 1L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Boolean delete(String key){
|
||||||
|
Set<String> keys = redisTemplate.keys(key+"*");
|
||||||
|
redisTemplate.delete(keys);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据key 获取key的过期时间
|
||||||
|
*
|
||||||
|
* @param key 键 不能为null
|
||||||
|
* @return 时间(秒) 返回-1, 代表为永久有效
|
||||||
|
*/
|
||||||
|
public Long getKeyExpire(String key) {
|
||||||
|
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改redis中key的名称
|
||||||
|
*
|
||||||
|
* @param oldKey 旧的key值
|
||||||
|
* @param newKey 新的key值
|
||||||
|
*/
|
||||||
|
public void renameKey(String oldKey, String newKey) {
|
||||||
|
redisTemplate.rename(oldKey, newKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:将map对象存入redis
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param map 要存入redis中的map
|
||||||
|
* @name: setMap
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public <T, M> void MapSetMap(String key, Map<T, M> map) {
|
||||||
|
redisTemplate.opsForHash().putAll(key, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:获取所有hash表中字段
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param
|
||||||
|
* @name: getMapByKey
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Set<Object> MapGetHashByKey(String key) {
|
||||||
|
return redisTemplate.opsForHash().keys(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:根据key和fieId获取对应的值
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param fieId hash中的fieId也是Map的Key
|
||||||
|
* @name: getValueByFieID
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Object MapGetValueByFieID(String key, String fieId) {
|
||||||
|
return redisTemplate.opsForHash().get(key, fieId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:根据key获取所有的键值对
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param key redis中的key
|
||||||
|
* @name: getMapByKey
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Map<Object, Object> MapGetMapByKey(String key) {
|
||||||
|
return redisTemplate.opsForHash().entries(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:向key中添加一对新的键值对
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param hashKey 键值对的key
|
||||||
|
* @param value 键值对的value
|
||||||
|
* @name: setNewMapValue
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public void MapSetNewMapValue(String key, String hashKey, Object value) {
|
||||||
|
redisTemplate.opsForHash().put(key, hashKey, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:根据key和field删除数据
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param fields 要删除的fields
|
||||||
|
* @return Long 影响的条数
|
||||||
|
* @name: hashDelete
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Long MapHashDelete(String key, Object... fields) {
|
||||||
|
return redisTemplate.opsForHash().delete(key, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:查看key下存了多少条键值对
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @param key redis的key
|
||||||
|
* @name: getMapValueSize
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Long MapGetMapValueSize(String key) {
|
||||||
|
return redisTemplate.opsForHash().size(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置值到List中的头部
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param value
|
||||||
|
* @return
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Boolean listAddInHead(String key, Object value) {
|
||||||
|
try {
|
||||||
|
redisTemplate.opsForList().leftPush(key, value);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量设置值到List中的头部
|
||||||
|
*
|
||||||
|
* @param key List名字
|
||||||
|
* @param values
|
||||||
|
* @return
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Boolean listAddAllInHead(String key, Collection<?> values) {
|
||||||
|
try {
|
||||||
|
redisTemplate.opsForList().leftPushAll(key, values);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果存在List->key, 则设置值到List中的头部
|
||||||
|
*
|
||||||
|
* @param key List名字
|
||||||
|
* @param value
|
||||||
|
* @return
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Boolean listAddIfPresent(String key, Object value) {
|
||||||
|
try {
|
||||||
|
redisTemplate.opsForList().leftPushIfPresent(key, value);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置值到List中的尾部
|
||||||
|
*
|
||||||
|
* @param key List名字
|
||||||
|
* @param value 值
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Boolean listAddInEnd(String key, Object value) {
|
||||||
|
try {
|
||||||
|
redisTemplate.opsForList().rightPush(key, value);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量设置值到List中的尾部
|
||||||
|
*
|
||||||
|
* @param key List名字
|
||||||
|
* @param values 要设置的集合
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Boolean listAddAllInEnd(String key, Collection<?> values) {
|
||||||
|
try {
|
||||||
|
redisTemplate.opsForList().rightPushAll(key, values);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通过索引去设置List->key中的值
|
||||||
|
*
|
||||||
|
* @param key redis的key
|
||||||
|
* @param index 索引
|
||||||
|
* @param value 值
|
||||||
|
* @return
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Boolean listAddByIndex(String key, long index, Object value) {
|
||||||
|
try {
|
||||||
|
redisTemplate.opsForList().set(key, index, value);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据索引获取list中的值
|
||||||
|
*
|
||||||
|
* @param key list名字
|
||||||
|
* @param index
|
||||||
|
* @return
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Object listGetByIndex(String key, long index) {
|
||||||
|
return redisTemplate.opsForList().index(key, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据索引范围获取list中的值
|
||||||
|
*
|
||||||
|
* @param key list名字
|
||||||
|
* @param start
|
||||||
|
* @param end
|
||||||
|
* @return
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public List<Object> listGetByRange(String key, long start, long end) {
|
||||||
|
return redisTemplate.opsForList().range(key, start, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除并获取列表中第一个元素(如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止)
|
||||||
|
*
|
||||||
|
* @param key list名字
|
||||||
|
* @return
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Object listLeftPop(String key) {
|
||||||
|
return redisTemplate.opsForList().leftPop(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除并获取列表中最后一个元素(如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止)
|
||||||
|
*
|
||||||
|
* @param key list名字
|
||||||
|
* @return
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Object listRightPop(String key) {
|
||||||
|
return redisTemplate.opsForList().rightPop(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:获取列表元素的大小
|
||||||
|
* <p>
|
||||||
|
* @name: listGetSize
|
||||||
|
* @param
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Long listGetSize(String key){
|
||||||
|
return redisTemplate.opsForList().size(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除集合中值等于value的元素(
|
||||||
|
* index=0, 删除所有值等于value的元素;
|
||||||
|
* index>0, 从头部开始删除第一个值等于value的元素;
|
||||||
|
* index<0, 从尾部开始删除第一个值等于value的元素)
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* @param index
|
||||||
|
* @param value
|
||||||
|
* @return
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public Long listRemove(String key, long index, Object value) {
|
||||||
|
return redisTemplate.opsForList().remove(key, index, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:清除所有缓存
|
||||||
|
* <p><b>该方法会清理掉redis中所有的缓存,谨慎使用</b>
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* @name: empty
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public void empty() {
|
||||||
|
redisTemplate.getConnectionFactory().getConnection().flushAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
package kim.wind.sms.comm.utils;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class SmsUtil {
|
||||||
|
private SmsUtil() {
|
||||||
|
} //私有构造防止实例化
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:生成一个指定长度的随机字符串,包含大小写英文字母和数字
|
||||||
|
* @name: getRandomString
|
||||||
|
* @param len 要生成的字符串的长度
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public static String getRandomString(int len){
|
||||||
|
String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||||
|
Random random = new Random();
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
int number = random.nextInt(62);
|
||||||
|
sb.append(str.charAt(number));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:获取一个长度为6的随机字符串
|
||||||
|
* @name: getRandomString
|
||||||
|
* @param
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public static String getRandomString(){
|
||||||
|
return getRandomString(6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>说明:生成一个指定长度的只有数字组成的随机字符串
|
||||||
|
* @name: getRandomInt
|
||||||
|
* @param len 要生成的长度
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public static String getRandomInt(int len){
|
||||||
|
String str = "0123456789";
|
||||||
|
Random random = new Random();
|
||||||
|
StringBuffer sb = new StringBuffer();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
int number = random.nextInt(10);
|
||||||
|
sb.append(str.charAt(number));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定元素是否为null或者空字符串
|
||||||
|
* @param str 指定元素
|
||||||
|
* @return 是否为null或者空字符串
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public static boolean isEmpty(Object str) {
|
||||||
|
return str == null || "".equals(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定元素是否不为 (null或者空字符串)
|
||||||
|
* @param str 指定元素
|
||||||
|
* @return 是否为null或者空字符串
|
||||||
|
* @author :Wind
|
||||||
|
*/
|
||||||
|
public static boolean isNotEmpty(Object str) {
|
||||||
|
return !isEmpty(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,218 @@
|
|||||||
|
package kim.wind.sms.comm.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import kim.wind.sms.comm.exception.SmsBlendException;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>类名: TimeExpiredPoolCache
|
||||||
|
* <p>说明: 一个自实现的内部缓存,可用于无法使用redis的场景
|
||||||
|
* @author :Wind
|
||||||
|
* 2023/3/25 18:26
|
||||||
|
**/
|
||||||
|
public class TimeExpiredPoolCache {
|
||||||
|
private static long defaultCachedMillis = 24 * 60 * 60 * 1000L;//过期时间默认24小时
|
||||||
|
private static long timerMillis = 30 * 1000L;//定时清理默认1分钟
|
||||||
|
/**
|
||||||
|
* 对象池
|
||||||
|
*/
|
||||||
|
private static ConcurrentHashMap<String, DataWrapper<?>> dataPool = null;
|
||||||
|
/**
|
||||||
|
* 对象单例
|
||||||
|
*/
|
||||||
|
private static TimeExpiredPoolCache instance = null;
|
||||||
|
|
||||||
|
private TimeExpiredPoolCache() {
|
||||||
|
dataPool = new ConcurrentHashMap<String, DataWrapper<?>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized void syncInit() {
|
||||||
|
if (instance == null) {
|
||||||
|
instance = new TimeExpiredPoolCache();
|
||||||
|
initTimer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TimeExpiredPoolCache getInstance() {
|
||||||
|
if (instance == null) {
|
||||||
|
syncInit();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定时器定时清理过期缓存
|
||||||
|
*/
|
||||||
|
private static Timer timer = new Timer();
|
||||||
|
|
||||||
|
private static void initTimer() {
|
||||||
|
timer.scheduleAtFixedRate(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
clearExpiredCaches();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new SmsBlendException(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, timerMillis, timerMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 缓存数据
|
||||||
|
*
|
||||||
|
* @param key key值
|
||||||
|
* @param data 缓存数据
|
||||||
|
* @param cachedMillis 过期时间
|
||||||
|
* @param dataRenewer 刷新数据
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T put(String key, T data, long cachedMillis, DataRenewer<T> dataRenewer) throws Exception {
|
||||||
|
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
|
||||||
|
if (data == null && dataRenewer != null) {
|
||||||
|
data = dataRenewer.renewData();
|
||||||
|
}
|
||||||
|
//当重新获取数据为空,直接返回不做put
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (dataWrapper != null) {
|
||||||
|
//更新
|
||||||
|
dataWrapper.update(data, cachedMillis);
|
||||||
|
} else {
|
||||||
|
dataWrapper = new DataWrapper<T>(data, cachedMillis);
|
||||||
|
dataPool.put(key, dataWrapper);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 直接设置缓存值和时间
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T put(String key, T data, long cachedMillis) throws Exception {
|
||||||
|
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
|
||||||
|
if (dataWrapper != null) {
|
||||||
|
//更新
|
||||||
|
dataWrapper.update(data, cachedMillis);
|
||||||
|
} else {
|
||||||
|
dataWrapper = new DataWrapper<T>(data, cachedMillis);
|
||||||
|
dataPool.put(key, dataWrapper);
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 默认构造时间的缓存数据
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public <T> T put(String key, T data, DataRenewer<T> dataRenewer) throws Exception {
|
||||||
|
return put(key, data, defaultCachedMillis, dataRenewer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取缓存
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(String key, long cachedMillis, DataRenewer<T> dataRenewer) throws Exception {
|
||||||
|
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
|
||||||
|
if (dataWrapper != null && !dataWrapper.isExpired()) {
|
||||||
|
return dataWrapper.data;
|
||||||
|
}
|
||||||
|
return put(key, null, cachedMillis, dataRenewer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> T get(String key) {
|
||||||
|
DataWrapper<T> dataWrapper = (DataWrapper<T>) dataPool.get(key);
|
||||||
|
if (dataWrapper != null && !dataWrapper.isExpired()) {
|
||||||
|
return dataWrapper.data;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除缓存
|
||||||
|
*/
|
||||||
|
public void clear() {
|
||||||
|
dataPool.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除指定key的value
|
||||||
|
*/
|
||||||
|
public void remove(String key) {
|
||||||
|
dataPool.remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据封装
|
||||||
|
*/
|
||||||
|
private class DataWrapper<T> {
|
||||||
|
/**
|
||||||
|
* 数据
|
||||||
|
*/
|
||||||
|
private T data;
|
||||||
|
/**
|
||||||
|
* 到期时间
|
||||||
|
*/
|
||||||
|
private long expiredTime;
|
||||||
|
/**
|
||||||
|
* 缓存时间
|
||||||
|
*/
|
||||||
|
private long cachedMillis;
|
||||||
|
|
||||||
|
private DataWrapper(T data, long cachedMillis) {
|
||||||
|
this.update(data, cachedMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(T data, long cachedMillis) {
|
||||||
|
this.data = data;
|
||||||
|
this.cachedMillis = cachedMillis;
|
||||||
|
this.updateExpiredTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateExpiredTime() {
|
||||||
|
this.expiredTime = System.currentTimeMillis() + cachedMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据是否过期
|
||||||
|
*/
|
||||||
|
public boolean isExpired() {
|
||||||
|
if (this.expiredTime > 0) {
|
||||||
|
return System.currentTimeMillis() > this.expiredTime;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 数据构造
|
||||||
|
*/
|
||||||
|
public interface DataRenewer<T> {
|
||||||
|
public T renewData();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除过期的缓存
|
||||||
|
*/
|
||||||
|
private static void clearExpiredCaches() {
|
||||||
|
List<String> expiredKeyList = new LinkedList<String>();
|
||||||
|
|
||||||
|
for (Entry<String, DataWrapper<?>> entry : dataPool.entrySet()) {
|
||||||
|
if (entry.getValue().isExpired()) {
|
||||||
|
expiredKeyList.add(entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (String key : expiredKeyList) {
|
||||||
|
dataPool.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -11,7 +11,7 @@
|
|||||||
<artifactId>sms-aggregation-spring-boot-starter</artifactId>
|
<artifactId>sms-aggregation-spring-boot-starter</artifactId>
|
||||||
<name>sms-aggregation-spring-boot-starter</name>
|
<name>sms-aggregation-spring-boot-starter</name>
|
||||||
<description>sms-aggregation-spring-boot-starter</description>
|
<description>sms-aggregation-spring-boot-starter</description>
|
||||||
<version>${modules.version}</version>
|
<version>1.0.0</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
@ -31,11 +31,6 @@
|
|||||||
<artifactId>spring-boot-starter</artifactId>
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>kim.wind</groupId>
|
|
||||||
<artifactId>sms-aggregation-api</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>kim.wind</groupId>
|
<groupId>kim.wind</groupId>
|
||||||
<artifactId>sms-aggregation-aliyun</artifactId>
|
<artifactId>sms-aggregation-aliyun</artifactId>
|
||||||
@ -55,6 +50,17 @@
|
|||||||
<groupId>kim.wind</groupId>
|
<groupId>kim.wind</groupId>
|
||||||
<artifactId>sms-aggregation-yunpian</artifactId>
|
<artifactId>sms-aggregation-yunpian</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!--aop依赖-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|||||||
@ -0,0 +1,124 @@
|
|||||||
|
package kim.wind.sms.starter.config;
|
||||||
|
|
||||||
|
|
||||||
|
import kim.wind.sms.comm.exception.SmsBlendException;
|
||||||
|
import kim.wind.sms.comm.utils.RedisUtils;
|
||||||
|
import kim.wind.sms.comm.utils.SmsUtil;
|
||||||
|
import kim.wind.sms.comm.utils.SpringUtil;
|
||||||
|
import kim.wind.sms.comm.utils.TimeExpiredPoolCache;
|
||||||
|
import org.aspectj.lang.ProceedingJoinPoint;
|
||||||
|
import org.aspectj.lang.annotation.Around;
|
||||||
|
import org.aspectj.lang.annotation.Aspect;
|
||||||
|
import org.aspectj.lang.annotation.Pointcut;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
@Aspect
|
||||||
|
public class AopAdvice {
|
||||||
|
|
||||||
|
private static final Long minTimer = 60 * 1000L;
|
||||||
|
private static final Long accTimer = 24 * 60 * 60 * 1000L;
|
||||||
|
|
||||||
|
private static final String REDIS_KEY = "sms:restricted:";
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SmsMainConfig config;
|
||||||
|
|
||||||
|
|
||||||
|
@Pointcut("@annotation(kim.wind.sms.comm.annotation.Restricted)")
|
||||||
|
public void restricted() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Around("restricted()")
|
||||||
|
public Object restrictedSendMessage(ProceedingJoinPoint p) throws Throwable {
|
||||||
|
|
||||||
|
String args = "";
|
||||||
|
ArrayList<String> argsList = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
args = (String) p.getArgs()[0];
|
||||||
|
} catch (Exception e) {
|
||||||
|
for (Object o : (ArrayList<?>) p.getArgs()[0]) {
|
||||||
|
argsList.add((String) o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SmsBlendException process = redisProcess(args);
|
||||||
|
if (process != null) {
|
||||||
|
throw process;
|
||||||
|
}
|
||||||
|
argsList.forEach(f -> {
|
||||||
|
SmsBlendException proce = null;
|
||||||
|
try {
|
||||||
|
proce = redisProcess(f);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
if (proce != null) {
|
||||||
|
throw proce;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return p.proceed();
|
||||||
|
}
|
||||||
|
|
||||||
|
private SmsBlendException process(String args) throws Exception {
|
||||||
|
TimeExpiredPoolCache instance = TimeExpiredPoolCache.getInstance();//缓存实例
|
||||||
|
Integer accountMax = config.getAccountMax();//每日最大发送量
|
||||||
|
Integer minuteMax = config.getMinuteMax();//每分钟最大发送量
|
||||||
|
if (SmsUtil.isNotEmpty(accountMax)) { //是否配置了每日限制
|
||||||
|
Integer i = instance.get(args + "max");
|
||||||
|
if (SmsUtil.isEmpty(i)) {
|
||||||
|
instance.put(args + "max", 1, accTimer);
|
||||||
|
} else if (i > accountMax) {
|
||||||
|
return new SmsBlendException("accountMax", args + "今日短信已达最大次数");
|
||||||
|
} else {
|
||||||
|
instance.put(args + "max", i + 1, accTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SmsUtil.isNotEmpty(minuteMax)) { //是否配置了每分钟最大限制
|
||||||
|
Integer o = instance.get(args);
|
||||||
|
if (SmsUtil.isNotEmpty(o)) {
|
||||||
|
if (o < minuteMax) {
|
||||||
|
instance.put(args, o + 1, minTimer);
|
||||||
|
} else {
|
||||||
|
return new SmsBlendException("minuteMax", args + "短信发送过于频繁!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instance.put(args, 1, minTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SmsBlendException redisProcess(String args) throws Exception{
|
||||||
|
RedisUtils redis = SpringUtil.getBean(RedisUtils.class);
|
||||||
|
if (redis == null || config.getRedisCache().equals("false")){
|
||||||
|
return process(args);
|
||||||
|
}
|
||||||
|
Integer accountMax = config.getAccountMax();//每日最大发送量
|
||||||
|
Integer minuteMax = config.getMinuteMax();//每分钟最大发送量
|
||||||
|
if (SmsUtil.isNotEmpty(accountMax)) { //是否配置了每日限制
|
||||||
|
Integer i = (Integer) redis.getByKey(REDIS_KEY+args + "max");
|
||||||
|
if (SmsUtil.isEmpty(i)) {
|
||||||
|
redis.set(REDIS_KEY+args + "max", 1);
|
||||||
|
} else if (i > accountMax) {
|
||||||
|
return new SmsBlendException("accountMax", args + "今日短信已达最大次数");
|
||||||
|
} else {
|
||||||
|
redis.set(REDIS_KEY+args + "max", i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SmsUtil.isNotEmpty(minuteMax)) { //是否配置了每分钟最大限制
|
||||||
|
Integer o = (Integer) redis.getByKey(REDIS_KEY+args);
|
||||||
|
if (SmsUtil.isNotEmpty(o)) {
|
||||||
|
if (o < minuteMax) {
|
||||||
|
redis.set(REDIS_KEY+args, o + 1);
|
||||||
|
} else {
|
||||||
|
return new SmsBlendException("minuteMax", args + "短信发送过于频繁!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
redis.set(REDIS_KEY+args, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,28 +1,104 @@
|
|||||||
package kim.wind.sms.starter.config;
|
package kim.wind.sms.starter.config;
|
||||||
|
|
||||||
|
import kim.wind.sms.aliyun.config.AlibabaSmsConfig;
|
||||||
|
import kim.wind.sms.aliyun.service.AlibabaSmsImpl;
|
||||||
|
import kim.wind.sms.api.SmsBlend;
|
||||||
|
import kim.wind.sms.comm.utils.RedisUtils;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import kim.wind.sms.comm.utils.SpringUtil;
|
import kim.wind.sms.comm.utils.SpringUtil;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConfigurationProperties(prefix = "sms") //指定配置文件注入属性前缀
|
@ConfigurationProperties(prefix = "sms") //指定配置文件注入属性前缀
|
||||||
|
@EnableAsync
|
||||||
@Data
|
@Data
|
||||||
public class SmsMainConfig {
|
public class SmsMainConfig {
|
||||||
|
|
||||||
/** 短信服务商*/
|
/** 短信服务商*/
|
||||||
@Value("${sms.supplier}")
|
@Value("${sms.supplier}")
|
||||||
private String supplier;
|
private String supplier;
|
||||||
|
|
||||||
/** 是否开启短信限制*/
|
/** 是否开启短信限制*/
|
||||||
private String restricted;
|
private String restricted;
|
||||||
|
|
||||||
|
/** 是否使用redis进行缓存*/
|
||||||
|
private String redisCache = "false";
|
||||||
|
|
||||||
/** 单账号每日最大发送量*/
|
/** 单账号每日最大发送量*/
|
||||||
private Integer accountMax;
|
private Integer accountMax;
|
||||||
|
|
||||||
/** 单账号每分钟最大发送*/
|
/** 单账号每分钟最大发送*/
|
||||||
private Integer minuteMax;
|
private Integer minuteMax;
|
||||||
|
|
||||||
|
/**核心线程池大小*/
|
||||||
|
private Integer corePoolSize = 10;
|
||||||
|
|
||||||
|
/** 最大线程数*/
|
||||||
|
private Integer maxPoolSize = 30;
|
||||||
|
|
||||||
|
/** 队列容量*/
|
||||||
|
private Integer queueCapacity = 50;
|
||||||
|
|
||||||
|
/** 活跃时间*/
|
||||||
|
private Integer keepAliveSeconds = 60;
|
||||||
|
|
||||||
|
/** 线程名字前缀*/
|
||||||
|
private String threadNamePrefix = "sms-executor-";
|
||||||
|
|
||||||
|
/** 设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean*/
|
||||||
|
private Boolean shutdownStrategy = true;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SpringUtil springUtil(){
|
public SpringUtil springUtil(){
|
||||||
return new SpringUtil();
|
return new SpringUtil();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public SmsBlend smsBlend(){
|
||||||
|
SmsBlend smsBlend = null;
|
||||||
|
switch (supplier){
|
||||||
|
case "alibaba":
|
||||||
|
smsBlend = new AlibabaSmsImpl();
|
||||||
|
}
|
||||||
|
return smsBlend;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean("smsExecutor")
|
||||||
|
protected Executor taskExecutor(){
|
||||||
|
// 创建一个线程池对象
|
||||||
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||||
|
executor.setCorePoolSize(corePoolSize);
|
||||||
|
executor.setMaxPoolSize(maxPoolSize);
|
||||||
|
executor.setQueueCapacity(100);
|
||||||
|
executor.setKeepAliveSeconds(60);
|
||||||
|
executor.setThreadNamePrefix(threadNamePrefix);
|
||||||
|
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||||
|
// 线程池对拒绝任务的处理策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
|
||||||
|
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||||
|
//初始化线程池
|
||||||
|
executor.initialize();
|
||||||
|
return executor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(prefix = "sms", name = "restricted", havingValue = "true")
|
||||||
|
public AopAdvice aopAdvice(){
|
||||||
|
return new AopAdvice();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(prefix = "sms", name = "redisCache", havingValue = "true")
|
||||||
|
public RedisUtils redisUtils(RedisTemplate<String, Object> redisTemplate){
|
||||||
|
return new RedisUtils(redisTemplate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,19 @@
|
|||||||
|
package kim.wind.sms.starter.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ThreadPoolExecutor;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Data
|
||||||
|
public class TaskPoolConfig {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,2 +1,3 @@
|
|||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
|
kim.wind.sms.starter.config.SmsMainConfig,\
|
||||||
|
kim.wind.sms.aliyun.config.AlibabaSmsConfig
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<artifactId>sms-aggregation-tencent</artifactId>
|
<artifactId>sms-aggregation-tencent</artifactId>
|
||||||
<name>sms-aggregation-tencent</name>
|
<name>sms-aggregation-tencent</name>
|
||||||
<description>sms-aggregation-tencent</description>
|
<description>sms-aggregation-tencent</description>
|
||||||
<version>${modules.version}</version>
|
<version>1.0.0</version>
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<artifactId>sms-aggregation-unisms</artifactId>
|
<artifactId>sms-aggregation-unisms</artifactId>
|
||||||
<name>sms-aggregation-unisms</name>
|
<name>sms-aggregation-unisms</name>
|
||||||
<description>sms-aggregation-unisms</description>
|
<description>sms-aggregation-unisms</description>
|
||||||
<version>${modules.version}</version>
|
<version>1.0.0</version>
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|||||||
@ -12,7 +12,7 @@
|
|||||||
<artifactId>sms-aggregation-yunpian</artifactId>
|
<artifactId>sms-aggregation-yunpian</artifactId>
|
||||||
<name>sms-aggregation-yunpian</name>
|
<name>sms-aggregation-yunpian</name>
|
||||||
<description>sms-aggregation-yunpian</description>
|
<description>sms-aggregation-yunpian</description>
|
||||||
<version>${modules.version}</version>
|
<version>1.0.0</version>
|
||||||
<properties>
|
<properties>
|
||||||
<java.version>1.8</java.version>
|
<java.version>1.8</java.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user