腾讯云短信&阿里云短信移除sdk,改为api实现

This commit is contained in:
995608517@qq.com 2023-04-23 21:05:18 +08:00
parent e89efb3e24
commit ee46aa2f6f
13 changed files with 911 additions and 673 deletions

View File

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j</artifactId> <artifactId>sms4j</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>sms4j</name> <name>sms4j</name>
<description>sms_aggregation</description> <description>sms_aggregation</description>
@ -55,16 +55,16 @@
</snapshotRepository> </snapshotRepository>
</distributionManagement> </distributionManagement>
<properties> <properties>
<unisms.version>0.0.4</unisms.version>
<okhttp.version>3.14.9</okhttp.version>
<aliyun.version>2.0.23</aliyun.version>
<json.version>2.0.15</json.version> <json.version>2.0.15</json.version>
<forest.version>1.5.30</forest.version>
<spring.boot.version>2.7.10</spring.boot.version>
<hutool.version>5.8.16</hutool.version> <hutool.version>5.8.16</hutool.version>
<okhttp.version>3.14.9</okhttp.version>
<tencent.version>3.1.622</tencent.version> <tencent.version>3.1.622</tencent.version>
<revision>1.0.5</revision> <revision>2.0.1</revision>
<unisms.version>0.0.4</unisms.version>
<spring.boot.version>2.7.10</spring.boot.version>
<forest.version>1.5.30</forest.version>
<jdcloud.version>1.3.3</jdcloud.version> <jdcloud.version>1.3.3</jdcloud.version>
<aliyun.version>2.0.23</aliyun.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
@ -83,67 +83,67 @@
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-spring-boot-starter</artifactId> <artifactId>sms4j-spring-boot-starter</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-autoimmit</artifactId> <artifactId>sms4j-autoimmit</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-core</artifactId> <artifactId>sms4j-core</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-comm</artifactId> <artifactId>sms4j-comm</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-api</artifactId> <artifactId>sms4j-api</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-aliyun</artifactId> <artifactId>sms4j-aliyun</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-tencent</artifactId> <artifactId>sms4j-tencent</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-unisms</artifactId> <artifactId>sms4j-unisms</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-yunpian</artifactId> <artifactId>sms4j-yunpian</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-huawei</artifactId> <artifactId>sms4j-huawei</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-jdcloud</artifactId> <artifactId>sms4j-jdcloud</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-cloopen</artifactId> <artifactId>sms4j-cloopen</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-emay</artifactId> <artifactId>sms4j-emay</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.aliyun</groupId> <groupId>com.aliyun</groupId>
@ -268,10 +268,6 @@
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.1</version>
</plugin>
<plugin> <plugin>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version> <version>3.2.2</version>

10
pom.xml
View File

@ -342,11 +342,11 @@
</configuration> </configuration>
</plugin> </plugin>
<!--Release --> <!--Release -->
<plugin> <!-- <plugin>-->
<groupId>org.apache.maven.plugins</groupId> <!-- <groupId>org.apache.maven.plugins</groupId>-->
<artifactId>maven-release-plugin</artifactId> <!-- <artifactId>maven-release-plugin</artifactId>-->
<version>2.5.1</version> <!-- <version>2.5.1</version>-->
</plugin> <!-- </plugin>-->
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>

View File

@ -18,18 +18,6 @@
</properties> </properties>
<dependencies> <dependencies>
<!-- 阿里云短信依赖-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<exclusions>
<exclusion>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency> <dependency>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-comm</artifactId> <artifactId>sms4j-comm</artifactId>

View File

