mirror of
https://gitee.com/dromara/sms4j.git
synced 2025-12-06 17:08:40 +08:00
完成华为云国内短信接入
修改基础http支持框架为Forest 重构云片短信该http框架为Forest 重构模块,添加自动配置模块
This commit is contained in:
parent
1a561009d1
commit
d55a27ff6f
41
pom.xml
41
pom.xml
@ -4,7 +4,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms_aggregation</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<name>sms-aggregation</name>
|
||||
<packaging>pom</packaging>
|
||||
<description>sms_aggregation</description>
|
||||
@ -19,6 +19,7 @@
|
||||
<module>sms-aggregation-yunpian</module>
|
||||
<module>sms-aggregation-spring-boot-starter</module>
|
||||
<module>sms-aggregation-huawei</module>
|
||||
<module>sms-aggregation-autoimmit</module>
|
||||
</modules>
|
||||
<!-- 开源协议 apache 2.0 -->
|
||||
<licenses>
|
||||
@ -49,7 +50,7 @@
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
<spring.boot.version>2.7.10</spring.boot.version>
|
||||
<modules.version>1.0.2</modules.version>
|
||||
<modules.version>1.0.3</modules.version>
|
||||
<aliyun.version>2.0.23</aliyun.version>
|
||||
<json.version>2.0.15</json.version>
|
||||
<okhttp.version>3.14.9</okhttp.version>
|
||||
@ -111,6 +112,12 @@
|
||||
<version>${modules.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms-aggregation-autoimmit</artifactId>
|
||||
<version>${modules.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!--通用模块-->
|
||||
<dependency>
|
||||
<groupId>kim.wind</groupId>
|
||||
@ -130,12 +137,6 @@
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>dysmsapi20170525</artifactId>
|
||||
<version>${aliyun.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- 阿里JSON解析器 -->
|
||||
@ -143,20 +144,8 @@
|
||||
<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>
|
||||
<!--Forest依赖 声明式HTTP客户端框架-->
|
||||
<dependency>
|
||||
<groupId>com.dtflys.forest</groupId>
|
||||
@ -184,12 +173,6 @@
|
||||
<groupId>com.apistd.uni</groupId>
|
||||
<artifactId>uni-sdk</artifactId>
|
||||
<version>${unisms.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- 腾讯云短信-->
|
||||
@ -197,12 +180,6 @@
|
||||
<groupId>com.tencentcloudapi</groupId>
|
||||
<artifactId>tencentcloud-sdk-java</artifactId>
|
||||
<version>${tencent.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
@ -5,13 +5,13 @@
|
||||
<parent>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms_aggregation</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>sms-aggregation-aliyun</artifactId>
|
||||
<name>sms-aggregation-aliyun</name>
|
||||
<description>sms-aggregation-aliyun</description>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
@ -5,16 +5,16 @@ import com.aliyun.teaopenapi.models.Config;
|
||||
import kim.wind.sms.aliyun.service.AlibabaSmsImpl;
|
||||
import kim.wind.sms.api.SmsBlend;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "sms.alibaba") //指定配置文件注入属性前缀
|
||||
@Data
|
||||
@ConditionalOnProperty(prefix = "sms", name = "supplier", havingValue = "alibaba")
|
||||
@ConditionalOnProperty(name = "sms.supplier", havingValue = "alibaba")
|
||||
public class AlibabaSmsConfig {
|
||||
|
||||
/** accessKey*/
|
||||
@ -32,7 +32,7 @@ public class AlibabaSmsConfig {
|
||||
|
||||
|
||||
@Bean
|
||||
public Client config() throws Exception {
|
||||
public Client client() throws Exception {
|
||||
Config config = new Config()
|
||||
// AccessKey ID
|
||||
.setAccessKeyId(accessKeyId)
|
||||
@ -43,8 +43,8 @@ public class AlibabaSmsConfig {
|
||||
return new Client(config);
|
||||
}
|
||||
|
||||
// @Bean
|
||||
// public SmsBlend smsBlend(){
|
||||
// return new AlibabaSmsImpl();
|
||||
// }
|
||||
@Bean
|
||||
public SmsBlend smsBlend(){
|
||||
return new AlibabaSmsImpl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ import kim.wind.sms.comm.annotation.Restricted;
|
||||
import kim.wind.sms.comm.delayedTime.DelayedTime;
|
||||
import kim.wind.sms.comm.entity.SmsResponse;
|
||||
import kim.wind.sms.comm.exception.SmsBlendException;
|
||||
import kim.wind.sms.comm.utils.HTTPUtils;
|
||||
import kim.wind.sms.comm.utils.http.HttpJsonTool;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@ -111,7 +111,7 @@ public class AlibabaSmsImpl implements SmsBlend {
|
||||
try {
|
||||
SendBatchSmsResponse sendBatchSmsResponse = client.sendBatchSmsWithOptions(sendBatchSmsRequest, runtime);
|
||||
smsResponse.setBizId(sendBatchSmsResponse.body.getBizId());
|
||||
smsResponse.setData(HTTPUtils.getJSONObject(sendBatchSmsResponse.body));
|
||||
smsResponse.setData(HttpJsonTool.getJSONObject(sendBatchSmsResponse.body));
|
||||
smsResponse.setCode(sendBatchSmsResponse.statusCode);
|
||||
if (!"OK".equals(sendBatchSmsResponse.body.code)) {
|
||||
smsResponse.setErrMessage((sendBatchSmsResponse.body.message));
|
||||
|
||||
@ -5,13 +5,13 @@
|
||||
<parent>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms_aggregation</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>sms-aggregation-api</artifactId>
|
||||
<name>sms-aggregation-api</name>
|
||||
<description>sms-aggregation-api</description>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package kim.wind.sms.starter.config;
|
||||
package kim.wind.sms.autoimmit.aop;
|
||||
|
||||
|
||||
import kim.wind.sms.autoimmit.config.SmsConfig;
|
||||
import kim.wind.sms.comm.exception.SmsBlendException;
|
||||
import kim.wind.sms.comm.utils.RedisUtils;
|
||||
import kim.wind.sms.comm.utils.SmsUtil;
|
||||
@ -23,7 +24,10 @@ public class AopAdvice {
|
||||
private static final String REDIS_KEY = "sms:restricted:";
|
||||
|
||||
@Autowired
|
||||
private SmsMainConfig config;
|
||||
private SmsConfig config;
|
||||
|
||||
@Autowired
|
||||
private SpringUtil springUtil;
|
||||
|
||||
|
||||
@Pointcut("@annotation(kim.wind.sms.comm.annotation.Restricted)")
|
||||
@ -90,32 +94,36 @@ public class AopAdvice {
|
||||
}
|
||||
|
||||
private SmsBlendException redisProcess(String args) throws Exception{
|
||||
RedisUtils redis = SpringUtil.getBean(RedisUtils.class);
|
||||
if (redis == null || config.getRedisCache().equals("false")){
|
||||
if (config.getRedisCache().equals("false")){
|
||||
return process(args);
|
||||
}
|
||||
// else {
|
||||
// springUtil.createBean(RedisUtils.class.getName(),new RedisUtils());
|
||||
// }
|
||||
RedisUtils redis = SpringUtil.getBean(RedisUtils.class);
|
||||
|
||||
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);
|
||||
redis.setOrTime(REDIS_KEY+args + "max", 1,accTimer/1000);
|
||||
} else if (i > accountMax) {
|
||||
return new SmsBlendException("accountMax", args + "今日短信已达最大次数");
|
||||
} else {
|
||||
redis.set(REDIS_KEY+args + "max", i + 1);
|
||||
redis.setOrTime(REDIS_KEY+args + "max", i + 1,accTimer/1000);
|
||||
}
|
||||
}
|
||||
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);
|
||||
redis.setOrTime(REDIS_KEY+args, o + 1,minTimer/1000);
|
||||
} else {
|
||||
return new SmsBlendException("minuteMax", args + "短信发送过于频繁!");
|
||||
}
|
||||
} else {
|
||||
redis.set(REDIS_KEY+args, 1);
|
||||
redis.setOrTime(REDIS_KEY+args, 1,minTimer/1000);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -0,0 +1,68 @@
|
||||
package kim.wind.sms.autoimmit.config;
|
||||
|
||||
import kim.wind.sms.autoimmit.aop.AopAdvice;
|
||||
import kim.wind.sms.comm.config.SmsBanner;
|
||||
import kim.wind.sms.comm.delayedTime.DelayedTime;
|
||||
import kim.wind.sms.comm.utils.RedisUtils;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
@Data
|
||||
public class SmsAutowiredConfig {
|
||||
public void init(){
|
||||
SmsBanner.PrintBanner("V1.0.3");
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties(prefix = "sms") //指定配置文件注入属性前缀
|
||||
public SmsConfig smsConfig(){
|
||||
return new SmsConfig();
|
||||
}
|
||||
|
||||
/** 注入一个定时器*/
|
||||
@Bean
|
||||
public DelayedTime delayedTime(){
|
||||
return new DelayedTime();
|
||||
}
|
||||
|
||||
/** 如果启用了短信限制,则注入Aop组件*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "sms", name = "restricted", havingValue = "true")
|
||||
public AopAdvice aopAdvice(){
|
||||
return new AopAdvice();
|
||||
}
|
||||
|
||||
/** 如果启用了redis作为缓存则注入redis工具类*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "sms", name = "redis-cache", havingValue = "true")
|
||||
public RedisUtils redisUtils(){
|
||||
return new RedisUtils();
|
||||
}
|
||||
|
||||
/** 注入线程池*/
|
||||
@Bean("smsExecutor")
|
||||
protected Executor taskExecutor(SmsConfig config){
|
||||
// 创建一个线程池对象
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(config.getCorePoolSize());
|
||||
executor.setMaxPoolSize(config.getMaxPoolSize());
|
||||
executor.setQueueCapacity(config.getQueueCapacity());
|
||||
executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
|
||||
executor.setThreadNamePrefix(config.getThreadNamePrefix());
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
// 线程池对拒绝任务的处理策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
//初始化线程池
|
||||
executor.initialize();
|
||||
return executor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package kim.wind.sms.autoimmit.config;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class SmsConfig {
|
||||
/** 短信服务商*/
|
||||
private String supplier;
|
||||
/** 打印banner*/
|
||||
private String isPrint = "true";
|
||||
|
||||
/** 是否开启短信限制*/
|
||||
private String restricted;
|
||||
|
||||
/** 是否使用redis进行缓存*/
|
||||
private String redisCache = "false";
|
||||
|
||||
/** 单账号每日最大发送量*/
|
||||
private Integer accountMax;
|
||||
|
||||
/** 单账号每分钟最大发送*/
|
||||
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;
|
||||
|
||||
|
||||
}
|
||||
@ -5,13 +5,13 @@
|
||||
<parent>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms_aggregation</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>sms-aggregation-comm</artifactId>
|
||||
<name>sms-aggregation-comm</name>
|
||||
<description>sms-aggregation-comm</description>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
@ -27,11 +27,6 @@
|
||||
<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>
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
package kim.wind.sms.comm.config;
|
||||
|
||||
import kim.wind.sms.comm.constant.Constant;
|
||||
|
||||
public class SmsBanner {
|
||||
|
||||
private static final String banner =
|
||||
@ -10,8 +12,8 @@ public class SmsBanner {
|
||||
" ____) | | | | | ____) | / ____ \\ | |__| | | |__| | | | \\ \\ | |____ | |__| | / ____ \\ | | _| |_ | |__| | | |\\ |\n" +
|
||||
" |_____/ |_| |_| |_____/ /_/ \\_\\ \\_____| \\_____| |_| \\_\\ |______| \\_____| /_/ \\_\\ |_| |_____| \\____/ |_| \\_|\n" +
|
||||
" \n" +
|
||||
" V1.0.2";
|
||||
public static void PrintBanner() {
|
||||
System.out.println(banner);
|
||||
" ";
|
||||
public static void PrintBanner(String version) {
|
||||
System.out.println(banner+version);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,41 @@
|
||||
package kim.wind.sms.comm.constant;
|
||||
|
||||
|
||||
/**
|
||||
* Constant
|
||||
* <p> 短信应用常量
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/3/31 19:33
|
||||
**/
|
||||
public abstract class Constant {
|
||||
|
||||
/**
|
||||
* 用于格式化鉴权头域,给"Authorization"参数赋值
|
||||
*/
|
||||
public static final String HUAWEI_AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";
|
||||
/**
|
||||
* 用于格式化鉴权头域,给"X-WSSE"参数赋值
|
||||
*/
|
||||
public static final String HUAWEI_WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
|
||||
/**
|
||||
* 华为云国内短信访问URI
|
||||
*/
|
||||
public static final String HUAWEI_REQUEST_URL = "/sms/batchSendSms/v1";
|
||||
/**
|
||||
* Content-Type
|
||||
*/
|
||||
public static final String FROM_URLENCODED = "application/x-www-form-urlencoded";
|
||||
|
||||
/**
|
||||
* 华为云规定 java时间格式
|
||||
*/
|
||||
public static final String HUAWEI_JAVA_DATE = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
|
||||
/** 云片短信国内短信请求地址*/
|
||||
public static final String YUNPIAN_URL = "https://sms.yunpian.com/v2";
|
||||
|
||||
|
||||
private Constant() {
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@ import lombok.Data;
|
||||
@Data
|
||||
public class SmsResponse {
|
||||
/** 状态码*/
|
||||
private Integer code;
|
||||
private String code;
|
||||
/** 返回消息*/
|
||||
private String message;
|
||||
/** 错误码,没有错误信息则为空*/
|
||||
|
||||
@ -1,420 +0,0 @@
|
||||
package kim.wind.sms.comm.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import kim.wind.sms.comm.utils.http.OKResponse;
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -2,10 +2,12 @@ package kim.wind.sms.comm.utils;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@ -13,9 +15,28 @@ import java.util.concurrent.TimeUnit;
|
||||
@Slf4j
|
||||
public class RedisUtils {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Autowired
|
||||
public void init(RedisConnectionFactory connectionFactory){
|
||||
// 指定相应的序列化方案
|
||||
StringRedisSerializer keySerializer = new StringRedisSerializer();
|
||||
JdkSerializationRedisSerializer valueSerializer = new JdkSerializationRedisSerializer();
|
||||
// 构建StringRedisTemplate
|
||||
StringRedisTemplate stringTemplate = new StringRedisTemplate();
|
||||
stringTemplate.setConnectionFactory(connectionFactory);
|
||||
stringTemplate.afterPropertiesSet();
|
||||
// 构建RedisTemplate
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(keySerializer);
|
||||
template.setHashKeySerializer(keySerializer);
|
||||
template.setValueSerializer(valueSerializer);
|
||||
template.setHashValueSerializer(valueSerializer);
|
||||
template.afterPropertiesSet();
|
||||
this.redisTemplate = template;
|
||||
}
|
||||
|
||||
public RedisUtils() {
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package kim.wind.sms.comm.utils;
|
||||
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class SmsUtil {
|
||||
@ -81,4 +82,17 @@ public class SmsUtil {
|
||||
return !isEmpty(str);
|
||||
}
|
||||
|
||||
public static String listToString(List<String> list) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (i == list.size() - 1) {
|
||||
str.append(list.get(i));
|
||||
} else {
|
||||
str.append(list.get(i));
|
||||
str.append(",");
|
||||
}
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
package kim.wind.sms.comm.utils;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
|
||||
@ -14,6 +17,9 @@ public class SpringUtil implements ApplicationContextAware {
|
||||
|
||||
private static ApplicationContext applicationContext;
|
||||
|
||||
@Autowired
|
||||
DefaultListableBeanFactory beanFactory;
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
|
||||
if(SpringUtil.applicationContext == null) {
|
||||
@ -49,4 +55,20 @@ public class SpringUtil implements ApplicationContextAware {
|
||||
return null ;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>说明:创建一个bean
|
||||
* @name: createBean
|
||||
* @param
|
||||
* @author :Wind
|
||||
*/
|
||||
public void createBean(Class<?>clazz){
|
||||
String name = clazz.getName();
|
||||
beanFactory.createBean(clazz);
|
||||
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
|
||||
beanFactory.registerBeanDefinition(name, beanDefinitionBuilder.getBeanDefinition());
|
||||
}
|
||||
public void createBean(String name,Object o){
|
||||
beanFactory.registerSingleton(name,o);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,9 +46,7 @@ public class TimeExpiredPoolCache {
|
||||
private static synchronized void syncInit() {
|
||||
if (instance == null) {
|
||||
instance = new TimeExpiredPoolCache();
|
||||
if (!persistenceInit()){
|
||||
dataPool = new ConcurrentHashMap<String, DataWrapper<?>>();
|
||||
}
|
||||
initTimer();
|
||||
}
|
||||
}
|
||||
@ -60,12 +58,15 @@ public class TimeExpiredPoolCache {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/** 读取持久化文件*/
|
||||
/**
|
||||
* 读取持久化文件
|
||||
*/
|
||||
private static boolean persistenceInit() {
|
||||
String path = FileTool.getPath() + FILE_TYPE;
|
||||
try {
|
||||
dataPool = JSONObject.parseObject(FileTool.readFile(path), ConcurrentHashMap.class);
|
||||
if (dataPool != null){
|
||||
|
||||
DataWrapper d = JSONObject.parseObject(FileTool.readFile(path), DataWrapper.class);
|
||||
if (dataPool != null) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
@ -79,7 +80,6 @@ public class TimeExpiredPoolCache {
|
||||
public void run() {
|
||||
try {
|
||||
clearExpiredCaches();
|
||||
persistence();
|
||||
} catch (Exception e) {
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
@ -87,10 +87,11 @@ public class TimeExpiredPoolCache {
|
||||
}, timerMillis, timerMillis);
|
||||
}
|
||||
|
||||
private static void persistence(){
|
||||
/** 写入持久化文件*/
|
||||
private static void persistence() {
|
||||
String path = FileTool.getPath() + FILE_TYPE;
|
||||
FileTool.createFile(path);
|
||||
FileTool.writeFile(new File(path),JSONObject.toJSONString(dataPool),false);
|
||||
FileTool.writeFile(new File(path), JSONObject.toJSONString(dataPool), false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -0,0 +1,35 @@
|
||||
package kim.wind.sms.comm.utils.http;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class HttpJsonTool {
|
||||
|
||||
|
||||
public static <T> T getJSONBody(Object response,Class<T>t){
|
||||
return JSONObject.parseObject(response.toString(), t);
|
||||
}
|
||||
|
||||
public static <T> T getJSONBody(String response,Class<T>t){
|
||||
return JSONObject.parseObject(response, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* <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());
|
||||
}
|
||||
}
|
||||
@ -5,11 +5,11 @@
|
||||
<parent>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms_aggregation</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>sms-aggregation-huawei</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<name>sms-aggregation-huawei</name>
|
||||
<description>sms-aggregation-huawei</description>
|
||||
|
||||
|
||||
@ -1,11 +1,19 @@
|
||||
package kim.wind.sms.huawei.config;
|
||||
|
||||
import com.dtflys.forest.Forest;
|
||||
import com.dtflys.forest.config.ForestConfiguration;
|
||||
import kim.wind.sms.api.SmsBlend;
|
||||
import kim.wind.sms.huawei.service.HuaweiSmsImpl;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
|
||||
@ConfigurationProperties(prefix = "sms.huawei") //指定配置文件注入属性前缀
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "sms.huawei") //指定配置文件注入属性前缀
|
||||
@ConditionalOnProperty(name = "sms.supplier", havingValue = "huawei")
|
||||
public class HuaweiSmsConfig {
|
||||
|
||||
/** appKey*/
|
||||
@ -22,5 +30,17 @@ public class HuaweiSmsConfig {
|
||||
private String statusCallBack;
|
||||
/** APP接入地址*/
|
||||
private String url;
|
||||
/** 是否打印http请求日志*/
|
||||
private Boolean httpLog = false;
|
||||
|
||||
@Bean("forestConfiguration")
|
||||
public ForestConfiguration forestConfiguration(){
|
||||
return Forest.config().setBackendName("httpclient").setLogEnabled(httpLog);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SmsBlend smsBlend (){
|
||||
return new HuaweiSmsImpl();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
package kim.wind.sms.huawei.constant;
|
||||
|
||||
|
||||
/**
|
||||
* Constant
|
||||
* <p> 华为云短信应用常量
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/3/31 19:33
|
||||
**/
|
||||
public abstract class Constant {
|
||||
/**
|
||||
* 用于格式化鉴权头域,给"Authorization"参数赋值
|
||||
*/
|
||||
public static final String AUTH_HEADER_VALUE = "WSSE realm=\"SDP\",profile=\"UsernameToken\",type=\"Appkey\"";
|
||||
/**
|
||||
* 用于格式化鉴权头域,给"X-WSSE"参数赋值
|
||||
*/
|
||||
public static final String WSSE_HEADER_FORMAT = "UsernameToken Username=\"%s\",PasswordDigest=\"%s\",Nonce=\"%s\",Created=\"%s\"";
|
||||
/**
|
||||
* 访问URI
|
||||
*/
|
||||
public static final String REQUEST_URL = "/sms/batchSendSms/v1";
|
||||
/**
|
||||
* Content-Type
|
||||
*/
|
||||
public static final String CONTENT_TYPE = "application/x-www-form-urlencoded";
|
||||
/**
|
||||
* 华为云规定 java时间格式
|
||||
*/
|
||||
public static final String JAVA_DATE = "yyyy-MM-dd'T'HH:mm:ss'Z'";
|
||||
private Constant() {
|
||||
}
|
||||
}
|
||||
@ -53,6 +53,6 @@ public enum HuaweiError {
|
||||
case "E200041":
|
||||
return E200041.getValue();
|
||||
}
|
||||
return "";
|
||||
return "服务异常,请查看官方异常码,异常码:"+code;
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,5 +22,5 @@ public class HuaweiResponse {
|
||||
|
||||
/** 短信ID列表,当目的号码存在多个时,每个号码都会返回一个SmsID。
|
||||
当返回异常响应时不携带此字段*/
|
||||
private List<SmsId> smsId;
|
||||
private List<SmsId> result;
|
||||
}
|
||||
|
||||
@ -1,27 +1,25 @@
|
||||
package kim.wind.sms.huawei.service;
|
||||
|
||||
import com.dtflys.forest.config.ForestConfiguration;
|
||||
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.constant.Constant;
|
||||
import kim.wind.sms.comm.delayedTime.DelayedTime;
|
||||
import kim.wind.sms.comm.entity.SmsResponse;
|
||||
import kim.wind.sms.comm.utils.HTTPUtils;
|
||||
import kim.wind.sms.comm.utils.http.OKResponse;
|
||||
import kim.wind.sms.huawei.config.HuaweiSmsConfig;
|
||||
import kim.wind.sms.huawei.constant.Constant;
|
||||
import kim.wind.sms.huawei.entity.HuaweiError;
|
||||
import kim.wind.sms.huawei.entity.HuaweiResponse;
|
||||
import kim.wind.sms.huawei.entity.SmsId;
|
||||
import kim.wind.sms.huawei.utils.HuaweiBuilder;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import static kim.wind.sms.comm.utils.SmsUtil.listToString;
|
||||
|
||||
@EnableConfigurationProperties({HuaweiSmsConfig.class})
|
||||
@Slf4j
|
||||
public class HuaweiSmsImpl implements SmsBlend {
|
||||
@ -37,17 +35,21 @@ public class HuaweiSmsImpl implements SmsBlend {
|
||||
private DelayedTime delayed;
|
||||
|
||||
@Autowired
|
||||
private HTTPUtils http ;
|
||||
@Qualifier("forestConfiguration")
|
||||
private ForestConfiguration http;
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
|
||||
|
||||
return null;
|
||||
LinkedHashMap<String,String> mes = new LinkedHashMap<>();
|
||||
mes.put(UUID.randomUUID().toString().replaceAll("-",""),message);
|
||||
return sendMessage(phone,config.getTemplateId(),mes);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
String url = config.getUrl() + Constant.HUAWEI_REQUEST_URL;
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
list.add(entry.getValue());
|
||||
@ -55,73 +57,114 @@ public class HuaweiSmsImpl implements SmsBlend {
|
||||
String mess = HuaweiBuilder.listToString(list);
|
||||
String requestBody = HuaweiBuilder.buildRequestBody(config.getSender(), phone, config.getTemplateId(), mess, config.getStatusCallBack(), config.getSignature());
|
||||
Map<String,String> headers = new LinkedHashMap<>();
|
||||
headers.put("Authorization",Constant.AUTH_HEADER_VALUE);
|
||||
headers.put("Authorization",Constant.HUAWEI_AUTH_HEADER_VALUE);
|
||||
headers.put("X-WSSE",HuaweiBuilder.buildWsseHeader(config.getAppKey(), config.getAppSecret()));
|
||||
OKResponse response = http.setBaseURL(config.getUrl()).builder()
|
||||
.setMediaType(Constant.CONTENT_TYPE)
|
||||
.headers(headers)
|
||||
.post(Constant.REQUEST_URL, requestBody)
|
||||
.sync();
|
||||
HuaweiResponse jsonBody = response.getJSONBody(HuaweiResponse.class);
|
||||
headers.put("Content-Type",Constant.FROM_URLENCODED);
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setCode(response.getCode());
|
||||
http.post(url)
|
||||
.addHeader(headers)
|
||||
.addBody(requestBody)
|
||||
.onSuccess(((data,req,res)->{
|
||||
HuaweiResponse jsonBody = res.get(HuaweiResponse.class);
|
||||
smsResponse.setCode(jsonBody.getCode());
|
||||
smsResponse.setMessage(jsonBody.getDescription());
|
||||
SmsId smsId = jsonBody.getSmsId().get(0);
|
||||
smsResponse.setBizId(smsId.getSmsMsgId());
|
||||
smsResponse.setData(jsonBody);
|
||||
if (response.getCode() != 200){
|
||||
smsResponse.setErrMessage(HuaweiError.getValue(smsId.getStatus()));
|
||||
}
|
||||
smsResponse.setBizId(jsonBody.getResult().get(0).getSmsMsgId());
|
||||
smsResponse.setData(jsonBody.getResult());
|
||||
}))
|
||||
.onError((ex,req,res)->{
|
||||
HuaweiResponse huaweiResponse = res.get(HuaweiResponse.class);
|
||||
smsResponse.setErrMessage(huaweiResponse.getDescription());
|
||||
smsResponse.setErrorCode(huaweiResponse.getCode());
|
||||
})
|
||||
.execute();
|
||||
return smsResponse;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
return null;
|
||||
return sendMessage(listToString(phones),message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
return null;
|
||||
return sendMessage(listToString(phones), templateId, messages);
|
||||
}
|
||||
|
||||
@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 sendMessageAsync(String phone, String message) {
|
||||
|
||||
pool.execute(() -> sendMessage(phone, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages, CallBack callBack) {
|
||||
|
||||
pool.execute(() -> {
|
||||
SmsResponse smsResponse = sendMessage(phone, templateId, messages);
|
||||
callBack.callBack(smsResponse);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
|
||||
pool.execute(() -> {
|
||||
sendMessage(phone, templateId, messages);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public void delayedMessage(String phone, String message, Long delayedTime) {
|
||||
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendMessage(phone, message);
|
||||
}
|
||||
}, delayedTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
|
||||
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendMessage(phone, templateId, messages);
|
||||
}
|
||||
}, delayedTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
|
||||
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
massTexting(phones, message);
|
||||
}
|
||||
}, delayedTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
|
||||
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
massTexting(phones, templateId, messages);
|
||||
}
|
||||
}, delayedTime);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package kim.wind.sms.huawei.service;
|
||||
package kim.wind.sms.huawei.utils;
|
||||
|
||||
import kim.wind.sms.huawei.constant.Constant;
|
||||
import kim.wind.sms.huawei.entity.HuaweiError;
|
||||
import kim.wind.sms.comm.constant.Constant;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
@ -25,7 +24,7 @@ public class HuaweiBuilder {
|
||||
* <p>构造X-WSSE参数值
|
||||
* @author :Wind
|
||||
*/
|
||||
static String buildWsseHeader(String appKey, String appSecret) {
|
||||
public static String buildWsseHeader(String appKey, String appSecret) {
|
||||
if (null == appKey || null == appSecret || appKey.isEmpty() || appSecret.isEmpty()) {
|
||||
System.out.println("buildWsseHeader(): appKey or appSecret is null.");
|
||||
return null;
|
||||
@ -46,7 +45,7 @@ public class HuaweiBuilder {
|
||||
String passwordDigestBase64Str = Base64.getEncoder().encodeToString(passwordDigest); //PasswordDigest
|
||||
//若passwordDigestBase64Str中包含换行符,请执行如下代码进行修正
|
||||
//passwordDigestBase64Str = passwordDigestBase64Str.replaceAll("[\\s*\t\n\r]", "");
|
||||
return String.format(Constant.WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
|
||||
return String.format(Constant.HUAWEI_WSSE_HEADER_FORMAT, appKey, passwordDigestBase64Str, nonce, time);
|
||||
}
|
||||
|
||||
static void trustAllHttpsCertificates() throws Exception {
|
||||
@ -79,7 +78,7 @@ public class HuaweiBuilder {
|
||||
* @param signature | 签名名称,使用国内短信通用模板时填写
|
||||
* @author :Wind
|
||||
*/
|
||||
static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
|
||||
public static String buildRequestBody(String sender, String receiver, String templateId, String templateParas,
|
||||
String statusCallBack, String signature) {
|
||||
if (null == sender || null == receiver || null == templateId || sender.isEmpty() || receiver.isEmpty()
|
||||
|| templateId.isEmpty()) {
|
||||
@ -116,7 +115,7 @@ public class HuaweiBuilder {
|
||||
return sb.deleteCharAt(sb.length()-1).toString();
|
||||
}
|
||||
|
||||
static String listToString(List<String> list){
|
||||
public static String listToString(List<String> list){
|
||||
StringBuilder stringBuffer = new StringBuilder();
|
||||
stringBuffer.append("[\"");
|
||||
for (String s : list) {
|
||||
@ -130,12 +129,12 @@ public class HuaweiBuilder {
|
||||
}
|
||||
|
||||
static String dateFormat(Date date){
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(Constant.JAVA_DATE);
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(Constant.HUAWEI_JAVA_DATE);
|
||||
return sdf.format(date);
|
||||
}
|
||||
|
||||
static Date strForDate(String date){
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(Constant.JAVA_DATE);
|
||||
SimpleDateFormat sdf = new SimpleDateFormat(Constant.HUAWEI_JAVA_DATE);
|
||||
try {
|
||||
return sdf.parse(date);
|
||||
} catch (ParseException e) {
|
||||
@ -5,13 +5,13 @@
|
||||
<parent>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms_aggregation</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>sms-aggregation-spring-boot-starter</artifactId>
|
||||
<name>sms-aggregation-spring-boot-starter</name>
|
||||
<description>sms-aggregation-spring-boot-starter</description>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
@ -26,6 +26,11 @@
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms-aggregation-autoimmit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
@ -33,46 +38,13 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms-aggregation-aliyun</artifactId>
|
||||
<artifactId>sms-aggregation-autoimmit</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms-aggregation-tencent</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms-aggregation-unisms</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms-aggregation-yunpian</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms-aggregation-huawei</artifactId>
|
||||
</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>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@ -1,148 +1,26 @@
|
||||
package kim.wind.sms.starter.config;
|
||||
|
||||
import kim.wind.sms.comm.utils.HTTPUtils;
|
||||
import kim.wind.sms.huawei.config.HuaweiSmsConfig;
|
||||
import kim.wind.sms.huawei.service.HuaweiSmsImpl;
|
||||
import kim.wind.sms.unisms.service.UniSmsImpl;
|
||||
import kim.wind.sms.aliyun.service.AlibabaSmsImpl;
|
||||
import kim.wind.sms.api.SmsBlend;
|
||||
import kim.wind.sms.comm.config.SmsBanner;
|
||||
import kim.wind.sms.comm.delayedTime.DelayedTime;
|
||||
import kim.wind.sms.comm.utils.RedisUtils;
|
||||
import kim.wind.sms.autoimmit.config.SmsAutowiredConfig;
|
||||
import kim.wind.sms.comm.utils.SpringUtil;
|
||||
import kim.wind.sms.tencent.service.TencentSmsImpl;
|
||||
import kim.wind.sms.yunpian.service.YunPianSmsImpl;
|
||||
import lombok.Data;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
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
|
||||
@ConfigurationProperties(prefix = "sms") //指定配置文件注入属性前缀
|
||||
@EnableAsync
|
||||
@Data
|
||||
public class SmsMainConfig {
|
||||
|
||||
/** 短信服务商*/
|
||||
@Value("${sms.supplier}")
|
||||
private String supplier;
|
||||
/** 打印banner*/
|
||||
private String isPrint = "true";
|
||||
|
||||
/** 是否开启短信限制*/
|
||||
private String restricted;
|
||||
|
||||
/** 是否使用redis进行缓存*/
|
||||
private String redisCache = "false";
|
||||
|
||||
/** 单账号每日最大发送量*/
|
||||
private Integer accountMax;
|
||||
|
||||
/** 单账号每分钟最大发送*/
|
||||
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
|
||||
public SpringUtil springUtil(){
|
||||
return new SpringUtil();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "sms", name = "supplier", havingValue = "huawei")
|
||||
public HuaweiSmsConfig huaweiSmsConfig(){
|
||||
return new HuaweiSmsConfig();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public HTTPUtils okhttpBean(){
|
||||
return new HTTPUtils();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "sms", name = "restricted", havingValue = "true")
|
||||
public AopAdvice aopAdvice(){
|
||||
return new AopAdvice();
|
||||
}
|
||||
|
||||
/** 如果启用了redis作为缓存则注入redis工具类*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "sms", name = "redisCache", havingValue = "true")
|
||||
public RedisUtils redisUtils(){
|
||||
return new RedisUtils();
|
||||
}
|
||||
|
||||
/** 注入一个定时器*/
|
||||
@Bean
|
||||
public DelayedTime delayedTime(){
|
||||
return new DelayedTime();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SmsBlend smsBlend(){
|
||||
SmsBlend smsBlend = null;
|
||||
switch (supplier){
|
||||
case "alibaba":
|
||||
smsBlend = new AlibabaSmsImpl();
|
||||
break;
|
||||
case "uniSms":
|
||||
smsBlend = new UniSmsImpl();
|
||||
break;
|
||||
case "yunpian":
|
||||
smsBlend = new YunPianSmsImpl();
|
||||
break;
|
||||
case "tencent":
|
||||
smsBlend = new TencentSmsImpl();
|
||||
break;
|
||||
case "huawei":
|
||||
smsBlend = new HuaweiSmsImpl();
|
||||
break;
|
||||
}
|
||||
if ("true".equals(isPrint)){
|
||||
SmsBanner.PrintBanner();
|
||||
}
|
||||
return smsBlend;
|
||||
}
|
||||
|
||||
@Bean("smsExecutor")
|
||||
protected Executor taskExecutor(){
|
||||
// 创建一个线程池对象
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(corePoolSize);
|
||||
executor.setMaxPoolSize(maxPoolSize);
|
||||
executor.setQueueCapacity(queueCapacity);
|
||||
executor.setKeepAliveSeconds(keepAliveSeconds);
|
||||
executor.setThreadNamePrefix(threadNamePrefix);
|
||||
executor.setWaitForTasksToCompleteOnShutdown(true);
|
||||
// 线程池对拒绝任务的处理策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
//初始化线程池
|
||||
executor.initialize();
|
||||
return executor;
|
||||
/** 主要配置注入*/
|
||||
@Bean(initMethod = "init")
|
||||
public SmsAutowiredConfig smsAutowiredConfig(){
|
||||
return new SmsAutowiredConfig();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
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,3 +1,9 @@
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
kim.wind.sms.starter.config.SmsMainConfig,\
|
||||
kim.wind.sms.aliyun.config.AlibabaSmsConfig
|
||||
kim.wind.sms.autoimmit.config.SmsAutowiredConfig,\
|
||||
kim.wind.sms.aliyun.config.AlibabaSmsConfig,\
|
||||
kim.wind.sms.huawei.config.HuaweiSmsConfig,\
|
||||
kim.wind.sms.tencent.config.TencentSmsConfig,\
|
||||
kim.wind.sms.unisms.config.UniSmsConfig,\
|
||||
kim.wind.sms.yunpian.config.YunPianSmsConfig
|
||||
|
||||
|
||||
@ -5,13 +5,13 @@
|
||||
<parent>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms_aggregation</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>sms-aggregation-tencent</artifactId>
|
||||
<name>sms-aggregation-tencent</name>
|
||||
<description>sms-aggregation-tencent</description>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
@ -4,16 +4,18 @@ import com.tencentcloudapi.common.Credential;
|
||||
import com.tencentcloudapi.common.profile.ClientProfile;
|
||||
import com.tencentcloudapi.common.profile.HttpProfile;
|
||||
import com.tencentcloudapi.sms.v20210111.SmsClient;
|
||||
import kim.wind.sms.api.SmsBlend;
|
||||
import kim.wind.sms.tencent.service.TencentSmsImpl;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "sms.tencent") //指定配置文件注入属性前缀
|
||||
@Data
|
||||
@ConditionalOnProperty(prefix = "sms", name = "supplier", havingValue = "tencent")
|
||||
@ConditionalOnProperty(name = "sms.supplier", havingValue = "tencent")
|
||||
public class TencentSmsConfig {
|
||||
|
||||
/** 应用accessKey*/
|
||||
@ -49,4 +51,9 @@ public class TencentSmsConfig {
|
||||
clientProfile.setHttpProfile(httpProfile);
|
||||
return new SmsClient(cred, territory,clientProfile);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SmsBlend smsBlend(){
|
||||
return new TencentSmsImpl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,13 +5,13 @@
|
||||
<parent>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms_aggregation</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>sms-aggregation-unisms</artifactId>
|
||||
<name>sms-aggregation-unisms</name>
|
||||
<description>sms-aggregation-unisms</description>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
@ -1,16 +1,18 @@
|
||||
package kim.wind.sms.unisms.config;
|
||||
|
||||
import com.apistd.uni.Uni;
|
||||
import kim.wind.sms.api.SmsBlend;
|
||||
import kim.wind.sms.unisms.service.UniSmsImpl;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "sms.uni-sms") //指定配置文件注入属性前缀
|
||||
@Data
|
||||
@ConditionalOnProperty(prefix = "sms", name = "supplier", havingValue = "uni-sms")
|
||||
@ConditionalOnProperty(name = "sms.supplier", havingValue = "uniSms")
|
||||
public class UniSmsConfig {
|
||||
|
||||
/** 访问键标识*/
|
||||
@ -35,4 +37,9 @@ public class UniSmsConfig {
|
||||
Uni.init(accessKeyId,accessKeySecret);
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SmsBlend smsBlend(){
|
||||
return new UniSmsImpl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,14 +3,14 @@ package kim.wind.sms.unisms.service;
|
||||
import com.apistd.uni.UniResponse;
|
||||
import com.apistd.uni.sms.UniMessage;
|
||||
import com.apistd.uni.sms.UniSMS;
|
||||
import kim.wind.sms.unisms.config.UniSmsConfig;
|
||||
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.delayedTime.DelayedTime;
|
||||
import kim.wind.sms.comm.entity.SmsResponse;
|
||||
import kim.wind.sms.comm.exception.SmsBlendException;
|
||||
import kim.wind.sms.comm.utils.HTTPUtils;
|
||||
import kim.wind.sms.comm.utils.http.HttpJsonTool;
|
||||
import kim.wind.sms.unisms.config.UniSmsConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
@ -169,7 +169,7 @@ public class UniSmsImpl implements SmsBlend {
|
||||
smsResponse.setErrorCode(send.code);
|
||||
smsResponse.setMessage(send.message);
|
||||
smsResponse.setBizId(send.requestId);
|
||||
smsResponse.setData(HTTPUtils.getJSONObject(send));
|
||||
smsResponse.setData(HttpJsonTool.getJSONObject(send));
|
||||
}catch(Exception e){
|
||||
smsResponse.setErrMessage(e.getMessage());
|
||||
}
|
||||
|
||||
@ -5,14 +5,14 @@
|
||||
<parent>
|
||||
<groupId>kim.wind</groupId>
|
||||
<artifactId>sms_aggregation</artifactId>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>sms-aggregation-yunpian</artifactId>
|
||||
<name>sms-aggregation-yunpian</name>
|
||||
<description>sms-aggregation-yunpian</description>
|
||||
<version>1.0.2</version>
|
||||
<version>1.0.3</version>
|
||||
<properties>
|
||||
<java.version>1.8</java.version>
|
||||
</properties>
|
||||
|
||||
@ -1,16 +1,52 @@
|
||||
package kim.wind.sms.yunpian.config;
|
||||
|
||||
import com.dtflys.forest.Forest;
|
||||
import com.dtflys.forest.config.ForestConfiguration;
|
||||
import kim.wind.sms.api.SmsBlend;
|
||||
import kim.wind.sms.yunpian.service.YunPianSmsImpl;
|
||||
import lombok.Data;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Data
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "sms.yunpian") //指定配置文件注入属性前缀
|
||||
@Data
|
||||
@ConditionalOnProperty(prefix = "sms", name = "supplier", havingValue = "yunpian")
|
||||
@ConditionalOnProperty(name = "sms.supplier", havingValue = "yunpian")
|
||||
public class YunPianSmsConfig {
|
||||
/** 账号唯一标识*/
|
||||
/**
|
||||
* 账号唯一标识
|
||||
*/
|
||||
private String apikey;
|
||||
|
||||
/**
|
||||
* 短信发送后将向这个地址推送(运营商返回的)发送报告
|
||||
*/
|
||||
private String callbackUrl;
|
||||
|
||||
/**
|
||||
* 模板Id
|
||||
*/
|
||||
private String templateId;
|
||||
|
||||
/**
|
||||
* 模板变量名称
|
||||
*/
|
||||
private String templateName;
|
||||
|
||||
/**
|
||||
* 是否打印http请求日志
|
||||
*/
|
||||
private Boolean httpLog = false;
|
||||
|
||||
@Bean
|
||||
public ForestConfiguration forestConfiguration() {
|
||||
return Forest.config().setBackendName("httpclient").setLogEnabled(httpLog);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SmsBlend smsBlend() {
|
||||
return new YunPianSmsImpl();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,24 @@
|
||||
package kim.wind.sms.yunpian.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dtflys.forest.config.ForestConfiguration;
|
||||
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.constant.Constant;
|
||||
import kim.wind.sms.comm.delayedTime.DelayedTime;
|
||||
import kim.wind.sms.comm.entity.SmsResponse;
|
||||
import kim.wind.sms.comm.exception.SmsBlendException;
|
||||
import kim.wind.sms.comm.utils.HTTPUtils;
|
||||
import kim.wind.sms.comm.utils.http.OKResponse;
|
||||
import kim.wind.sms.yunpian.config.YunPianSmsConfig;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static kim.wind.sms.comm.utils.SmsUtil.listToString;
|
||||
|
||||
public class YunPianSmsImpl implements SmsBlend {
|
||||
|
||||
@ -26,75 +30,42 @@ public class YunPianSmsImpl implements SmsBlend {
|
||||
private DelayedTime delayed;
|
||||
|
||||
@Autowired
|
||||
private HTTPUtils http ;
|
||||
private YunPianSmsConfig config;
|
||||
|
||||
@Autowired
|
||||
private YunPianSmsConfig config;
|
||||
private ForestConfiguration http;
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
Map<String,String> body = new HashMap<>();
|
||||
body.put("apikey",config.getApikey());
|
||||
body.put("mobile",phone);
|
||||
return getSmsResponse(message, body);
|
||||
Map<String, String> body = setBody(phone, message, null);
|
||||
return getSendResponse(body);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
Map<String,String> body = new HashMap<>();
|
||||
body.put("apikey",config.getApikey());
|
||||
body.put("mobile",phone);
|
||||
body.put("tpl_id",templateId);
|
||||
body.put("tpl_value",formattingMap(messages));
|
||||
Map<String,String> map = new HashMap<>();
|
||||
map.put("Accept","application/json;charset=utf-8");
|
||||
OKResponse sync = http.setBaseURL("https://sms.yunpian.com/v2").builder()
|
||||
.headers(map)
|
||||
.postOrBody("/sms/tpl_single_send.json", body)
|
||||
.sync();
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setCode(sync.getCode());
|
||||
JSONObject jsonObject = HTTPUtils.getJSONObject(sync);
|
||||
smsResponse.setData(jsonObject);
|
||||
return smsResponse;
|
||||
Map<String, String> body = setBody(phone, "", messages);
|
||||
return getSendResponse(body);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
if (phones.size()>1000){
|
||||
if (phones.size() > 1000) {
|
||||
throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于1000");
|
||||
}
|
||||
Map<String,String> body = new HashMap<>();
|
||||
body.put("apikey",config.getApikey());
|
||||
body.put("mobile",listToString(phones));
|
||||
return getSmsResponse(message, body);
|
||||
return sendMessage(listToString(phones),message);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
if (phones.size()>1000){
|
||||
if (phones.size() > 1000) {
|
||||
throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于1000");
|
||||
}
|
||||
Map<String,String> body = new HashMap<>();
|
||||
body.put("apikey",config.getApikey());
|
||||
body.put("mobile",listToString(phones));
|
||||
body.put("tpl_id",templateId);
|
||||
body.put("tpl_value",formattingMap(messages));
|
||||
Map<String,String> map = new HashMap<>();
|
||||
map.put("Accept","application/json;charset=utf-8");
|
||||
OKResponse sync = http.setBaseURL("https://sms.yunpian.com/v2").builder()
|
||||
.headers(map)
|
||||
.postOrBody("/tpl_batch_send.json", body)
|
||||
.sync();
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setCode(sync.getCode());
|
||||
JSONObject jsonObject = HTTPUtils.getJSONObject(sync);
|
||||
smsResponse.setData(jsonObject);
|
||||
return smsResponse;
|
||||
return sendMessage(listToString(phones), templateId, messages);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -115,8 +86,8 @@ public class YunPianSmsImpl implements SmsBlend {
|
||||
@Override
|
||||
@Restricted
|
||||
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages, CallBack callBack) {
|
||||
pool.execute(()->{
|
||||
SmsResponse smsResponse = sendMessage(phone,templateId,messages);
|
||||
pool.execute(() -> {
|
||||
SmsResponse smsResponse = sendMessage(phone, templateId, messages);
|
||||
callBack.callBack(smsResponse);
|
||||
});
|
||||
}
|
||||
@ -124,8 +95,8 @@ public class YunPianSmsImpl implements SmsBlend {
|
||||
@Override
|
||||
@Restricted
|
||||
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
pool.execute(()->{
|
||||
sendMessage(phone,templateId,messages);
|
||||
pool.execute(() -> {
|
||||
sendMessage(phone, templateId, messages);
|
||||
});
|
||||
}
|
||||
|
||||
@ -135,9 +106,9 @@ public class YunPianSmsImpl implements SmsBlend {
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendMessage(phone,message);
|
||||
sendMessage(phone, message);
|
||||
}
|
||||
},delayedTime);
|
||||
}, delayedTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -146,9 +117,9 @@ public class YunPianSmsImpl implements SmsBlend {
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
sendMessage(phone,templateId,messages);
|
||||
sendMessage(phone, templateId, messages);
|
||||
}
|
||||
},delayedTime);
|
||||
}, delayedTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -157,9 +128,9 @@ public class YunPianSmsImpl implements SmsBlend {
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
massTexting(phones,message);
|
||||
massTexting(phones, message);
|
||||
}
|
||||
},delayedTime);
|
||||
}, delayedTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -168,49 +139,76 @@ public class YunPianSmsImpl implements SmsBlend {
|
||||
this.delayed.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
massTexting(phones,templateId,messages);
|
||||
massTexting(phones, templateId, messages);
|
||||
}
|
||||
},delayedTime);
|
||||
}, delayedTime);
|
||||
}
|
||||
|
||||
private String formattingMap(Map<String,String> messages){
|
||||
private String formattingMap(Map<String, String> messages) {
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (Map.Entry<String,String> entry : messages.entrySet()) {
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
str.append("#");
|
||||
str.append(entry.getKey());
|
||||
str.append("#=");
|
||||
str.append(entry.getValue());
|
||||
str.append("&");
|
||||
}
|
||||
str.deleteCharAt(str.length()-1);
|
||||
str.deleteCharAt(str.length() - 1);
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
private String listToString(List<String> list){
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
if (i == list.size() - 1) {
|
||||
str.append(list.get(i));
|
||||
private Map<String, String> setBody(String phone, String mes, LinkedHashMap<String, String> messages) {
|
||||
LinkedHashMap<String, String> message = new LinkedHashMap<>();
|
||||
if (mes.isEmpty()) {
|
||||
message = messages;
|
||||
} else {
|
||||
str.append(list.get(i));
|
||||
str.append(",");
|
||||
message.put(config.getTemplateName(), mes);
|
||||
}
|
||||
}
|
||||
return str.toString();
|
||||
Map<String, String> body = new HashMap<>();
|
||||
body.put("apikey", config.getApikey());
|
||||
body.put("mobile", phone);
|
||||
body.put("tpl_id", config.getTemplateId());
|
||||
body.put("tpl_value", formattingMap(message));
|
||||
if (!config.getCallbackUrl().isEmpty()) body.put("callback_url", config.getCallbackUrl());
|
||||
return body;
|
||||
}
|
||||
|
||||
private SmsResponse getSmsResponse(String message, Map<String, String> body) {
|
||||
body.put("text",message);
|
||||
Map<String,String> map = new HashMap<>();
|
||||
map.put("Accept","application/json;charset=utf-8");
|
||||
OKResponse sync = http.setBaseURL("http://sms.yunpian.com/v2").builder()
|
||||
.headers(map)
|
||||
.postOrBody("/sms/single_send.json", body)
|
||||
.sync();
|
||||
private Map<String, String> getHeaders() {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
headers.put("Accept", "application/json;charset=utf-8");
|
||||
headers.put("Content-Type", Constant.FROM_URLENCODED);
|
||||
return headers;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static SmsResponse getSmsResponse(JSONObject execute) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
smsResponse.setCode(sync.getCode());
|
||||
JSONObject jsonObject = HTTPUtils.getJSONObject(sync);
|
||||
smsResponse.setData(jsonObject);
|
||||
smsResponse.setCode(execute.getString("code"));
|
||||
smsResponse.setMessage(execute.getString("msg"));
|
||||
smsResponse.setBizId(execute.getString("sid"));
|
||||
if (execute.getInteger("code") != 0) {
|
||||
smsResponse.setErrMessage(execute.getString("msg"));
|
||||
}
|
||||
smsResponse.setData(execute);
|
||||
return smsResponse;
|
||||
}
|
||||
|
||||
private SmsResponse getSendResponse(Map<String, String> body) {
|
||||
Map<String, String> headers = getHeaders();
|
||||
AtomicReference<SmsResponse> smsResponse = null;
|
||||
http.post(Constant.YUNPIAN_URL + "/sms/tpl_single_send.json")
|
||||
.addHeader(headers)
|
||||
.addBody(body)
|
||||
.onSuccess(((data,req,res)->{
|
||||
JSONObject jsonBody = res.get(JSONObject.class);
|
||||
smsResponse.set(getSmsResponse(jsonBody));
|
||||
}))
|
||||
.onError((ex,req,res)->{
|
||||
JSONObject jsonBody = res.get(JSONObject.class);
|
||||
smsResponse.set(getSmsResponse(jsonBody));
|
||||
})
|
||||
.execute();
|
||||
|
||||
return smsResponse.get();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user