mirror of
https://gitee.com/dromara/sms4j.git
synced 2025-12-06 17:08:40 +08:00
🔥 重构项目结构,新增 sms4j-provider 短信厂商对接模块(国内短信)
This commit is contained in:
parent
76fafd56d5
commit
ce720abd07
55
pom.xml
55
pom.xml
@ -2,7 +2,6 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j</artifactId>
|
||||
<version>${revision}</version>
|
||||
@ -15,17 +14,10 @@
|
||||
<modules>
|
||||
<module>sms4j-comm</module>
|
||||
<module>sms4j-api</module>
|
||||
<module>sms4j-provider</module>
|
||||
<module>sms4j-core</module>
|
||||
<module>sms4j-autoimmit</module>
|
||||
<module>sms4j-spring-boot-starter</module>
|
||||
<module>sms4j-aliyun</module>
|
||||
<module>sms4j-tencent</module>
|
||||
<module>sms4j-unisms</module>
|
||||
<module>sms4j-yunpian</module>
|
||||
<module>sms4j-huawei</module>
|
||||
<module>sms4j-jdcloud</module>
|
||||
<module>sms4j-cloopen</module>
|
||||
<module>sms4j-emay</module>
|
||||
</modules>
|
||||
|
||||
<!-- 开源协议 Apache 2.0 -->
|
||||
@ -117,51 +109,10 @@
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 短信厂商对接模块(国内短信) -->
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-aliyun</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-tencent</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-unisms</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-yunpian</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-huawei</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-jdcloud</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-cloopen</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-emay</artifactId>
|
||||
<artifactId>sms4j-provider</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-aliyun</artifactId>
|
||||
<name>sms4j-aliyun</name>
|
||||
<description>sms4j-aliyun</description>
|
||||
<version>${revision}</version>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-comm</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>3.14.9</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -10,12 +10,9 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-api</artifactId>
|
||||
|
||||
<name>sms4j-api</name>
|
||||
<description>sms4j-api</description>
|
||||
<version>${revision}</version>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
|
||||
@ -10,13 +10,10 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-autoimmit</artifactId>
|
||||
<version>${revision}</version>
|
||||
|
||||
<name>sms4j-autoimmit</name>
|
||||
<description>sms4j-autoimmit</description>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!--aop依赖-->
|
||||
<dependency>
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-cloopen</artifactId>
|
||||
<name>sms4j-cloopen</name>
|
||||
<description>sms4j-cloopen</description>
|
||||
<version>${revision}</version>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-comm</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -10,15 +10,11 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-comm</artifactId>
|
||||
|
||||
<name>sms4j-comm</name>
|
||||
<description>sms4j-comm</description>
|
||||
<version>${revision}</version>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
|
||||
@ -10,57 +10,14 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-core</artifactId>
|
||||
<version>${revision}</version>
|
||||
|
||||
<name>sms4j-core</name>
|
||||
<description>sms4j-core</description>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-aliyun</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-tencent</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-unisms</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-yunpian</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-huawei</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-jdcloud</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-emay</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-cloopen</artifactId>
|
||||
<artifactId>sms4j-provider</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
package org.dromara.sms4j.core.factory;
|
||||
|
||||
import org.dromara.sms4j.comm.config.SmsConfig;
|
||||
import org.dromara.sms4j.comm.enumerate.ConfigType;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.core.SupplierSqlConfig;
|
||||
import org.dromara.sms4j.emay.config.EmaySmsConfig;
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaSmsConfig;
|
||||
|
||||
@ -1,31 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-emay</artifactId>
|
||||
<version>${revision}</version>
|
||||
<name>sms4j-emay</name>
|
||||
<description>sms4j-emay</description>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-comm</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-huawei</artifactId>
|
||||
<version>${revision}</version>
|
||||
<name>sms4j-huawei</name>
|
||||
<description>sms4j-huawei</description>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-comm</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
</build>
|
||||
</project>
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -7,29 +7,29 @@
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-jdcloud</artifactId>
|
||||
<name>sms4j-jdcloud</name>
|
||||
<description>sms4j-jdcloud</description>
|
||||
<version>${revision}</version>
|
||||
<artifactId>sms4j-provider</artifactId>
|
||||
|
||||
<name>sms4j-provider</name>
|
||||
<description>短信厂商对接模块(国内短信)</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 合一短信依赖 -->
|
||||
<dependency>
|
||||
<groupId>com.apistd.uni</groupId>
|
||||
<artifactId>uni-sdk</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 京东云短信依赖 -->
|
||||
<dependency>
|
||||
<groupId>com.jdcloud.sdk</groupId>
|
||||
<artifactId>sms</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-comm</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1,44 +1,44 @@
|
||||
package org.dromara.sms4j.aliyun.config;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AlibabaConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 模板变量名称
|
||||
*/
|
||||
private String templateName;
|
||||
|
||||
/**
|
||||
* 请求地址
|
||||
*/
|
||||
@Builder.Default
|
||||
private String requestUrl = "dysmsapi.aliyuncs.com";
|
||||
|
||||
/**
|
||||
* 接口名称
|
||||
*/
|
||||
@Builder.Default
|
||||
private String action = "SendSms";
|
||||
|
||||
/**
|
||||
* 接口版本号
|
||||
*/
|
||||
@Builder.Default
|
||||
private String version = "2017-05-25";
|
||||
|
||||
/**
|
||||
* 地域信息默认为 cn-hangzhou
|
||||
*/
|
||||
@Builder.Default
|
||||
private String regionId = "cn-hangzhou";
|
||||
}
|
||||
package org.dromara.sms4j.aliyun.config;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class AlibabaConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 模板变量名称
|
||||
*/
|
||||
private String templateName;
|
||||
|
||||
/**
|
||||
* 请求地址
|
||||
*/
|
||||
@Builder.Default
|
||||
private String requestUrl = "dysmsapi.aliyuncs.com";
|
||||
|
||||
/**
|
||||
* 接口名称
|
||||
*/
|
||||
@Builder.Default
|
||||
private String action = "SendSms";
|
||||
|
||||
/**
|
||||
* 接口版本号
|
||||
*/
|
||||
@Builder.Default
|
||||
private String version = "2017-05-25";
|
||||
|
||||
/**
|
||||
* 地域信息默认为 cn-hangzhou
|
||||
*/
|
||||
@Builder.Default
|
||||
private String regionId = "cn-hangzhou";
|
||||
}
|
||||
@ -1,62 +1,62 @@
|
||||
package org.dromara.sms4j.aliyun.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.aliyun.service.AlibabaSmsImpl;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* AlibabaSmsConfig
|
||||
* <p> 阿里巴巴对象建造者
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/4/8 14:54
|
||||
**/
|
||||
@Slf4j
|
||||
public class AlibabaSmsConfig {
|
||||
|
||||
private static AlibabaSmsImpl alibabaSms;
|
||||
|
||||
private static AlibabaSmsConfig alibabaSmsConfig;
|
||||
|
||||
/**
|
||||
* getAlibabaSms
|
||||
* <p> 建造一个短信实现对像
|
||||
*
|
||||
* @author :Wind
|
||||
*/
|
||||
public static AlibabaSmsImpl createAlibabaSms(AlibabaConfig alibabaConfig) {
|
||||
if (alibabaSmsConfig == null) {
|
||||
alibabaSmsConfig = new AlibabaSmsConfig();
|
||||
}
|
||||
if (alibabaSms == null) {
|
||||
alibabaSms = new AlibabaSmsImpl(
|
||||
alibabaConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime());
|
||||
}
|
||||
return alibabaSms;
|
||||
}
|
||||
|
||||
/**
|
||||
* refresh
|
||||
* <p> 刷新对象
|
||||
*
|
||||
* @author :Wind
|
||||
*/
|
||||
public static AlibabaSmsImpl refresh(AlibabaConfig alibabaConfig) {
|
||||
// 如果配置对象为空则创建一个
|
||||
if (alibabaSmsConfig == null) {
|
||||
alibabaSmsConfig = new AlibabaSmsConfig();
|
||||
}
|
||||
//重新构造一个实现对象
|
||||
alibabaSms = new AlibabaSmsImpl(
|
||||
alibabaConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime());
|
||||
return alibabaSms;
|
||||
}
|
||||
|
||||
private AlibabaSmsConfig() {
|
||||
}
|
||||
}
|
||||
package org.dromara.sms4j.aliyun.config;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.aliyun.service.AlibabaSmsImpl;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
|
||||
|
||||
/**
|
||||
* AlibabaSmsConfig
|
||||
* <p> 阿里巴巴对象建造者
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/4/8 14:54
|
||||
**/
|
||||
@Slf4j
|
||||
public class AlibabaSmsConfig {
|
||||
|
||||
private static AlibabaSmsImpl alibabaSms;
|
||||
|
||||
private static AlibabaSmsConfig alibabaSmsConfig;
|
||||
|
||||
/**
|
||||
* getAlibabaSms
|
||||
* <p> 建造一个短信实现对像
|
||||
*
|
||||
* @author :Wind
|
||||
*/
|
||||
public static AlibabaSmsImpl createAlibabaSms(AlibabaConfig alibabaConfig) {
|
||||
if (alibabaSmsConfig == null) {
|
||||
alibabaSmsConfig = new AlibabaSmsConfig();
|
||||
}
|
||||
if (alibabaSms == null) {
|
||||
alibabaSms = new AlibabaSmsImpl(
|
||||
alibabaConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime());
|
||||
}
|
||||
return alibabaSms;
|
||||
}
|
||||
|
||||
/**
|
||||
* refresh
|
||||
* <p> 刷新对象
|
||||
*
|
||||
* @author :Wind
|
||||
*/
|
||||
public static AlibabaSmsImpl refresh(AlibabaConfig alibabaConfig) {
|
||||
// 如果配置对象为空则创建一个
|
||||
if (alibabaSmsConfig == null) {
|
||||
alibabaSmsConfig = new AlibabaSmsConfig();
|
||||
}
|
||||
//重新构造一个实现对象
|
||||
alibabaSms = new AlibabaSmsImpl(
|
||||
alibabaConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime());
|
||||
return alibabaSms;
|
||||
}
|
||||
|
||||
private AlibabaSmsConfig() {
|
||||
}
|
||||
}
|
||||
@ -1,196 +1,196 @@
|
||||
package org.dromara.sms4j.aliyun.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dtflys.forest.config.ForestConfiguration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
|
||||
import org.dromara.sms4j.aliyun.utils.AliyunUtils;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.callback.CallBack;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* <p>类名: AlibabaSmsImpl
|
||||
* <p>说明: 阿里云短信实现
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/3/26 17:16
|
||||
**/
|
||||
|
||||
@Slf4j
|
||||
public class AlibabaSmsImpl implements SmsBlend {
|
||||
|
||||
private final AlibabaConfig alibabaSmsConfig;
|
||||
|
||||
private final Executor pool;
|
||||
|
||||
private final DelayedTime delayed;
|
||||
|
||||
private final ForestConfiguration http = BeanFactory.getForestConfiguration();
|
||||
|
||||
/**
|
||||
* AlibabaSmsImpl
|
||||
* <p>构造器,用于构造短信实现模块
|
||||
*
|
||||
* @author :Wind
|
||||
*/
|
||||
|
||||
public AlibabaSmsImpl(AlibabaConfig alibabaSmsConfig, Executor pool, DelayedTime delayedTime) {
|
||||
this.alibabaSmsConfig = alibabaSmsConfig;
|
||||
this.pool = pool;
|
||||
this.delayed = delayedTime;
|
||||
}
|
||||
|
||||
@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) {
|
||||
String messageStr = JSON.toJSONString(messages);
|
||||
return getSmsResponse(phone, messageStr, templateId);
|
||||
}
|
||||
|
||||
@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) {
|
||||
String messageStr = JSON.toJSONString(messages);
|
||||
return getSmsResponse(arrayToString(phones), messageStr, templateId);
|
||||
}
|
||||
|
||||
private SmsResponse getSmsResponse(String phone, String message, String templateId) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
String requestUrl;
|
||||
String paramStr;
|
||||
try {
|
||||
requestUrl = AliyunUtils.generateSendSmsRequestUrl(this.alibabaSmsConfig, message, phone, templateId);
|
||||
paramStr = AliyunUtils.generateParamBody(alibabaSmsConfig, phone, message, templateId);
|
||||
} catch (Exception e) {
|
||||
log.error("aliyun send message error", e);
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
log.info("requestUrl {}", requestUrl);
|
||||
http.post(requestUrl)
|
||||
.addHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.addBody(paramStr)
|
||||
.onSuccess(((data, req, res) -> {
|
||||
JSONObject jsonBody = res.get(JSONObject.class);
|
||||
log.info(jsonBody.toJSONString());
|
||||
}))
|
||||
.onError((ex, req, res) -> {
|
||||
JSONObject jsonBody = res.get(JSONObject.class);
|
||||
log.info(jsonBody.toJSONString());
|
||||
})
|
||||
.execute();
|
||||
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 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);
|
||||
}
|
||||
|
||||
private String arrayToString(List<String> list) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : list) {
|
||||
sb.append(",").append("+86").append(s);
|
||||
}
|
||||
return sb.substring(1);
|
||||
}
|
||||
}
|
||||
package org.dromara.sms4j.aliyun.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dtflys.forest.config.ForestConfiguration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
|
||||
import org.dromara.sms4j.aliyun.utils.AliyunUtils;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.callback.CallBack;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* <p>类名: AlibabaSmsImpl
|
||||
* <p>说明: 阿里云短信实现
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/3/26 17:16
|
||||
**/
|
||||
|
||||
@Slf4j
|
||||
public class AlibabaSmsImpl implements SmsBlend {
|
||||
|
||||
private final AlibabaConfig alibabaSmsConfig;
|
||||
|
||||
private final Executor pool;
|
||||
|
||||
private final DelayedTime delayed;
|
||||
|
||||
private final ForestConfiguration http = BeanFactory.getForestConfiguration();
|
||||
|
||||
/**
|
||||
* AlibabaSmsImpl
|
||||
* <p>构造器,用于构造短信实现模块
|
||||
*
|
||||
* @author :Wind
|
||||
*/
|
||||
|
||||
public AlibabaSmsImpl(AlibabaConfig alibabaSmsConfig, Executor pool, DelayedTime delayedTime) {
|
||||
this.alibabaSmsConfig = alibabaSmsConfig;
|
||||
this.pool = pool;
|
||||
this.delayed = delayedTime;
|
||||
}
|
||||
|
||||
@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) {
|
||||
String messageStr = JSON.toJSONString(messages);
|
||||
return getSmsResponse(phone, messageStr, templateId);
|
||||
}
|
||||
|
||||
@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) {
|
||||
String messageStr = JSON.toJSONString(messages);
|
||||
return getSmsResponse(arrayToString(phones), messageStr, templateId);
|
||||
}
|
||||
|
||||
private SmsResponse getSmsResponse(String phone, String message, String templateId) {
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
String requestUrl;
|
||||
String paramStr;
|
||||
try {
|
||||
requestUrl = AliyunUtils.generateSendSmsRequestUrl(this.alibabaSmsConfig, message, phone, templateId);
|
||||
paramStr = AliyunUtils.generateParamBody(alibabaSmsConfig, phone, message, templateId);
|
||||
} catch (Exception e) {
|
||||
log.error("aliyun send message error", e);
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
log.info("requestUrl {}", requestUrl);
|
||||
http.post(requestUrl)
|
||||
.addHeader("Content-Type", "application/x-www-form-urlencoded")
|
||||
.addBody(paramStr)
|
||||
.onSuccess(((data, req, res) -> {
|
||||
JSONObject jsonBody = res.get(JSONObject.class);
|
||||
log.info(jsonBody.toJSONString());
|
||||
}))
|
||||
.onError((ex, req, res) -> {
|
||||
JSONObject jsonBody = res.get(JSONObject.class);
|
||||
log.info(jsonBody.toJSONString());
|
||||
})
|
||||
.execute();
|
||||
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 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);
|
||||
}
|
||||
|
||||
private String arrayToString(List<String> list) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String s : list) {
|
||||
sb.append(",").append("+86").append(s);
|
||||
}
|
||||
return sb.substring(1);
|
||||
}
|
||||
}
|
||||
@ -1,124 +1,124 @@
|
||||
package org.dromara.sms4j.aliyun.utils;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Richard
|
||||
* @date 2023/4/20 16:55
|
||||
*/
|
||||
public class AliyunUtils {
|
||||
|
||||
/**
|
||||
* 加密方式
|
||||
*/
|
||||
private static final String ALGORITHM = "HMAC-SHA1";
|
||||
|
||||
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
|
||||
public static String generateSendSmsRequestUrl(AlibabaConfig alibabaConfig, String message, String phone, String templateId) throws Exception {
|
||||
// 这里一定要设置GMT时区
|
||||
sdf.setTimeZone(new SimpleTimeZone(0, "GMT"));
|
||||
Map<String, String> paras = new HashMap<>();
|
||||
// 1. 公共请求参数
|
||||
paras.put("SignatureMethod", ALGORITHM);
|
||||
paras.put("SignatureNonce", UUID.randomUUID().toString());
|
||||
paras.put("AccessKeyId", alibabaConfig.getAccessKeyId());
|
||||
paras.put("SignatureVersion", "1.0");
|
||||
paras.put("Timestamp", sdf.format(new Date()));
|
||||
paras.put("Format", "JSON");
|
||||
paras.put("Action", alibabaConfig.getAction());
|
||||
paras.put("Version", alibabaConfig.getVersion());
|
||||
paras.put("RegionId", alibabaConfig.getRegionId());
|
||||
// 2. 业务API参数
|
||||
Map<String, String> paramMap = generateParamMap(alibabaConfig, phone, message, templateId);
|
||||
// 3. 参数KEY排序
|
||||
Map<String, String> sortParas = new TreeMap<>(paras);
|
||||
sortParas.putAll(paramMap);
|
||||
// 4. 构造待签名的字符串
|
||||
Iterator<String> it = sortParas.keySet().iterator();
|
||||
StringBuilder sortQueryStringTmp = new StringBuilder();
|
||||
while (it.hasNext()) {
|
||||
String key = it.next();
|
||||
sortQueryStringTmp.append("&").append(specialUrlEncode(key)).append("=").append(specialUrlEncode(sortParas.get(key)));
|
||||
}
|
||||
|
||||
String stringToSign = "POST" + "&" +
|
||||
specialUrlEncode("/") + "&" +
|
||||
specialUrlEncode(sortQueryStringTmp.substring(1));
|
||||
String signature = sign(alibabaConfig.getAccessKeySecret() + "&", stringToSign);
|
||||
// 5. 生成请求的url参数
|
||||
StringBuilder sortQueryString = new StringBuilder();
|
||||
it = paras.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
String key = it.next();
|
||||
sortQueryString.append("&").append(specialUrlEncode(key)).append("=").append(specialUrlEncode(paras.get(key)));
|
||||
}
|
||||
// 6.生成合法请求URL
|
||||
return Constant.HTTPS_PREFIX + alibabaConfig.getRequestUrl() + "/?Signature=" + signature + sortQueryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* url编码
|
||||
*/
|
||||
private static String specialUrlEncode(String value) throws Exception {
|
||||
return URLEncoder.encode(value, StandardCharsets.UTF_8.name()).replace("+", "%20")
|
||||
.replace("*", "%2A").replace("%7E", "~");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
*
|
||||
* @param accessSecret accessSecret
|
||||
* @param stringToSign 待生成签名的字符串
|
||||
*/
|
||||
private static String sign(String accessSecret, String stringToSign) throws Exception {
|
||||
Mac mac = Mac.getInstance("HmacSHA1");
|
||||
mac.init(new javax.crypto.spec.SecretKeySpec(accessSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
|
||||
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.encode(signData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成请求body参数
|
||||
*
|
||||
* @param alibabaConfig 配置数据
|
||||
* @param phone 手机号
|
||||
* @param message 短信内容
|
||||
* @param templateId 模板id
|
||||
*/
|
||||
public static Map<String, String> generateParamMap(AlibabaConfig alibabaConfig, String phone, String message, String templateId) {
|
||||
Map<String, String> paramMap = new HashMap<>();
|
||||
paramMap.put("PhoneNumbers", phone);
|
||||
paramMap.put("SignName", alibabaConfig.getSignature());
|
||||
paramMap.put("TemplateParam", message);
|
||||
paramMap.put("TemplateCode", templateId);
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成请求参数body字符串
|
||||
*
|
||||
* @param alibabaConfig
|
||||
* @param phone
|
||||
* @param message
|
||||
* @param templateId
|
||||
*/
|
||||
public static String generateParamBody(AlibabaConfig alibabaConfig, String phone, String message, String templateId) throws Exception {
|
||||
Map<String, String> paramMap = generateParamMap(alibabaConfig, phone, message, templateId);
|
||||
StringBuilder sortQueryString = new StringBuilder();
|
||||
for (String key : paramMap.keySet()) {
|
||||
sortQueryString.append("&").append(specialUrlEncode(key)).append("=")
|
||||
.append(specialUrlEncode(paramMap.get(key)));
|
||||
}
|
||||
return sortQueryString.substring(1);
|
||||
}
|
||||
|
||||
}
|
||||
package org.dromara.sms4j.aliyun.utils;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Richard
|
||||
* @date 2023/4/20 16:55
|
||||
*/
|
||||
public class AliyunUtils {
|
||||
|
||||
/**
|
||||
* 加密方式
|
||||
*/
|
||||
private static final String ALGORITHM = "HMAC-SHA1";
|
||||
|
||||
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
|
||||
|
||||
public static String generateSendSmsRequestUrl(AlibabaConfig alibabaConfig, String message, String phone, String templateId) throws Exception {
|
||||
// 这里一定要设置GMT时区
|
||||
sdf.setTimeZone(new SimpleTimeZone(0, "GMT"));
|
||||
Map<String, String> paras = new HashMap<>();
|
||||
// 1. 公共请求参数
|
||||
paras.put("SignatureMethod", ALGORITHM);
|
||||
paras.put("SignatureNonce", UUID.randomUUID().toString());
|
||||
paras.put("AccessKeyId", alibabaConfig.getAccessKeyId());
|
||||
paras.put("SignatureVersion", "1.0");
|
||||
paras.put("Timestamp", sdf.format(new Date()));
|
||||
paras.put("Format", "JSON");
|
||||
paras.put("Action", alibabaConfig.getAction());
|
||||
paras.put("Version", alibabaConfig.getVersion());
|
||||
paras.put("RegionId", alibabaConfig.getRegionId());
|
||||
// 2. 业务API参数
|
||||
Map<String, String> paramMap = generateParamMap(alibabaConfig, phone, message, templateId);
|
||||
// 3. 参数KEY排序
|
||||
Map<String, String> sortParas = new TreeMap<>(paras);
|
||||
sortParas.putAll(paramMap);
|
||||
// 4. 构造待签名的字符串
|
||||
Iterator<String> it = sortParas.keySet().iterator();
|
||||
StringBuilder sortQueryStringTmp = new StringBuilder();
|
||||
while (it.hasNext()) {
|
||||
String key = it.next();
|
||||
sortQueryStringTmp.append("&").append(specialUrlEncode(key)).append("=").append(specialUrlEncode(sortParas.get(key)));
|
||||
}
|
||||
|
||||
String stringToSign = "POST" + "&" +
|
||||
specialUrlEncode("/") + "&" +
|
||||
specialUrlEncode(sortQueryStringTmp.substring(1));
|
||||
String signature = sign(alibabaConfig.getAccessKeySecret() + "&", stringToSign);
|
||||
// 5. 生成请求的url参数
|
||||
StringBuilder sortQueryString = new StringBuilder();
|
||||
it = paras.keySet().iterator();
|
||||
while (it.hasNext()) {
|
||||
String key = it.next();
|
||||
sortQueryString.append("&").append(specialUrlEncode(key)).append("=").append(specialUrlEncode(paras.get(key)));
|
||||
}
|
||||
// 6.生成合法请求URL
|
||||
return Constant.HTTPS_PREFIX + alibabaConfig.getRequestUrl() + "/?Signature=" + signature + sortQueryString;
|
||||
}
|
||||
|
||||
/**
|
||||
* url编码
|
||||
*/
|
||||
private static String specialUrlEncode(String value) throws Exception {
|
||||
return URLEncoder.encode(value, StandardCharsets.UTF_8.name()).replace("+", "%20")
|
||||
.replace("*", "%2A").replace("%7E", "~");
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成签名
|
||||
*
|
||||
* @param accessSecret accessSecret
|
||||
* @param stringToSign 待生成签名的字符串
|
||||
*/
|
||||
private static String sign(String accessSecret, String stringToSign) throws Exception {
|
||||
Mac mac = Mac.getInstance("HmacSHA1");
|
||||
mac.init(new javax.crypto.spec.SecretKeySpec(accessSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
|
||||
byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
|
||||
return Base64.encode(signData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成请求body参数
|
||||
*
|
||||
* @param alibabaConfig 配置数据
|
||||
* @param phone 手机号
|
||||
* @param message 短信内容
|
||||
* @param templateId 模板id
|
||||
*/
|
||||
public static Map<String, String> generateParamMap(AlibabaConfig alibabaConfig, String phone, String message, String templateId) {
|
||||
Map<String, String> paramMap = new HashMap<>();
|
||||
paramMap.put("PhoneNumbers", phone);
|
||||
paramMap.put("SignName", alibabaConfig.getSignature());
|
||||
paramMap.put("TemplateParam", message);
|
||||
paramMap.put("TemplateCode", templateId);
|
||||
return paramMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成请求参数body字符串
|
||||
*
|
||||
* @param alibabaConfig
|
||||
* @param phone
|
||||
* @param message
|
||||
* @param templateId
|
||||
*/
|
||||
public static String generateParamBody(AlibabaConfig alibabaConfig, String phone, String message, String templateId) throws Exception {
|
||||
Map<String, String> paramMap = generateParamMap(alibabaConfig, phone, message, templateId);
|
||||
StringBuilder sortQueryString = new StringBuilder();
|
||||
for (String key : paramMap.keySet()) {
|
||||
sortQueryString.append("&").append(specialUrlEncode(key)).append("=")
|
||||
.append(specialUrlEncode(paramMap.get(key)));
|
||||
}
|
||||
return sortQueryString.substring(1);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,52 +1,52 @@
|
||||
package org.dromara.sms4j.tencent.config;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TencentConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 短信sdkAppId
|
||||
*/
|
||||
private String sdkAppId;
|
||||
|
||||
/**
|
||||
* 地域信息默认为 ap-guangzhou
|
||||
*/
|
||||
@Builder.Default
|
||||
private String territory = "ap-guangzhou";
|
||||
|
||||
/**
|
||||
* 请求超时时间
|
||||
*/
|
||||
@Builder.Default
|
||||
private Integer connTimeout = 60;
|
||||
/** 请求地址*/
|
||||
@Builder.Default
|
||||
private String requestUrl = "sms.tencentcloudapi.com";
|
||||
/**
|
||||
* 接口名称
|
||||
*/
|
||||
@Builder.Default
|
||||
private String action = "SendSms";
|
||||
|
||||
/**
|
||||
* 接口版本
|
||||
*/
|
||||
@Builder.Default
|
||||
private String version = "2021-01-11";
|
||||
|
||||
/**
|
||||
* 服务名
|
||||
*/
|
||||
@Builder.Default
|
||||
private String service = "sms";
|
||||
}
|
||||
package org.dromara.sms4j.tencent.config;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.dromara.sms4j.comm.config.BaseConfig;
|
||||
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@ToString(callSuper = true)
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class TencentConfig extends BaseConfig {
|
||||
|
||||
/**
|
||||
* 短信sdkAppId
|
||||
*/
|
||||
private String sdkAppId;
|
||||
|
||||
/**
|
||||
* 地域信息默认为 ap-guangzhou
|
||||
*/
|
||||
@Builder.Default
|
||||
private String territory = "ap-guangzhou";
|
||||
|
||||
/**
|
||||
* 请求超时时间
|
||||
*/
|
||||
@Builder.Default
|
||||
private Integer connTimeout = 60;
|
||||
/** 请求地址*/
|
||||
@Builder.Default
|
||||
private String requestUrl = "sms.tencentcloudapi.com";
|
||||
/**
|
||||
* 接口名称
|
||||
*/
|
||||
@Builder.Default
|
||||
private String action = "SendSms";
|
||||
|
||||
/**
|
||||
* 接口版本
|
||||
*/
|
||||
@Builder.Default
|
||||
private String version = "2021-01-11";
|
||||
|
||||
/**
|
||||
* 服务名
|
||||
*/
|
||||
@Builder.Default
|
||||
private String service = "sms";
|
||||
}
|
||||
@ -1,52 +1,52 @@
|
||||
package org.dromara.sms4j.tencent.config;
|
||||
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.tencent.service.TencentSmsImpl;
|
||||
|
||||
/**
|
||||
* TencentSmsConfig
|
||||
* <p> 建造腾讯云短信
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/4/8 16:05
|
||||
**/
|
||||
public class TencentSmsConfig {
|
||||
private TencentSmsConfig() {
|
||||
}
|
||||
|
||||
private static TencentSmsImpl tencentSms;
|
||||
|
||||
private static TencentSmsConfig tencentSmsConfig;
|
||||
|
||||
/**
|
||||
* 建造一个腾讯云的短信实现
|
||||
*/
|
||||
public static TencentSmsImpl createTencentSms(TencentConfig tencentConfig) {
|
||||
if (tencentSmsConfig == null) {
|
||||
tencentSmsConfig = new TencentSmsConfig();
|
||||
}
|
||||
if (tencentSms == null) {
|
||||
tencentSms = new TencentSmsImpl(
|
||||
tencentConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime()
|
||||
);
|
||||
}
|
||||
return tencentSms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新对象
|
||||
*/
|
||||
public static TencentSmsImpl refresh(TencentConfig tencentConfig) {
|
||||
if (tencentSmsConfig == null) {
|
||||
tencentSmsConfig = new TencentSmsConfig();
|
||||
}
|
||||
tencentSms = new TencentSmsImpl(
|
||||
tencentConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime()
|
||||
);
|
||||
return tencentSms;
|
||||
}
|
||||
}
|
||||
package org.dromara.sms4j.tencent.config;
|
||||
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.tencent.service.TencentSmsImpl;
|
||||
|
||||
/**
|
||||
* TencentSmsConfig
|
||||
* <p> 建造腾讯云短信
|
||||
*
|
||||
* @author :Wind
|
||||
* 2023/4/8 16:05
|
||||
**/
|
||||
public class TencentSmsConfig {
|
||||
private TencentSmsConfig() {
|
||||
}
|
||||
|
||||
private static TencentSmsImpl tencentSms;
|
||||
|
||||
private static TencentSmsConfig tencentSmsConfig;
|
||||
|
||||
/**
|
||||
* 建造一个腾讯云的短信实现
|
||||
*/
|
||||
public static TencentSmsImpl createTencentSms(TencentConfig tencentConfig) {
|
||||
if (tencentSmsConfig == null) {
|
||||
tencentSmsConfig = new TencentSmsConfig();
|
||||
}
|
||||
if (tencentSms == null) {
|
||||
tencentSms = new TencentSmsImpl(
|
||||
tencentConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime()
|
||||
);
|
||||
}
|
||||
return tencentSms;
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新对象
|
||||
*/
|
||||
public static TencentSmsImpl refresh(TencentConfig tencentConfig) {
|
||||
if (tencentSmsConfig == null) {
|
||||
tencentSmsConfig = new TencentSmsConfig();
|
||||
}
|
||||
tencentSms = new TencentSmsImpl(
|
||||
tencentConfig,
|
||||
BeanFactory.getExecutor(),
|
||||
BeanFactory.getDelayedTime()
|
||||
);
|
||||
return tencentSms;
|
||||
}
|
||||
}
|
||||
@ -1,205 +1,205 @@
|
||||
package org.dromara.sms4j.tencent.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dtflys.forest.config.ForestConfiguration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.callback.CallBack;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.tencent.config.TencentConfig;
|
||||
import org.dromara.sms4j.tencent.utils.TencentUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Slf4j
|
||||
public class TencentSmsImpl implements SmsBlend {
|
||||
|
||||
private TencentConfig tencentSmsConfig;
|
||||
|
||||
private Executor pool;
|
||||
|
||||
private DelayedTime delayed;
|
||||
|
||||
private final ForestConfiguration http = BeanFactory.getForestConfiguration();
|
||||
|
||||
public TencentSmsImpl(TencentConfig tencentSmsConfig, Executor pool, DelayedTime delayed) {
|
||||
this.tencentSmsConfig = tencentSmsConfig;
|
||||
this.pool = pool;
|
||||
this.delayed = delayed;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
String[] split = message.split("&");
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
map.put(String.valueOf(i), split[i]);
|
||||
}
|
||||
return sendMessage(phone, tencentSmsConfig.getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
list.add(entry.getValue());
|
||||
}
|
||||
String[] s = new String[list.size()];
|
||||
return getSmsResponse(new String[]{"+86" + phone}, list.toArray(s), templateId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
String[] split = message.split("&");
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
map.put(String.valueOf(i), split[i]);
|
||||
}
|
||||
return massTexting(phones, tencentSmsConfig.getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
list.add(entry.getValue());
|
||||
}
|
||||
String[] s = new String[list.size()];
|
||||
return getSmsResponse(arrayToString(phones), list.toArray(s), templateId);
|
||||
}
|
||||
|
||||
private SmsResponse getSmsResponse(String[] phones, String[] messages, String templateId) {
|
||||
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
|
||||
String signature;
|
||||
try {
|
||||
signature = TencentUtils.generateSignature(this.tencentSmsConfig, templateId, messages, phones, timestamp);
|
||||
} catch (Exception e) {
|
||||
log.error("tencent send message error", e);
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
Map<String, Object> headsMap = TencentUtils.generateHeadsMap(signature, timestamp, tencentSmsConfig.getAction(),
|
||||
tencentSmsConfig.getVersion(), tencentSmsConfig.getTerritory(), tencentSmsConfig.getRequestUrl());
|
||||
Map<String, Object> requestBody = TencentUtils.generateRequestBody(phones, tencentSmsConfig.getSdkAppId(),
|
||||
tencentSmsConfig.getSignature(), templateId, messages);
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
String url = Constant.HTTPS_PREFIX + tencentSmsConfig.getRequestUrl();
|
||||
http.post(url)
|
||||
.addHeader(headsMap)
|
||||
.addBody(requestBody)
|
||||
.onSuccess(((data, req, res) -> {
|
||||
JSONObject jsonBody = res.get(JSONObject.class);
|
||||
JSONObject response = jsonBody.getJSONObject("Response");
|
||||
JSONArray sendStatusSet = response.getJSONArray("SendStatusSet");
|
||||
smsResponse.setBizId(sendStatusSet.getJSONObject(0).getString("SerialNo"));
|
||||
smsResponse.setMessage(sendStatusSet.getJSONObject(0).getString("Message"));
|
||||
smsResponse.setCode(sendStatusSet.getJSONObject(0).getString("Code"));
|
||||
}))
|
||||
.onError((ex, req, res) -> {
|
||||
JSONObject jsonBody = res.get(JSONObject.class);
|
||||
JSONObject response = jsonBody.getJSONObject("Response");
|
||||
JSONArray sendStatusSet = response.getJSONArray("SendStatusSet");
|
||||
smsResponse.setErrMessage(sendStatusSet.getJSONObject(0).getString("Message"));
|
||||
smsResponse.setErrorCode(sendStatusSet.getJSONObject(0).getString("Code"));
|
||||
})
|
||||
.execute();
|
||||
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 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);
|
||||
}
|
||||
|
||||
private String[] arrayToString(List<String> list) {
|
||||
String[] strs = new String[list.size()];
|
||||
List<String> toStr = new ArrayList<>();
|
||||
for (String s : list) {
|
||||
toStr.add("+86" + s);
|
||||
}
|
||||
return toStr.toArray(strs);
|
||||
}
|
||||
}
|
||||
package org.dromara.sms4j.tencent.service;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.dtflys.forest.config.ForestConfiguration;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.api.SmsBlend;
|
||||
import org.dromara.sms4j.api.callback.CallBack;
|
||||
import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.constant.Constant;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.factory.BeanFactory;
|
||||
import org.dromara.sms4j.tencent.config.TencentConfig;
|
||||
import org.dromara.sms4j.tencent.utils.TencentUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
@Slf4j
|
||||
public class TencentSmsImpl implements SmsBlend {
|
||||
|
||||
private TencentConfig tencentSmsConfig;
|
||||
|
||||
private Executor pool;
|
||||
|
||||
private DelayedTime delayed;
|
||||
|
||||
private final ForestConfiguration http = BeanFactory.getForestConfiguration();
|
||||
|
||||
public TencentSmsImpl(TencentConfig tencentSmsConfig, Executor pool, DelayedTime delayed) {
|
||||
this.tencentSmsConfig = tencentSmsConfig;
|
||||
this.pool = pool;
|
||||
this.delayed = delayed;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String message) {
|
||||
String[] split = message.split("&");
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
map.put(String.valueOf(i), split[i]);
|
||||
}
|
||||
return sendMessage(phone, tencentSmsConfig.getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
list.add(entry.getValue());
|
||||
}
|
||||
String[] s = new String[list.size()];
|
||||
return getSmsResponse(new String[]{"+86" + phone}, list.toArray(s), templateId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String message) {
|
||||
String[] split = message.split("&");
|
||||
LinkedHashMap<String, String> map = new LinkedHashMap<>();
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
map.put(String.valueOf(i), split[i]);
|
||||
}
|
||||
return massTexting(phones, tencentSmsConfig.getTemplateId(), map);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Restricted
|
||||
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (Map.Entry<String, String> entry : messages.entrySet()) {
|
||||
list.add(entry.getValue());
|
||||
}
|
||||
String[] s = new String[list.size()];
|
||||
return getSmsResponse(arrayToString(phones), list.toArray(s), templateId);
|
||||
}
|
||||
|
||||
private SmsResponse getSmsResponse(String[] phones, String[] messages, String templateId) {
|
||||
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
|
||||
String signature;
|
||||
try {
|
||||
signature = TencentUtils.generateSignature(this.tencentSmsConfig, templateId, messages, phones, timestamp);
|
||||
} catch (Exception e) {
|
||||
log.error("tencent send message error", e);
|
||||
throw new SmsBlendException(e.getMessage());
|
||||
}
|
||||
Map<String, Object> headsMap = TencentUtils.generateHeadsMap(signature, timestamp, tencentSmsConfig.getAction(),
|
||||
tencentSmsConfig.getVersion(), tencentSmsConfig.getTerritory(), tencentSmsConfig.getRequestUrl());
|
||||
Map<String, Object> requestBody = TencentUtils.generateRequestBody(phones, tencentSmsConfig.getSdkAppId(),
|
||||
tencentSmsConfig.getSignature(), templateId, messages);
|
||||
SmsResponse smsResponse = new SmsResponse();
|
||||
String url = Constant.HTTPS_PREFIX + tencentSmsConfig.getRequestUrl();
|
||||
http.post(url)
|
||||
.addHeader(headsMap)
|
||||
.addBody(requestBody)
|
||||
.onSuccess(((data, req, res) -> {
|
||||
JSONObject jsonBody = res.get(JSONObject.class);
|
||||
JSONObject response = jsonBody.getJSONObject("Response");
|
||||
JSONArray sendStatusSet = response.getJSONArray("SendStatusSet");
|
||||
smsResponse.setBizId(sendStatusSet.getJSONObject(0).getString("SerialNo"));
|
||||
smsResponse.setMessage(sendStatusSet.getJSONObject(0).getString("Message"));
|
||||
smsResponse.setCode(sendStatusSet.getJSONObject(0).getString("Code"));
|
||||
}))
|
||||
.onError((ex, req, res) -> {
|
||||
JSONObject jsonBody = res.get(JSONObject.class);
|
||||
JSONObject response = jsonBody.getJSONObject("Response");
|
||||
JSONArray sendStatusSet = response.getJSONArray("SendStatusSet");
|
||||
smsResponse.setErrMessage(sendStatusSet.getJSONObject(0).getString("Message"));
|
||||
smsResponse.setErrorCode(sendStatusSet.getJSONObject(0).getString("Code"));
|
||||
})
|
||||
.execute();
|
||||
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 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);
|
||||
}
|
||||
|
||||
private String[] arrayToString(List<String> list) {
|
||||
String[] strs = new String[list.size()];
|
||||
List<String> toStr = new ArrayList<>();
|
||||
for (String s : list) {
|
||||
toStr.add("+86" + s);
|
||||
}
|
||||
return toStr.toArray(strs);
|
||||
}
|
||||
}
|
||||
@ -1,138 +1,138 @@
|
||||
package org.dromara.sms4j.tencent.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.tencent.config.TencentConfig;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
|
||||
/**
|
||||
* @author Richard
|
||||
* @date 2023-04-18 19:50
|
||||
*/
|
||||
@Slf4j
|
||||
public class TencentUtils {
|
||||
/**
|
||||
* 加密方式
|
||||
*/
|
||||
private static final String ALGORITHM = "TC3-HMAC-SHA256";
|
||||
/**
|
||||
* 请求方式
|
||||
*/
|
||||
private static final String HTTP_REQUEST_METHOD = "POST";
|
||||
|
||||
private static final String CT_JSON = "application/json; charset=utf-8";
|
||||
|
||||
|
||||
private static byte[] hmac256(byte[] key, String msg) throws Exception {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
|
||||
mac.init(secretKeySpec);
|
||||
return mac.doFinal(msg.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private static String sha256Hex(String s) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] d = md.digest(s.getBytes(StandardCharsets.UTF_8));
|
||||
return DatatypeConverter.printHexBinary(d).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成腾讯云发送短信接口签名
|
||||
*
|
||||
* @param templateId 模板id
|
||||
* @param messages 短信内容
|
||||
* @param phones 手机号
|
||||
* @param timestamp 时间戳
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String generateSignature(TencentConfig tencentConfig, String templateId, String[] messages, String[] phones,
|
||||
String timestamp) throws Exception {
|
||||
// ************* 步骤 1:拼接规范请求串 *************
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
String date = sdf.format(new Date(Long.parseLong(timestamp + "000")));
|
||||
String canonicalUri = "/";
|
||||
String canonicalQueryString = "";
|
||||
String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + tencentConfig.getRequestUrl() + "\n";
|
||||
String signedHeaders = "content-type;host";
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
// 实际调用需要更新参数,这里仅作为演示签名验证通过的例子
|
||||
params.put("PhoneNumberSet", phones);
|
||||
params.put("SmsSdkAppId", tencentConfig.getSdkAppId());
|
||||
params.put("SignName", tencentConfig.getSignature());
|
||||
params.put("TemplateId", templateId);
|
||||
params.put("TemplateParamSet", messages);
|
||||
String payload = JSON.toJSONString(params);
|
||||
String hashedRequestPayload = sha256Hex(payload);
|
||||
String canonicalRequest = HTTP_REQUEST_METHOD + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
|
||||
+ canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
|
||||
// ************* 步骤 2:拼接待签名字符串 *************
|
||||
String credentialScope = date + "/" + tencentConfig.getService() + "/" + "tc3_request";
|
||||
String hashedCanonicalRequest = sha256Hex(canonicalRequest);
|
||||
String stringToSign = ALGORITHM + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
|
||||
// ************* 步骤 3:计算签名 *************
|
||||
byte[] secretDate = hmac256(("TC3" + tencentConfig.getAccessKeySecret()).getBytes(StandardCharsets.UTF_8), date);
|
||||
byte[] secretService = hmac256(secretDate, tencentConfig.getService());
|
||||
byte[] secretSigning = hmac256(secretService, "tc3_request");
|
||||
String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();
|
||||
// ************* 步骤 4:拼接 Authorization *************
|
||||
return ALGORITHM + " " + "Credential=" + tencentConfig.getAccessKeyId() + "/" + credentialScope + ", "
|
||||
+ "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成腾讯云短信请求头map
|
||||
*
|
||||
* @param authorization 签名信息
|
||||
* @param timestamp 时间戳
|
||||
* @param action 接口名称
|
||||
* @param version 接口版本
|
||||
* @param territory 服务器地区
|
||||
* @param requestUrl 请求地址
|
||||
*/
|
||||
public static Map<String, Object> generateHeadsMap(String authorization, String timestamp, String action,
|
||||
String version, String territory, String requestUrl) {
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put("Authorization", authorization);
|
||||
headers.put("Content-Type", CT_JSON);
|
||||
headers.put("Host", requestUrl);
|
||||
headers.put("X-TC-Action", action);
|
||||
headers.put("X-TC-Timestamp", timestamp);
|
||||
headers.put("X-TC-Version", version);
|
||||
headers.put("X-TC-Region", territory);
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成腾讯云短信请求body
|
||||
*
|
||||
* @param phones 手机号
|
||||
* @param sdkAppId appid
|
||||
* @param signatureName 短信签名
|
||||
* @param templateId 模板id
|
||||
* @param templateParamSet 模板参数
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> generateRequestBody(String[] phones, String sdkAppId, String signatureName,
|
||||
String templateId, String[] templateParamSet) {
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("PhoneNumberSet", phones);
|
||||
requestBody.put("SmsSdkAppId", sdkAppId);
|
||||
requestBody.put("SignName", signatureName);
|
||||
requestBody.put("TemplateId", templateId);
|
||||
requestBody.put("TemplateParamSet", templateParamSet);
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
package org.dromara.sms4j.tencent.utils;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.sms4j.tencent.config.TencentConfig;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.xml.bind.DatatypeConverter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
|
||||
|
||||
/**
|
||||
* @author Richard
|
||||
* @date 2023-04-18 19:50
|
||||
*/
|
||||
@Slf4j
|
||||
public class TencentUtils {
|
||||
/**
|
||||
* 加密方式
|
||||
*/
|
||||
private static final String ALGORITHM = "TC3-HMAC-SHA256";
|
||||
/**
|
||||
* 请求方式
|
||||
*/
|
||||
private static final String HTTP_REQUEST_METHOD = "POST";
|
||||
|
||||
private static final String CT_JSON = "application/json; charset=utf-8";
|
||||
|
||||
|
||||
private static byte[] hmac256(byte[] key, String msg) throws Exception {
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec secretKeySpec = new SecretKeySpec(key, mac.getAlgorithm());
|
||||
mac.init(secretKeySpec);
|
||||
return mac.doFinal(msg.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private static String sha256Hex(String s) throws Exception {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] d = md.digest(s.getBytes(StandardCharsets.UTF_8));
|
||||
return DatatypeConverter.printHexBinary(d).toLowerCase();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成腾讯云发送短信接口签名
|
||||
*
|
||||
* @param templateId 模板id
|
||||
* @param messages 短信内容
|
||||
* @param phones 手机号
|
||||
* @param timestamp 时间戳
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String generateSignature(TencentConfig tencentConfig, String templateId, String[] messages, String[] phones,
|
||||
String timestamp) throws Exception {
|
||||
// ************* 步骤 1:拼接规范请求串 *************
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
|
||||
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
String date = sdf.format(new Date(Long.parseLong(timestamp + "000")));
|
||||
String canonicalUri = "/";
|
||||
String canonicalQueryString = "";
|
||||
String canonicalHeaders = "content-type:application/json; charset=utf-8\n" + "host:" + tencentConfig.getRequestUrl() + "\n";
|
||||
String signedHeaders = "content-type;host";
|
||||
HashMap<String, Object> params = new HashMap<>();
|
||||
// 实际调用需要更新参数,这里仅作为演示签名验证通过的例子
|
||||
params.put("PhoneNumberSet", phones);
|
||||
params.put("SmsSdkAppId", tencentConfig.getSdkAppId());
|
||||
params.put("SignName", tencentConfig.getSignature());
|
||||
params.put("TemplateId", templateId);
|
||||
params.put("TemplateParamSet", messages);
|
||||
String payload = JSON.toJSONString(params);
|
||||
String hashedRequestPayload = sha256Hex(payload);
|
||||
String canonicalRequest = HTTP_REQUEST_METHOD + "\n" + canonicalUri + "\n" + canonicalQueryString + "\n"
|
||||
+ canonicalHeaders + "\n" + signedHeaders + "\n" + hashedRequestPayload;
|
||||
// ************* 步骤 2:拼接待签名字符串 *************
|
||||
String credentialScope = date + "/" + tencentConfig.getService() + "/" + "tc3_request";
|
||||
String hashedCanonicalRequest = sha256Hex(canonicalRequest);
|
||||
String stringToSign = ALGORITHM + "\n" + timestamp + "\n" + credentialScope + "\n" + hashedCanonicalRequest;
|
||||
// ************* 步骤 3:计算签名 *************
|
||||
byte[] secretDate = hmac256(("TC3" + tencentConfig.getAccessKeySecret()).getBytes(StandardCharsets.UTF_8), date);
|
||||
byte[] secretService = hmac256(secretDate, tencentConfig.getService());
|
||||
byte[] secretSigning = hmac256(secretService, "tc3_request");
|
||||
String signature = DatatypeConverter.printHexBinary(hmac256(secretSigning, stringToSign)).toLowerCase();
|
||||
// ************* 步骤 4:拼接 Authorization *************
|
||||
return ALGORITHM + " " + "Credential=" + tencentConfig.getAccessKeyId() + "/" + credentialScope + ", "
|
||||
+ "SignedHeaders=" + signedHeaders + ", " + "Signature=" + signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成腾讯云短信请求头map
|
||||
*
|
||||
* @param authorization 签名信息
|
||||
* @param timestamp 时间戳
|
||||
* @param action 接口名称
|
||||
* @param version 接口版本
|
||||
* @param territory 服务器地区
|
||||
* @param requestUrl 请求地址
|
||||
*/
|
||||
public static Map<String, Object> generateHeadsMap(String authorization, String timestamp, String action,
|
||||
String version, String territory, String requestUrl) {
|
||||
Map<String, Object> headers = new HashMap<>();
|
||||
headers.put("Authorization", authorization);
|
||||
headers.put("Content-Type", CT_JSON);
|
||||
headers.put("Host", requestUrl);
|
||||
headers.put("X-TC-Action", action);
|
||||
headers.put("X-TC-Timestamp", timestamp);
|
||||
headers.put("X-TC-Version", version);
|
||||
headers.put("X-TC-Region", territory);
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成腾讯云短信请求body
|
||||
*
|
||||
* @param phones 手机号
|
||||
* @param sdkAppId appid
|
||||
* @param signatureName 短信签名
|
||||
* @param templateId 模板id
|
||||
* @param templateParamSet 模板参数
|
||||
* @return
|
||||
*/
|
||||
public static Map<String, Object> generateRequestBody(String[] phones, String sdkAppId, String signatureName,
|
||||
String templateId, String[] templateParamSet) {
|
||||
Map<String, Object> requestBody = new HashMap<>();
|
||||
requestBody.put("PhoneNumberSet", phones);
|
||||
requestBody.put("SmsSdkAppId", sdkAppId);
|
||||
requestBody.put("SignName", signatureName);
|
||||
requestBody.put("TemplateId", templateId);
|
||||
requestBody.put("TemplateParamSet", templateParamSet);
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
}
|
||||
@ -9,7 +9,6 @@ import org.dromara.sms4j.api.entity.SmsResponse;
|
||||
import org.dromara.sms4j.comm.annotation.Restricted;
|
||||
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
|
||||
import org.dromara.sms4j.comm.exception.SmsBlendException;
|
||||
import org.dromara.sms4j.comm.utils.http.HttpJsonTool;
|
||||
import org.dromara.sms4j.unisms.config.UniConfig;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -10,13 +10,10 @@
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-spring-boot-starter</artifactId>
|
||||
<name>sms4j-spring-boot-starter</name>
|
||||
<description>sms4j-spring-boot-starter</description>
|
||||
<version>${revision}</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
<name>sms4j-spring-boot-starter</name>
|
||||
<description>sms4j-spring-boot-starter</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@ -37,9 +34,4 @@
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
|
||||
@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-tencent</artifactId>
|
||||
<name>sms4j-tencent</name>
|
||||
<description>sms4j-tencent</description>
|
||||
<version>${revision}</version>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-comm</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-unisms</artifactId>
|
||||
<name>sms4j-unisms</name>
|
||||
<description>sms4j-unisms</description>
|
||||
<version>${revision}</version>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.apistd.uni</groupId>
|
||||
<artifactId>uni-sdk</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-comm</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1 +0,0 @@
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>sms4j-yunpian</artifactId>
|
||||
<name>sms4j-yunpian</name>
|
||||
<description>sms4j-yunpian</description>
|
||||
<version>${revision}</version>
|
||||
|
||||
<properties>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-comm</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.dromara.sms4j</groupId>
|
||||
<artifactId>sms4j-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -1 +0,0 @@
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user