@ -1,26 +1,44 @@
package org.dromara.sms4j.aliyun.config; package org.dromara.sms4j.aliyun.config;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.comm.config.BaseConfig; import org.dromara.sms4j.comm.config.BaseConfig;
@Data @Data
@SuperBuilder @SuperBuilder
@ToString(callSuper = true) @ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class AlibabaConfig extends BaseConfig { public class AlibabaConfig extends BaseConfig {
/** /**
* 模板变量名称 * 模板变量名称
*/ */
private String templateName; private String templateName;
/** /**
* 请求地址 * 请求地址
*/ */
@Builder.Default @Builder.Default
private String requestUrl = "dysmsapi.aliyuncs.com"; 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";
}

View File

@ -1,78 +1,62 @@
package org.dromara.sms4j.aliyun.config; package org.dromara.sms4j.aliyun.config;
import com.aliyun.dysmsapi20170525.Client; import lombok.extern.slf4j.Slf4j;
import com.aliyun.teaopenapi.models.Config; import org.dromara.sms4j.aliyun.service.AlibabaSmsImpl;
import org.dromara.sms4j.aliyun.service.AlibabaSmsImpl; import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.factory.BeanFactory;
import lombok.extern.slf4j.Slf4j; /**
* AlibabaSmsConfig
* <p> 阿里巴巴对象建造者
/** *
* AlibabaSmsConfig * @author :Wind
* <p> 阿里巴巴对象建造者 * 2023/4/8 14:54
* @author :Wind **/
* 2023/4/8 14:54 @Slf4j
**/ public class AlibabaSmsConfig {
@Slf4j
public class AlibabaSmsConfig { private static AlibabaSmsImpl alibabaSms;
private static AlibabaSmsImpl alibabaSms; private static AlibabaSmsConfig alibabaSmsConfig;
private static AlibabaSmsConfig alibabaSmsConfig; /**
* getAlibabaSms
private Client client(AlibabaConfig alibabaConfig){ * <p> 建造一个短信实现对像
try { *
Config config = new Config() * @author :Wind
// AccessKey ID */
.setAccessKeyId(alibabaConfig.getAccessKeyId()) public static AlibabaSmsImpl createAlibabaSms(AlibabaConfig alibabaConfig) {
// AccessKey Secret if (alibabaSmsConfig == null) {
.setAccessKeySecret(alibabaConfig.getAccessKeySecret()); alibabaSmsConfig = new AlibabaSmsConfig();
// 访问的域名 }
config.endpoint = alibabaConfig.getRequestUrl(); if (alibabaSms == null) {
return new Client(config); alibabaSms = new AlibabaSmsImpl(
}catch (Exception e){ alibabaConfig,
log.error(e.getMessage()); BeanFactory.getExecutor(),
throw new SmsBlendException(e.getMessage()); BeanFactory.getDelayedTime());
} }
} return alibabaSms;
}
/**
* getAlibabaSms /**
* <p> 建造一个短信实现对像 * refresh
* @author :Wind * <p> 刷新对象
*/ *
public static AlibabaSmsImpl createAlibabaSms(AlibabaConfig alibabaConfig) { * @author :Wind
if (alibabaSmsConfig == null){ */
alibabaSmsConfig = new AlibabaSmsConfig(); public static AlibabaSmsImpl refresh(AlibabaConfig alibabaConfig) {
} // 如果配置对象为空则创建一个
if (alibabaSms == null){ if (alibabaSmsConfig == null) {
alibabaSms = new AlibabaSmsImpl(alibabaSmsConfig.client(alibabaConfig), alibabaSmsConfig = new AlibabaSmsConfig();
alibabaConfig, }
BeanFactory.getExecutor(), //重新构造一个实现对象
BeanFactory.getDelayedTime()); alibabaSms = new AlibabaSmsImpl(
} alibabaConfig,
return alibabaSms; BeanFactory.getExecutor(),
} BeanFactory.getDelayedTime());
return alibabaSms;
/** }
* refresh
* <p> 刷新对象 private AlibabaSmsConfig() {
* @author :Wind }
*/ }
public static AlibabaSmsImpl refresh(AlibabaConfig alibabaConfig){
// 如果配置对象为空则创建一个
if (alibabaSmsConfig == null){
alibabaSmsConfig = new AlibabaSmsConfig();
}
//重新构造一个实现对象
alibabaSms= new AlibabaSmsImpl(alibabaSmsConfig.client(alibabaConfig),
alibabaConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime());
return alibabaSms;
}
private AlibabaSmsConfig() {
}
}

View File

@ -1,215 +1,196 @@
package org.dromara.sms4j.aliyun.service; package org.dromara.sms4j.aliyun.service;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSON;
import com.aliyun.dysmsapi20170525.Client; import com.alibaba.fastjson.JSONObject;
import com.aliyun.dysmsapi20170525.models.SendBatchSmsRequest; import com.dtflys.forest.config.ForestConfiguration;
import com.aliyun.dysmsapi20170525.models.SendBatchSmsResponse; import lombok.extern.slf4j.Slf4j;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest; import org.dromara.sms4j.aliyun.config.AlibabaConfig;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse; import org.dromara.sms4j.aliyun.utils.AliyunUtils;
import com.aliyun.tea.TeaException; import org.dromara.sms4j.api.SmsBlend;
import com.aliyun.teautil.models.RuntimeOptions; import org.dromara.sms4j.api.callback.CallBack;
import org.dromara.sms4j.aliyun.config.AlibabaConfig; import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.api.SmsBlend; import org.dromara.sms4j.comm.annotation.Restricted;
import org.dromara.sms4j.api.callback.CallBack; import org.dromara.sms4j.comm.delayedTime.DelayedTime;
import org.dromara.sms4j.api.entity.SmsResponse; import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.annotation.Restricted; import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
import org.dromara.sms4j.comm.exception.SmsBlendException; import java.util.LinkedHashMap;
import org.dromara.sms4j.comm.utils.http.HttpJsonTool; import java.util.List;
import lombok.extern.slf4j.Slf4j; import java.util.TimerTask;
import java.util.concurrent.Executor;
import java.util.LinkedHashMap;
import java.util.List; /**
import java.util.TimerTask; * <p>类名: AlibabaSmsImpl
import java.util.concurrent.Executor; * <p>说明 阿里云短信实现
*
/** * @author :Wind
* <p>类名: AlibabaSmsImpl * 2023/3/26 17:16
* <p>说明 阿里云短信实现 **/
* @author :Wind
* 2023/3/26 17:16 @Slf4j
**/ public class AlibabaSmsImpl implements SmsBlend {
@Slf4j private final AlibabaConfig alibabaSmsConfig;
public class AlibabaSmsImpl implements SmsBlend {
private final Executor pool;
private final Client client;
private final DelayedTime delayed;
private final AlibabaConfig alibabaSmsConfig;
private final ForestConfiguration http = BeanFactory.getForestConfiguration();
private final Executor pool;
/**
private final DelayedTime delayed; * AlibabaSmsImpl
* <p>构造器用于构造短信实现模块
/** *
* AlibabaSmsImpl * @author :Wind
* <p>构造器用于构造短信实现模块 */
* @author :Wind
*/ public AlibabaSmsImpl(AlibabaConfig alibabaSmsConfig, Executor pool, DelayedTime delayedTime) {
this.alibabaSmsConfig = alibabaSmsConfig;
public AlibabaSmsImpl(Client client, AlibabaConfig alibabaSmsConfig,Executor pool,DelayedTime delayedTime){ this.pool = pool;
this.client = client; this.delayed = delayedTime;
this.alibabaSmsConfig = alibabaSmsConfig; }
this.pool = pool;
this.delayed = delayedTime; @Override
} @Restricted
public SmsResponse sendMessage(String phone, String message) {
@Override LinkedHashMap<String, String> map = new LinkedHashMap<>();
@Restricted map.put(alibabaSmsConfig.getTemplateName(), message);
public SmsResponse sendMessage(String phone, String message) { return sendMessage(phone, alibabaSmsConfig.getTemplateId(), map);
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) {
@Override String messageStr = JSON.toJSONString(messages);
@Restricted return getSmsResponse(phone, messageStr, templateId);
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) { }
SendSmsRequest sendSmsRequest = new SendSmsRequest();
String s = JSONObject.toJSONString(messages); @Override
sendSmsRequest.setPhoneNumbers(phone) @Restricted
.setTemplateCode(templateId) public SmsResponse massTexting(List<String> phones, String message) {
.setTemplateParam(s) LinkedHashMap<String, String> map = new LinkedHashMap<>();
.setSignName(alibabaSmsConfig.getSignature()); map.put(alibabaSmsConfig.getTemplateName(), message);
RuntimeOptions runtime = new RuntimeOptions(); return massTexting(phones, alibabaSmsConfig.getTemplateId(), map);
SmsResponse smsResponse = new SmsResponse(); }
try {
SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime); @Override
smsResponse.setBizId(sendSmsResponse.body.getBizId()); @Restricted
smsResponse.setData(sendSmsResponse.body); public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
smsResponse.setCode(String.valueOf(sendSmsResponse.statusCode)); String messageStr = JSON.toJSONString(messages);
if (!"OK".equals(sendSmsResponse.body.code)) { return getSmsResponse(arrayToString(phones), messageStr, templateId);
smsResponse.setErrMessage((sendSmsResponse.body.message)); }
smsResponse.setErrorCode(sendSmsResponse.body.code);
} else { private SmsResponse getSmsResponse(String phone, String message, String templateId) {
smsResponse.setMessage(sendSmsResponse.body.message); SmsResponse smsResponse = new SmsResponse();
} String requestUrl;
} catch (TeaException error) { String paramStr;
log.error(error.getMessage()); try {
throw new SmsBlendException(error.message); requestUrl = AliyunUtils.generateSendSmsRequestUrl(this.alibabaSmsConfig, message, phone, templateId);
} catch (Exception _error) { paramStr = AliyunUtils.generateParamBody(alibabaSmsConfig, phone, message, templateId);
TeaException error = new TeaException(_error.getMessage(), _error); } catch (Exception e) {
log.error(_error.getMessage()); log.error("aliyun send message error", e);
throw new SmsBlendException(error.message); throw new SmsBlendException(e.getMessage());
} }
return smsResponse; log.info("requestUrl {}", requestUrl);
} http.post(requestUrl)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
@Override .addBody(paramStr)
@Restricted .onSuccess(((data, req, res) -> {
public SmsResponse massTexting(List<String> phones, String message) { JSONObject jsonBody = res.get(JSONObject.class);
LinkedHashMap<String, String> map = new LinkedHashMap<>(); log.info(jsonBody.toJSONString());
map.put(alibabaSmsConfig.getTemplateName(), message); }))
return massTexting(phones, alibabaSmsConfig.getTemplateId(), map); .onError((ex, req, res) -> {
} JSONObject jsonBody = res.get(JSONObject.class);
log.info(jsonBody.toJSONString());
@Override })
@Restricted .execute();
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) { return smsResponse;
SendBatchSmsRequest sendBatchSmsRequest = new SendBatchSmsRequest(); }
sendBatchSmsRequest.setPhoneNumberJson(JSONObject.toJSONString(phones))//群发的手机号
.setTemplateCode(templateId)//模板id @Override
.setTemplateParamJson(JSONObject.toJSONString(messages))//消息内容 @Restricted
.setSignNameJson(alibabaSmsConfig.getSignature());//短信签名 public void sendMessageAsync(String phone, String message, CallBack callBack) {
RuntimeOptions runtime = new RuntimeOptions(); pool.execute(() -> {
SmsResponse smsResponse = new SmsResponse(); SmsResponse smsResponse = sendMessage(phone, message);
try { callBack.callBack(smsResponse);
SendBatchSmsResponse sendBatchSmsResponse = client.sendBatchSmsWithOptions(sendBatchSmsRequest, runtime); });
smsResponse.setBizId(sendBatchSmsResponse.body.getBizId()); }
smsResponse.setData(HttpJsonTool.getJSONObject(sendBatchSmsResponse.body));
smsResponse.setCode(String.valueOf(sendBatchSmsResponse.statusCode)); @Override
if (!"OK".equals(sendBatchSmsResponse.body.code)) { @Restricted
smsResponse.setErrMessage((sendBatchSmsResponse.body.message)); public void sendMessageAsync(String phone, String message) {
smsResponse.setErrorCode(sendBatchSmsResponse.body.code); pool.execute(() -> {
} else { sendMessage(phone, message);
smsResponse.setMessage(sendBatchSmsResponse.body.message); });
} }
} catch (TeaException error) {
log.error(error.getMessage()); @Override
throw new SmsBlendException(error.message); @Restricted
} catch (Exception _error) { public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages, CallBack callBack) {
TeaException error = new TeaException(_error.getMessage(), _error); pool.execute(() -> {
log.error(error.getMessage()); SmsResponse smsResponse = sendMessage(phone, templateId, messages);
throw new SmsBlendException(error.message); callBack.callBack(smsResponse);
} });
return smsResponse; }
}
@Override
@Override @Restricted
@Restricted public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
public void sendMessageAsync(String phone, String message, CallBack callBack) { pool.execute(() -> {
pool.execute(() -> { sendMessage(phone, templateId, messages);
SmsResponse smsResponse = sendMessage(phone, message); });
callBack.callBack(smsResponse); }
});
} @Override
@Restricted
@Override public void delayedMessage(String phone, String message, Long delayedTime) {
@Restricted this.delayed.schedule(new TimerTask() {
public void sendMessageAsync(String phone, String message) { @Override
pool.execute(() -> { public void run() {
sendMessage(phone, message); sendMessage(phone, message);
}); }
} }, delayedTime);
}
@Override
@Restricted @Override
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages, CallBack callBack) { @Restricted
pool.execute(()->{ public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
SmsResponse smsResponse = sendMessage(phone,templateId,messages); this.delayed.schedule(new TimerTask() {
callBack.callBack(smsResponse); @Override
}); public void run() {
} sendMessage(phone, templateId, messages);
}
@Override }, delayedTime);
@Restricted }
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
pool.execute(()->{ @Override
sendMessage(phone,templateId,messages); @Restricted
}); public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
} this.delayed.schedule(new TimerTask() {
@Override
@Override public void run() {
@Restricted massTexting(phones, message);
public void delayedMessage(String phone, String message, Long delayedTime) { }
this.delayed.schedule(new TimerTask() { }, delayedTime);
@Override }
public void run() {
sendMessage(phone,message); @Override
} @Restricted
},delayedTime); public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
} this.delayed.schedule(new TimerTask() {
@Override
@Override public void run() {
@Restricted massTexting(phones, templateId, messages);
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) { }
this.delayed.schedule(new TimerTask() { }, delayedTime);
@Override }
public void run() {
sendMessage(phone,templateId,messages); private String arrayToString(List<String> list) {
} StringBuilder sb = new StringBuilder();
},delayedTime); for (String s : list) {
} sb.append(",").append("+86").append(s);
}
@Override return sb.substring(1);
@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);
}
}

View File

@ -0,0 +1,124 @@
package org.dromara.sms4j.aliyun.utils;
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
import org.dromara.sms4j.comm.constant.Constant;
import sun.misc.BASE64Encoder;
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 new BASE64Encoder().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);
}
}

View File

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" <project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j</artifactId> <artifactId>sms4j</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
</parent> </parent>
<groupId>org.dromara.sms4j</groupId> <groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-comm</artifactId> <artifactId>sms4j-comm</artifactId>
<version>1.0.5</version> <version>2.0.1</version>
<name>sms4j-comm</name> <name>sms4j-comm</name>
<description>sms4j-comm</description> <description>sms4j-comm</description>
<licenses> <licenses>

View File

@ -1,6 +1,9 @@
package org.dromara.sms4j.comm.constant; package org.dromara.sms4j.comm.constant;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/** /**
* Constant * Constant
* <p> 短信应用常量 * <p> 短信应用常量
@ -27,6 +30,8 @@ public abstract class Constant {
*/ */
public static final String FROM_URLENCODED = "application/x-www-form-urlencoded"; public static final String FROM_URLENCODED = "application/x-www-form-urlencoded";
public static final String APPLICATION_JSON_UTF8 = "application/json; charset=utf-8";
/** /**
* 华为云规定 java时间格式 * 华为云规定 java时间格式
*/ */
@ -35,6 +40,10 @@ public abstract class Constant {
/** 云片短信国内短信请求地址*/ /** 云片短信国内短信请求地址*/
public static final String YUNPIAN_URL = "https://sms.yunpian.com/v2"; public static final String YUNPIAN_URL = "https://sms.yunpian.com/v2";
/**
* https请求前缀
*/
public static final String HTTPS_PREFIX = "https://";
private Constant() { private Constant() {
} }

View File

@ -1,32 +1,52 @@
package org.dromara.sms4j.tencent.config; package org.dromara.sms4j.tencent.config;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.ToString; import lombok.ToString;
import lombok.experimental.SuperBuilder; import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.comm.config.BaseConfig; import org.dromara.sms4j.comm.config.BaseConfig;
@Data @Data
@SuperBuilder @SuperBuilder
@ToString(callSuper = true) @ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public class TencentConfig extends BaseConfig { public class TencentConfig extends BaseConfig {
/** /**
* 短信sdkAppId * 短信sdkAppId
*/ */
private String sdkAppId; private String sdkAppId;
/** /**
* 地域信息默认为 ap-guangzhou * 地域信息默认为 ap-guangzhou
*/ */
@Builder.Default @Builder.Default
private String territory = "ap-guangzhou"; private String territory = "ap-guangzhou";
/** /**
* 请求超时时间 * 请求超时时间
*/ */
@Builder.Default @Builder.Default
private Integer connTimeout = 60; 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";
}

View File

@ -1,67 +1,52 @@
package org.dromara.sms4j.tencent.config; package org.dromara.sms4j.tencent.config;
import com.tencentcloudapi.common.Credential; import org.dromara.sms4j.comm.factory.BeanFactory;
import com.tencentcloudapi.common.profile.ClientProfile; import org.dromara.sms4j.tencent.service.TencentSmsImpl;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient; /**
import org.dromara.sms4j.comm.factory.BeanFactory; * TencentSmsConfig
import org.dromara.sms4j.tencent.service.TencentSmsImpl; * <p> 建造腾讯云短信
*
/** * @author :Wind
* TencentSmsConfig * 2023/4/8 16:05
* <p> 建造腾讯云短信 **/
* @author :Wind public class TencentSmsConfig {
* 2023/4/8 16:05 private TencentSmsConfig() {
**/ }
public class TencentSmsConfig {
private TencentSmsConfig() { private static TencentSmsImpl tencentSms;
}
private static TencentSmsConfig tencentSmsConfig;
private static TencentSmsImpl tencentSms;
/**
private static TencentSmsConfig tencentSmsConfig; * 建造一个腾讯云的短信实现
*/
public static TencentSmsImpl createTencentSms(TencentConfig tencentConfig) {
if (tencentSmsConfig == null) {
private SmsClient tencentBean( TencentConfig tencentConfig) { tencentSmsConfig = new TencentSmsConfig();
Credential cred = new Credential(tencentConfig.getAccessKeyId(),tencentConfig.getAccessKeySecret()); }
HttpProfile httpProfile = new HttpProfile(); if (tencentSms == null) {
httpProfile.setReqMethod("POST"); tencentSms = new TencentSmsImpl(
httpProfile.setConnTimeout(tencentConfig.getConnTimeout()); tencentConfig,
httpProfile.setEndpoint("sms.tencentcloudapi.com"); BeanFactory.getExecutor(),
ClientProfile clientProfile = new ClientProfile(); BeanFactory.getDelayedTime()
clientProfile.setSignMethod("HmacSHA256"); );
clientProfile.setHttpProfile(httpProfile); }
return new SmsClient(cred, tencentConfig.getTerritory(),clientProfile); return tencentSms;
} }
/** 建造一个腾讯云的短信实现*/ /**
public static TencentSmsImpl createTencentSms(TencentConfig tencentConfig){ * 刷新对象
if (tencentSmsConfig == null){ */
tencentSmsConfig = new TencentSmsConfig(); public static TencentSmsImpl refresh(TencentConfig tencentConfig) {
} if (tencentSmsConfig == null) {
if (tencentSms == null){ tencentSmsConfig = new TencentSmsConfig();
tencentSms = new TencentSmsImpl( }
tencentConfig, tencentSms = new TencentSmsImpl(
tencentSmsConfig.tencentBean(tencentConfig), tencentConfig,
BeanFactory.getExecutor(), BeanFactory.getExecutor(),
BeanFactory.getDelayedTime() BeanFactory.getDelayedTime()
); );
} return tencentSms;
return tencentSms; }
} }
/** 刷新对象*/
public static TencentSmsImpl refresh(TencentConfig tencentConfig){
if (tencentSmsConfig == null){
tencentSmsConfig = new TencentSmsConfig();
}
tencentSms = new TencentSmsImpl(
tencentConfig,
tencentSmsConfig.tencentBean(tencentConfig),
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime()
);
return tencentSms;
}
}

View File

@ -1,210 +1,205 @@
package org.dromara.sms4j.tencent.service; package org.dromara.sms4j.tencent.service;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.tencentcloudapi.common.exception.TencentCloudSDKException; import com.dtflys.forest.config.ForestConfiguration;
import com.tencentcloudapi.sms.v20210111.SmsClient; import lombok.extern.slf4j.Slf4j;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest; import org.dromara.sms4j.api.SmsBlend;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse; import org.dromara.sms4j.api.callback.CallBack;
import org.dromara.sms4j.api.SmsBlend; import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.api.callback.CallBack; import org.dromara.sms4j.comm.annotation.Restricted;
import org.dromara.sms4j.api.entity.SmsResponse; import org.dromara.sms4j.comm.constant.Constant;
import org.dromara.sms4j.comm.annotation.Restricted; import org.dromara.sms4j.comm.delayedTime.DelayedTime;
import org.dromara.sms4j.comm.delayedTime.DelayedTime; import org.dromara.sms4j.comm.exception.SmsBlendException;
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.config.TencentConfig;
import lombok.extern.slf4j.Slf4j; import org.dromara.sms4j.tencent.utils.TencentUtils;
import java.util.*; import java.util.*;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@Slf4j
public class TencentSmsImpl implements SmsBlend { @Slf4j
public class TencentSmsImpl implements SmsBlend {
private TencentConfig tencentSmsConfig;
private TencentConfig tencentSmsConfig;
private SmsClient client;
private Executor pool;
private Executor pool;
private DelayedTime delayed;
private DelayedTime delayed;
private final ForestConfiguration http = BeanFactory.getForestConfiguration();
public TencentSmsImpl(TencentConfig tencentSmsConfig, SmsClient client, Executor pool, DelayedTime delayed) {
this.tencentSmsConfig = tencentSmsConfig; public TencentSmsImpl(TencentConfig tencentSmsConfig, Executor pool, DelayedTime delayed) {
this.client = client; this.tencentSmsConfig = tencentSmsConfig;
this.pool = pool; this.pool = pool;
this.delayed = delayed; this.delayed = delayed;
} }
@Override @Override
@Restricted @Restricted
public SmsResponse sendMessage(String phone, String message) { public SmsResponse sendMessage(String phone, String message) {
String[] split = message.split("&"); String[] split = message.split("&");
LinkedHashMap<String, String> map = new LinkedHashMap<>(); LinkedHashMap<String, String> map = new LinkedHashMap<>();
for (int i = 0; i < split.length; i++) { for (int i = 0; i < split.length; i++) {
map.put(String.valueOf(i),split[i]); map.put(String.valueOf(i), split[i]);
} }
return sendMessage(phone, tencentSmsConfig.getTemplateId(),map); return sendMessage(phone, tencentSmsConfig.getTemplateId(), map);
} }
@Override @Override
@Restricted @Restricted
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) { public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
SmsResponse smsResponse = new SmsResponse(); List<String> list = new ArrayList<>();
try { for (Map.Entry<String, String> entry : messages.entrySet()) {
SendSmsRequest req = new SendSmsRequest(); list.add(entry.getValue());
req.setSignName(tencentSmsConfig.getSignature()); }
req.setTemplateId(templateId); String[] s = new String[list.size()];
req.setSmsSdkAppId(tencentSmsConfig.getSdkAppId()); return getSmsResponse(new String[]{"+86" + phone}, list.toArray(s), templateId);
List<String> list = new ArrayList<>(); }
for (Map.Entry<String, String> entry : messages.entrySet()) {
list.add(entry.getValue()); @Override
} @Restricted
String[] s = new String[list.size()]; public SmsResponse massTexting(List<String> phones, String message) {
req.setTemplateParamSet(list.toArray(s)); String[] split = message.split("&");
req.setPhoneNumberSet(new String[]{"+86" + phone}); LinkedHashMap<String, String> map = new LinkedHashMap<>();
SendSmsResponse res = client.SendSms(req); for (int i = 0; i < split.length; i++) {
String s1 = SendSmsResponse.toJsonString(res); map.put(String.valueOf(i), split[i]);
JSONObject jsonObject = JSON.parseObject(s1); }
if (!"Ok".equals(jsonObject.getString("Code"))) { return massTexting(phones, tencentSmsConfig.getTemplateId(), map);
smsResponse.setErrMessage(jsonObject.getString("Message")); }
log.debug(smsResponse.getErrMessage());
} @Override
smsResponse.setMessage(jsonObject.getString("Message")); @Restricted
smsResponse.setBizId(res.getRequestId()); public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
smsResponse.setData(jsonObject); List<String> list = new ArrayList<>();
} catch (TencentCloudSDKException e) { for (Map.Entry<String, String> entry : messages.entrySet()) {
throw new SmsBlendException(e.getMessage()); list.add(entry.getValue());
} }
return smsResponse; String[] s = new String[list.size()];
} return getSmsResponse(arrayToString(phones), list.toArray(s), templateId);
}
@Override
@Restricted private SmsResponse getSmsResponse(String[] phones, String[] messages, String templateId) {
public SmsResponse massTexting(List<String> phones, String message) { String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String[] split = message.split("&"); String signature;
LinkedHashMap<String, String> map = new LinkedHashMap<>(); try {
for (int i = 0; i < split.length; i++) { signature = TencentUtils.generateSignature(this.tencentSmsConfig, templateId, messages, phones, timestamp);
map.put(String.valueOf(i),split[i]); } catch (Exception e) {
} log.error("tencent send message error", e);
return massTexting(phones,tencentSmsConfig.getTemplateId(),map); throw new SmsBlendException(e.getMessage());
} }
Map<String, Object> headsMap = TencentUtils.generateHeadsMap(signature, timestamp, tencentSmsConfig.getAction(),
@Override tencentSmsConfig.getVersion(), tencentSmsConfig.getTerritory(), tencentSmsConfig.getRequestUrl());
@Restricted Map<String, Object> requestBody = TencentUtils.generateRequestBody(phones, tencentSmsConfig.getSdkAppId(),
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) { tencentSmsConfig.getSignature(), templateId, messages);
SmsResponse smsResponse = new SmsResponse(); SmsResponse smsResponse = new SmsResponse();
try { String url = Constant.HTTPS_PREFIX + tencentSmsConfig.getRequestUrl();
SendSmsRequest req = new SendSmsRequest(); http.post(url)
req.setSignName(tencentSmsConfig.getSignature()); .addHeader(headsMap)
req.setTemplateId(templateId); .addBody(requestBody)
req.setSmsSdkAppId(tencentSmsConfig.getSdkAppId()); .onSuccess(((data, req, res) -> {
List<String> list = new ArrayList<>(); JSONObject jsonBody = res.get(JSONObject.class);
for (Map.Entry<String, String> entry : messages.entrySet()) { JSONObject response = jsonBody.getJSONObject("Response");
list.add(entry.getValue()); JSONArray sendStatusSet = response.getJSONArray("SendStatusSet");
} smsResponse.setBizId(sendStatusSet.getJSONObject(0).getString("SerialNo"));
String[] s = new String[list.size()]; smsResponse.setMessage(sendStatusSet.getJSONObject(0).getString("Message"));
req.setTemplateParamSet(list.toArray(s)); smsResponse.setCode(sendStatusSet.getJSONObject(0).getString("Code"));
req.setPhoneNumberSet(arrayToString(phones)); }))
SendSmsResponse res = client.SendSms(req); .onError((ex, req, res) -> {
String s1 = SendSmsResponse.toJsonString(res); JSONObject jsonBody = res.get(JSONObject.class);
JSONObject jsonObject = JSON.parseObject(s1); JSONObject response = jsonBody.getJSONObject("Response");
if (!"Ok".equals(jsonObject.getString("Code"))) { JSONArray sendStatusSet = response.getJSONArray("SendStatusSet");
smsResponse.setErrMessage(jsonObject.getString("Message")); smsResponse.setErrMessage(sendStatusSet.getJSONObject(0).getString("Message"));
log.debug(jsonObject.getString("Message")); smsResponse.setErrorCode(sendStatusSet.getJSONObject(0).getString("Code"));
} })
smsResponse.setMessage(jsonObject.getString("Message")); .execute();
smsResponse.setBizId(res.getRequestId()); return smsResponse;
smsResponse.setData(jsonObject); }
} catch (TencentCloudSDKException e) {
throw new SmsBlendException(e.getMessage()); @Override
} @Restricted
return smsResponse; public void sendMessageAsync(String phone, String message, CallBack callBack) {
} pool.execute(() -> {
SmsResponse smsResponse = sendMessage(phone, message);
@Override callBack.callBack(smsResponse);
@Restricted });
public void sendMessageAsync(String phone, String message, CallBack callBack) { }
pool.execute(() -> {
SmsResponse smsResponse = sendMessage(phone, message); @Override
callBack.callBack(smsResponse); @Restricted
}); public void sendMessageAsync(String phone, String message) {
} pool.execute(() -> {
sendMessage(phone, message);
@Override });
@Restricted }
public void sendMessageAsync(String phone, String message) {
pool.execute(() -> { @Override
sendMessage(phone, message); @Restricted
}); public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages, CallBack callBack) {
} pool.execute(() -> {
SmsResponse smsResponse = sendMessage(phone, templateId, messages);
@Override callBack.callBack(smsResponse);
@Restricted });
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages, CallBack callBack) { }
pool.execute(()->{
SmsResponse smsResponse = sendMessage(phone,templateId,messages); @Override
callBack.callBack(smsResponse); @Restricted
}); public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
} pool.execute(() -> {
sendMessage(phone, templateId, messages);
@Override });
@Restricted }
public void sendMessageAsync(String phone, String templateId, LinkedHashMap<String, String> messages) {
pool.execute(()->{ @Override
sendMessage(phone,templateId,messages); @Restricted
}); public void delayedMessage(String phone, String message, Long delayedTime) {
} this.delayed.schedule(new TimerTask() {
@Override
@Override public void run() {
@Restricted sendMessage(phone, message);
public void delayedMessage(String phone, String message, Long delayedTime) { }
this.delayed.schedule(new TimerTask() { }, delayedTime);
@Override }
public void run() {
sendMessage(phone,message); @Override
} @Restricted
},delayedTime); public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
} this.delayed.schedule(new TimerTask() {
@Override
@Override public void run() {
@Restricted sendMessage(phone, templateId, messages);
public void delayedMessage(String phone, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) { }
this.delayed.schedule(new TimerTask() { }, delayedTime);
@Override }
public void run() {
sendMessage(phone,templateId,messages); @Override
} @Restricted
},delayedTime); public void delayMassTexting(List<String> phones, String message, Long delayedTime) {
} this.delayed.schedule(new TimerTask() {
@Override
@Override public void run() {
@Restricted massTexting(phones, message);
public void delayMassTexting(List<String> phones, String message, Long delayedTime) { }
this.delayed.schedule(new TimerTask() { }, delayedTime);
@Override }
public void run() {
massTexting(phones,message); @Override
} @Restricted
},delayedTime); public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) {
} this.delayed.schedule(new TimerTask() {
@Override
@Override public void run() {
@Restricted massTexting(phones, templateId, messages);
public void delayMassTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages, Long delayedTime) { }
this.delayed.schedule(new TimerTask() { }, delayedTime);
@Override }
public void run() {
massTexting(phones,templateId,messages); private String[] arrayToString(List<String> list) {
} String[] strs = new String[list.size()];
},delayedTime); List<String> toStr = new ArrayList<>();
} for (String s : list) {
toStr.add("+86" + s);
private String[] arrayToString(List<String> list){ }
String[] strs = new String[list.size()]; return toStr.toArray(strs);
List<String> toStr = new ArrayList<>(); }
for (String s : list) { }
toStr.add("+86"+s);
}
return toStr.toArray(strs);
}
}

View File

@ -0,0 +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;
}
}