Merge branch 'dev-2.x'

# Conflicts:
#	.flattened-pom.xml
#	pom.xml
#	sms4j-comm/.flattened-pom.xml
This commit is contained in:
wind 2023-04-24 20:59:35 +08:00
commit bd739e184c
55 changed files with 1338 additions and 1026 deletions

View File

@ -1,13 +1,13 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
<packaging>pom</packaging>
<name>sms4j</name>
<description>简约而强大的短信集成</description>
<description>sms_aggregation</description>
<url>https://gitee.com/dromara/sms_aggregation</url>
<licenses>
<license>
@ -47,25 +47,24 @@
<distributionManagement>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
</distributionManagement>
<properties>
<unisms.version>0.0.4</unisms.version>
<okhttp.version>3.14.9</okhttp.version>
<cloopen.version>1.0.4</cloopen.version>
<aliyun.version>2.0.23</aliyun.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>
<okhttp.version>3.14.9</okhttp.version>
<tencent.version>3.1.622</tencent.version>
<revision>2.0.0</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>
<aliyun.version>2.0.23</aliyun.version>
</properties>
<dependencyManagement>
<dependencies>
@ -84,67 +83,67 @@
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-spring-boot-starter</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-autoimmit</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-core</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-comm</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-api</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-aliyun</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-tencent</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-unisms</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-yunpian</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-huawei</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-jdcloud</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-cloopen</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-emay</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
@ -205,11 +204,6 @@
<artifactId>sms</artifactId>
<version>${jdcloud.version}</version>
</dependency>
<dependency>
<groupId>com.cloopen</groupId>
<artifactId>java-sms-sdk</artifactId>
<version>${cloopen.version}</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
@ -263,18 +257,6 @@
<additionalparam>-Xdoclint:none</additionalparam>
</configuration>
</plugin>
<plugin>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.6</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.0</version>
@ -286,10 +268,6 @@
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>

3
.gitignore vendored
View File

@ -30,6 +30,7 @@ target/
build/
!**/src/main/**/build/
!**/src/test/**/build/
.flattened-pom.xml
**/.flattened-pom.xml
### VS Code ###
.vscode/

Binary file not shown.

View File

@ -1,18 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

View File

@ -1,9 +1,9 @@
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sms4j v2.0.0</h1>
<h1 align="center" style="margin: 30px 0 30px; font-weight: bold;">sms4j v2.0.1</h1>
<h4 align="center" style="margin: 30px 0 30px; font-weight: bold;">sms4j -- 让发送短信变的更简单</h4>
<p align="center">
<a href="https://gitee.com/the-wind-is-like-a-song/sms_aggregation/stargazers"><img src="https://gitee.com/the-wind-is-like-a-song/sms_aggregation/badge/star.svg?theme=gvp"></a>
<a href="https://gitee.com/the-wind-is-like-a-song/sms_aggregation/master/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-green"></a>
<a href="https://gitee.com/the-wind-is-like-a-song/sms_aggregation"><img src="https://img.shields.io/badge/version-v2.0.0-blue"></a>
<a href="https://gitee.com/the-wind-is-like-a-song/sms_aggregation"><img src="https://img.shields.io/badge/version-v2.0.1-blue"></a>
</p>
<img src="/public/logo.png">

16
pom.xml
View File

@ -8,7 +8,7 @@
<name>sms4j</name>
<packaging>pom</packaging>
<description>简约而强大的短信集成</description>
<description>sms_aggregation</description>
<url>https://gitee.com/dromara/sms_aggregation</url>
<modules>
@ -55,12 +55,11 @@
<properties>
<spring.boot.version>2.7.10</spring.boot.version>
<revision>2.0.0</revision>
<revision>2.0.1</revision>
<aliyun.version>2.0.23</aliyun.version>
<tencent.version>3.1.622</tencent.version>
<unisms.version>0.0.4</unisms.version>
<jdcloud.version>1.3.3</jdcloud.version>
<cloopen.version>1.0.4</cloopen.version>
<json.version>2.0.15</json.version>
<okhttp.version>3.14.9</okhttp.version>
<forest.version>1.5.30</forest.version>
@ -240,13 +239,6 @@
<version>${jdcloud.version}</version>
</dependency>
<!-- 容联云短信依赖 -->
<dependency>
<groupId>com.cloopen</groupId>
<artifactId>java-sms-sdk</artifactId>
<version>${cloopen.version}</version>
</dependency>
<!-- okhttp依赖,防止出现java.lang.NoSuchFieldError: Companion异常-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
@ -274,11 +266,11 @@
<distributionManagement>
<snapshotRepository>
<id>ossrh</id>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
<url>https://s01.oss.sonatype.org/content/repositories/snapshots</url>
</snapshotRepository>
<repository>
<id>ossrh</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
<url>https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/</url>
</repository>
</distributionManagement>

View File

@ -31,10 +31,6 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-comm</artifactId>

Binary file not shown.

View File

@ -1,18 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

View File

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

View File

@ -1,23 +1,44 @@
package org.dromara.sms4j.aliyun.config;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class AlibabaConfig {
/** accessKey*/
private String accessKeyId;
/** 访问键秘钥 */
private String accessKeySecret;
/** 短信签名*/
private String signature;
/** 模板Id*/
private String templateId;
/** 模板变量名称*/
private String templateName;
/** 请求地址*/
private String requestUrl = "dysmsapi.aliyuncs.com";
}
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";
}

View File

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

View File

@ -1,215 +1,196 @@
package org.dromara.sms4j.aliyun.service;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendBatchSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendBatchSmsResponse;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.tea.TeaException;
import com.aliyun.teautil.models.RuntimeOptions;
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
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.utils.http.HttpJsonTool;
import lombok.extern.slf4j.Slf4j;
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 Client client;
private final AlibabaConfig alibabaSmsConfig;
private final Executor pool;
private final DelayedTime delayed;
/**
* AlibabaSmsImpl
* <p>构造器用于构造短信实现模块
* @author :Wind
*/
public AlibabaSmsImpl(Client client, AlibabaConfig alibabaSmsConfig,Executor pool,DelayedTime delayedTime){
this.client = client;
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) {
SendSmsRequest sendSmsRequest = new SendSmsRequest();
String s = JSONObject.toJSONString(messages);
sendSmsRequest.setPhoneNumbers(phone)
.setTemplateCode(templateId)
.setTemplateParam(s)
.setSignName(alibabaSmsConfig.getSignature());
RuntimeOptions runtime = new RuntimeOptions();
SmsResponse smsResponse = new SmsResponse();
try {
SendSmsResponse sendSmsResponse = client.sendSmsWithOptions(sendSmsRequest, runtime);
smsResponse.setBizId(sendSmsResponse.body.getBizId());
smsResponse.setData(sendSmsResponse.body);
smsResponse.setCode(String.valueOf(sendSmsResponse.statusCode));
if (!"OK".equals(sendSmsResponse.body.code)) {
smsResponse.setErrMessage((sendSmsResponse.body.message));
smsResponse.setErrorCode(sendSmsResponse.body.code);
} else {
smsResponse.setMessage(sendSmsResponse.body.message);
}
} catch (TeaException error) {
log.error(error.getMessage());
throw new SmsBlendException(error.message);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
log.error(_error.getMessage());
throw new SmsBlendException(error.message);
}
return smsResponse;
}
@Override
@Restricted
public SmsResponse massTexting(List<String> phones, String message) {
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put(alibabaSmsConfig.getTemplateName(), message);
return massTexting(phones, alibabaSmsConfig.getTemplateId(), map);
}
@Override
@Restricted
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
SendBatchSmsRequest sendBatchSmsRequest = new SendBatchSmsRequest();
sendBatchSmsRequest.setPhoneNumberJson(JSONObject.toJSONString(phones))//群发的手机号
.setTemplateCode(templateId)//模板id
.setTemplateParamJson(JSONObject.toJSONString(messages))//消息内容
.setSignNameJson(alibabaSmsConfig.getSignature());//短信签名
RuntimeOptions runtime = new RuntimeOptions();
SmsResponse smsResponse = new SmsResponse();
try {
SendBatchSmsResponse sendBatchSmsResponse = client.sendBatchSmsWithOptions(sendBatchSmsRequest, runtime);
smsResponse.setBizId(sendBatchSmsResponse.body.getBizId());
smsResponse.setData(HttpJsonTool.getJSONObject(sendBatchSmsResponse.body));
smsResponse.setCode(String.valueOf(sendBatchSmsResponse.statusCode));
if (!"OK".equals(sendBatchSmsResponse.body.code)) {
smsResponse.setErrMessage((sendBatchSmsResponse.body.message));
smsResponse.setErrorCode(sendBatchSmsResponse.body.code);
} else {
smsResponse.setMessage(sendBatchSmsResponse.body.message);
}
} catch (TeaException error) {
log.error(error.getMessage());
throw new SmsBlendException(error.message);
} catch (Exception _error) {
TeaException error = new TeaException(_error.getMessage(), _error);
log.error(error.getMessage());
throw new SmsBlendException(error.message);
}
return smsResponse;
}
@Override
@Restricted
public void sendMessageAsync(String phone, String message, CallBack callBack) {
pool.execute(() -> {
SmsResponse smsResponse = sendMessage(phone, message);
callBack.callBack(smsResponse);
});
}
@Override
@Restricted
public void 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);
}
}
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);
}
}

View File

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

Binary file not shown.

View File

@ -1,18 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

Binary file not shown.

View File

@ -1,18 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

View File

@ -56,6 +56,7 @@ public class SmsAutowiredConfig {
return new ConfigUtil(environment);
}
/** smsConfig参数意义为确保注入时smsConfig已经存在*/
@Bean
@ConditionalOnProperty(prefix = "sms", name = "config-type", havingValue = "config_file")
protected SupplierConfig supplierConfig(SmsConfig smsConfig){
@ -64,8 +65,8 @@ public class SmsAutowiredConfig {
@Bean
@ConditionalOnProperty(prefix = "sms", name = "config-type", havingValue = "sql_config")
protected void supplierSqlConfig(){
new SupplierSqlConfig();
protected void supplierSqlConfig(SmsSqlConfig smsSqlConfig){
SupplierSqlConfig.refreshSqlConfig();
}
@ -80,8 +81,9 @@ public class SmsAutowiredConfig {
springUtil.createBean(AopAdvice.class);
log.debug("SMS restriction is enabled");
}
//打印banner
if (BeanFactory.getSmsConfig().getIsPrint()){
SmsBanner.PrintBanner("V 1.0.5");
SmsBanner.PrintBanner("V 2.0.1");
}
}
}

View File

@ -72,9 +72,4 @@ public class SupplierConfig {
protected EmayConfig emayConfig(){
return SupplierFactory.getEmayConfig();
}
/** 为的是延后执行*/
protected void init(){
}
}

View File

@ -21,10 +21,6 @@
</license>
</licenses>
<dependencies>
<dependency>
<groupId>com.cloopen</groupId>
<artifactId>java-sms-sdk</artifactId>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-comm</artifactId>

Binary file not shown.

View File

@ -1,18 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

View File

@ -16,12 +16,6 @@
<version>${revision}</version>
<dependencies>
<!-- 容联云短信依赖 -->
<dependency>
<groupId>com.cloopen</groupId>
<artifactId>java-sms-sdk</artifactId>
</dependency>
<dependency>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-comm</artifactId>

View File

@ -0,0 +1,32 @@
package org.dromara.sms4j.cloopen.api;
import com.dtflys.forest.annotation.Address;
import com.dtflys.forest.annotation.BaseRequest;
import com.dtflys.forest.annotation.Body;
import com.dtflys.forest.annotation.PostRequest;
import java.util.Map;
/**
* 容联云 REST API
*
* @author Charles7c
* @since 2023/4/17 20:57
*/
@Address(basePath = "{baseUrl}")
@BaseRequest(headers = {
"Accept: application/json",
"Content-Type: application/json;charset=utf-8",
"Authorization: {authorization}",
})
public interface CloopenRestApi {
/**
* 发送模板短信
*
* @param paramMap 请求参数
* @return 响应结果
*/
@PostRequest("/Accounts/{accessKeyId}/SMS/TemplateSMS?sig={sign}")
Map<String, Object> sendSms(@Body Map<String, Object> paramMap);
}

View File

@ -1,7 +1,11 @@
package org.dromara.sms4j.cloopen.config;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.comm.config.BaseConfig;
/**
* 容联云短信配置属性
@ -10,24 +14,37 @@ import lombok.experimental.Accessors;
* @since 2023/4/10 22:10
*/
@Data
@Accessors(chain = true)
public class CloopenConfig {
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class CloopenConfig extends BaseConfig {
/** Access Key */
private String accessKeyId;
/** Access Key Secret */
private String accessKeySecret;
/** 模板 ID */
private String templateId;
/** 应用 ID */
/**
* 应用 ID
*/
private String appId;
/** Rest URL 域名 */
/**
* REST API Base URL
*/
@Builder.Default
private String baseUrl = "https://app.cloopen.com:8883/2013-12-26";
/**
* Rest URL 域名
*
* @deprecated v2.0.1
* @see baseUrl
*/
@Deprecated
private String serverIp;
/** Rest URL 端口 */
/**
* Rest URL 端口
*
* @deprecated v2.0.1
* @see baseUrl
*/
@Deprecated
private String serverPort;
}

View File

@ -1,7 +1,7 @@
package org.dromara.sms4j.cloopen.config;
import com.cloopen.rest.sdk.BodyType;
import com.cloopen.rest.sdk.CCPRestSmsSDK;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.sms4j.cloopen.service.CloopenSmsImpl;
import org.dromara.sms4j.comm.factory.BeanFactory;
@ -11,27 +11,13 @@ import org.dromara.sms4j.comm.factory.BeanFactory;
* @author Charles7c
* @since 2023/4/10 22:10
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class CloopenSmsConfig {
private static CloopenSmsImpl cloopenSms;
private static CloopenSmsConfig cloopenSmsConfig;
/**
* 客户端对象
*
* @param cloopenConfig 容联云短信配置属性
* @return 客户端对象
*/
public CCPRestSmsSDK client(CloopenConfig cloopenConfig) {
CCPRestSmsSDK sdk = new CCPRestSmsSDK();
sdk.init(cloopenConfig.getServerIp(), cloopenConfig.getServerPort());
sdk.setAccount(cloopenConfig.getAccessKeyId(), cloopenConfig.getAccessKeySecret());
sdk.setAppId(cloopenConfig.getAppId());
sdk.setBodyType(BodyType.Type_JSON);
return sdk;
}
/**
* 创建容联云短信实现
*/
@ -40,12 +26,7 @@ public class CloopenSmsConfig {
cloopenSmsConfig = new CloopenSmsConfig();
}
if (cloopenSms == null) {
cloopenSms = new CloopenSmsImpl(
cloopenSmsConfig.client(cloopenConfig),
cloopenConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime()
);
cloopenSms = new CloopenSmsImpl(cloopenConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
}
return cloopenSms;
}
@ -57,12 +38,7 @@ public class CloopenSmsConfig {
if (cloopenSmsConfig == null) {
cloopenSmsConfig = new CloopenSmsConfig();
}
cloopenSms = new CloopenSmsImpl(
cloopenSmsConfig.client(cloopenConfig),
cloopenConfig,
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime()
);
cloopenSms = new CloopenSmsImpl(cloopenConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
return cloopenSms;
}
}

View File

@ -1,17 +1,17 @@
package org.dromara.sms4j.cloopen.service;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.json.JSONUtil;
import com.cloopen.rest.sdk.CCPRestSmsSDK;
import com.dtflys.forest.Forest;
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.cloopen.api.CloopenRestApi;
import org.dromara.sms4j.cloopen.config.CloopenConfig;
import org.dromara.sms4j.cloopen.util.CloopenHelper;
import org.dromara.sms4j.comm.annotation.Restricted;
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import java.util.*;
import java.util.concurrent.Executor;
@ -25,7 +25,7 @@ import java.util.concurrent.Executor;
@Slf4j
public class CloopenSmsImpl implements SmsBlend {
private final CCPRestSmsSDK client;
private final CloopenRestApi restApi;
private final CloopenConfig config;
@ -33,11 +33,11 @@ public class CloopenSmsImpl implements SmsBlend {
private final DelayedTime delayed;
public CloopenSmsImpl(CCPRestSmsSDK client, CloopenConfig config, Executor pool, DelayedTime delayed) {
this.client = client;
public CloopenSmsImpl(CloopenConfig config, Executor pool, DelayedTime delayed) {
this.config = config;
this.pool = pool;
this.delayed = delayed;
restApi = Forest.client(CloopenRestApi.class);
}
@Override
@ -63,34 +63,13 @@ public class CloopenSmsImpl implements SmsBlend {
@Override
@Restricted
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
SmsResponse smsResponse = new SmsResponse();
Map<String, Object> result = null;
try {
String[] datas = messages.keySet().stream().map(messages::get).toArray(String[]::new);
result = client.sendTemplateSMS(String.join(",", phones), templateId, datas);
String statusCode = Convert.toStr(result.get("statusCode"));
String statusMsg = Convert.toStr(result.get("statusMsg"));
smsResponse.setData(result.get("data"));
smsResponse.setCode(statusCode);
smsResponse.setMessage(statusMsg);
boolean isSuccess = "000000".equals(statusCode);
if (!isSuccess) {
smsResponse.setErrMessage(statusMsg);
smsResponse.setErrorCode(statusCode);
} else {
Object bizId = JSONUtil.getByPath(JSONUtil.parse(result.get("data")), "templateSMS.smsMessageSid");
smsResponse.setBizId(Convert.toStr(bizId));
}
} catch (Exception e) {
if (result != null) {
smsResponse.setErrMessage(Convert.toStr(result.get("statusMsg")));
smsResponse.setErrorCode(Convert.toStr(result.get("statusCode")));
} else {
throw new SmsBlendException(e.getMessage());
}
}
return smsResponse;
CloopenHelper helper = new CloopenHelper(config);
Map<String, Object> paramMap = MapUtil.newHashMap(4);
paramMap.put("to", String.join(",", phones));
paramMap.put("appId", config.getAppId());
paramMap.put("templateId", templateId);
paramMap.put("datas", messages.keySet().stream().map(messages::get).toArray(String[]::new));
return helper.request(restApi::sendSms, paramMap);
}
@Override

View File

@ -0,0 +1,113 @@
package org.dromara.sms4j.cloopen.util;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.dtflys.forest.Forest;
import com.dtflys.forest.config.ForestConfiguration;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.cloopen.config.CloopenConfig;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.utils.RestApiFunction;
import java.util.Collections;
import java.util.Date;
import java.util.Map;
import java.util.Optional;
/**
* 容联云 Helper
*
* @author Charles7c
* @since 2023/4/17 20:57
*/
public class CloopenHelper {
private final CloopenConfig config;
public CloopenHelper(CloopenConfig config) {
this.config = config;
}
/**
* 发起 REST 请求
*
* @param restApiFunction REST API 函数式接口
* @param paramMap 请求参数
* @param <R> 响应类型
* @return 响应信息
*/
public <R> SmsResponse request(RestApiFunction<Map<String, Object>, R> restApiFunction, Map<String, Object> paramMap) {
SmsResponse smsResponse = new SmsResponse();
try {
String timestamp = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
// 设置全局变量
ForestConfiguration forestConfiguration = Forest.config();
forestConfiguration.setVariableValue("baseUrl", (method) -> config.getBaseUrl());
forestConfiguration.setVariableValue("accessKeyId", (method) -> config.getAccessKeyId());
forestConfiguration.setVariableValue("sign", this.generateSign(config.getAccessKeyId(), config.getAccessKeySecret(), timestamp));
forestConfiguration.setVariableValue("authorization", this.generateAuthorization(config.getAccessKeyId(), timestamp));
// 调用请求
R response = restApiFunction.apply(paramMap);
// 解析结果
Map<String, Object> responseMap = Optional.ofNullable(response)
.map(JSONUtil::parseObj)
.map(obj -> (Map<String, Object>) obj)
.orElse(Collections.emptyMap());
String statusCode = Convert.toStr(responseMap.get("statusCode"));
String statusMsg = Convert.toStr(responseMap.get("statusMsg"));
boolean isSuccess = "000000".equals(statusCode);
if (isSuccess) {
smsResponse.setCode(statusCode);
smsResponse.setMessage(statusMsg);
smsResponse.setData(response);
Object bizId = JSONUtil.getByPath(JSONUtil.parse(responseMap), "templateSMS.smsMessageSid");
smsResponse.setBizId(Convert.toStr(bizId));
} else {
smsResponse.setErrMessage(statusMsg);
smsResponse.setErrorCode(statusCode);
}
} catch (Exception e) {
throw new SmsBlendException(e.getMessage());
}
return smsResponse;
}
/**
* 生成签名
* <p>
* 1.使用 MD5 加密账户 Id + 账户授权令牌 + 时间戳其中账户 Id 和账户授权令牌根据 url 的验证级别对应主账户<br>
* 时间戳是当前系统时间格式 "yyyyMMddHHmmss"时间戳有效时间为 24 小时20140416142030 <br>
* 2.参数需要大写
* </p>
*
* @param accessKeyId /
* @param accessKeySecret /
* @param timestamp 时间戳
* @return 签名
*/
private String generateSign(String accessKeyId, String accessKeySecret, String timestamp) {
return SecureUtil.md5(accessKeyId + accessKeySecret + timestamp).toUpperCase();
}
/**
* 生成验证信息
* <p>
* 1.使用 Base64 编码账户 Id + 冒号 + 时间戳其中账户 Id 根据 url 的验证级别对应主账户<br>
* 2.冒号为英文冒号<br>
* 3.时间戳是当前系统时间格式 "yyyyMMddHHmmss"需与签名中时间戳相同
* </p>
*
* @param accessKeyId /
* @param timestamp 时间戳
* @return 验证信息
*/
private String generateAuthorization(String accessKeyId, String timestamp) {
return Base64.encode(accessKeyId + ":" + timestamp);
}
}

View File

@ -1,15 +1,15 @@
<?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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</parent>
<groupId>org.dromara.sms4j</groupId>
<artifactId>sms4j-comm</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
<name>sms4j-comm</name>
<description>sms4j-comm</description>
<licenses>

Binary file not shown.

View File

@ -1,18 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

View File

@ -0,0 +1,35 @@
package org.dromara.sms4j.comm.config;
import lombok.Data;
import lombok.experimental.SuperBuilder;
/**
* 短信配置属性基类
*
* @author Charles7c
* @since 2023/4/20 23:03
*/
@Data
@SuperBuilder
public class BaseConfig {
/**
* Access Key
*/
private String accessKeyId;
/**
* Access Key Secret
*/
private String accessKeySecret;
/**
* 短信签名
*/
private String signature;
/**
* 模板 ID
*/
private String templateId;
}

View File

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

View File

@ -46,8 +46,13 @@ public class JDBCTool {
*/
public Connection getConn() {
Connection connection;
String url;
try {
String url = config.getUrl() + "/" + config.getDatabaseName();
if (config.getDatabaseName().isEmpty()){
url = config.getUrl();
}else{
url = config.getUrl() + "/" + config.getDatabaseName();
}
connection = DriverManager.getConnection(url, config.getUsername(), config.getPassword());
} catch (SQLException e) {
throw new SmsSqlException(e.getMessage());

View File

@ -0,0 +1,16 @@
package org.dromara.sms4j.comm.utils;
import java.io.Serializable;
/**
* REST API 函数式接口
*
* @param <P> 请求参数
* @param <R> 响应
* @author Charles7c
* @since 2023/4/17 20:57
*/
@FunctionalInterface
public interface RestApiFunction<P, R> extends Serializable {
R apply(P param);
}

View File

@ -6,6 +6,7 @@ import org.dromara.sms4j.comm.enumerate.SupplierType;
import org.dromara.sms4j.comm.utils.JDBCTool;
import org.dromara.sms4j.comm.utils.SmsUtil;
import org.dromara.sms4j.core.config.SupplierFactory;
import org.dromara.sms4j.emay.config.EmayConfig;
import org.dromara.sms4j.huawei.config.HuaweiConfig;
import org.dromara.sms4j.jdcloud.config.JdCloudConfig;
import org.dromara.sms4j.tencent.config.TencentConfig;
@ -23,16 +24,22 @@ import java.util.Map;
public class SupplierSqlConfig {
private static Map<String, String> select;
static {
/**
* readSqlConfig
* <p>读取数据库配置信息
* @author :Wind
*/
public static void readSqlConfig(){
select = JDBCTool.selectConfig();
}
/**
* SupplierSqlConfig
* <p>在类初始化是完成方法调用
* refreshSqlConfig
* <p>读取并刷新数据库配置
* @author :Wind
*/
public SupplierSqlConfig() {
public static void refreshSqlConfig(){
readSqlConfig();
alibaba();
huawei();
jingdong();
@ -40,6 +47,15 @@ public class SupplierSqlConfig {
uniSms();
yunPian();
cloopen();
emay();
}
public SupplierSqlConfig() {
refreshSqlConfig();
}
public static SupplierSqlConfig newSupplierSqlConfig(){
return new SupplierSqlConfig();
}
/**
@ -49,7 +65,7 @@ public class SupplierSqlConfig {
*/
public static void alibaba(){
AlibabaConfig alibabaConfig = SmsUtil.jsonForObject(select.get(SupplierType.ALIBABA.getName()), AlibabaConfig.class);
SmsUtil.copyBean(alibabaConfig, SupplierFactory.getAlibabaConfig());
SupplierFactory.setAlibabaConfig(alibabaConfig);
}
/**
@ -59,7 +75,7 @@ public class SupplierSqlConfig {
*/
public static void huawei(){
HuaweiConfig huaweiConfig = SmsUtil.jsonForObject(select.get(SupplierType.HUAWEI.getName()), HuaweiConfig.class);
SmsUtil.copyBean(huaweiConfig, SupplierFactory.getHuaweiConfig());
SupplierFactory.setHuaweiConfig(huaweiConfig);
}
/**
@ -69,7 +85,7 @@ public class SupplierSqlConfig {
*/
public static void jingdong(){
JdCloudConfig jdCloudConfig = SmsUtil.jsonForObject(select.get(SupplierType.JD_CLOUD.getName()), JdCloudConfig.class);
SmsUtil.copyBean(jdCloudConfig,SupplierFactory.getJdCloudConfig());
SupplierFactory.setJdCloudConfig(jdCloudConfig);
}
/**
@ -79,7 +95,7 @@ public class SupplierSqlConfig {
*/
public static void tencent(){
TencentConfig tencentConfig = SmsUtil.jsonForObject(select.get(SupplierType.TENCENT.getName()), TencentConfig.class);
SmsUtil.copyBean(tencentConfig, SupplierFactory.getTencentConfig());
SupplierFactory.setTencentConfig(tencentConfig);
}
/**
@ -89,7 +105,7 @@ public class SupplierSqlConfig {
*/
public static void uniSms(){
UniConfig uniConfig = SmsUtil.jsonForObject(select.get(SupplierType.UNI_SMS.getName()), UniConfig.class);
SmsUtil.copyBean(uniConfig,SupplierFactory.getUniConfig());
SupplierFactory.setUniConfig(uniConfig);
}
/**
@ -99,17 +115,25 @@ public class SupplierSqlConfig {
*/
public static void yunPian(){
YunpianConfig yunpianConfig = SmsUtil.jsonForObject(select.get(SupplierType.YUNPIAN.getName()), YunpianConfig.class);
SmsUtil.copyBean(yunpianConfig,SupplierFactory.getYunpianConfig());
SupplierFactory.setYunpianConfig(yunpianConfig);
}
/**
* cloopen
* <p>数据库读取并设置联云短信
* <p>数据库读取并设置联云短信
* @author :Wind
*/
public static void cloopen(){
CloopenConfig cloopenConfig = SmsUtil.jsonForObject(select.get(SupplierType.CLOOPEN.getName()), CloopenConfig.class);
SmsUtil.copyBean(cloopenConfig,SupplierFactory.getCloopenConfig());
SupplierFactory.setCloopenConfig(cloopenConfig);
}
/**
* emay
* <p>数据库读取并设置亿美软通短信
*/
public static void emay() {
EmayConfig emayConfig = SmsUtil.jsonForObject(select.get(SupplierType.EMAY.getName()), EmayConfig.class);
SupplierFactory.setEmayConfig(emayConfig);
}
}

View File

@ -1,5 +1,7 @@
package org.dromara.sms4j.core.config;
import org.dromara.sms4j.comm.enumerate.SupplierType;
import org.dromara.sms4j.core.factory.SmsFactory;
import org.dromara.sms4j.emay.config.EmayConfig;
import org.dromara.sms4j.aliyun.config.AlibabaConfig;
import org.dromara.sms4j.cloopen.config.CloopenConfig;
@ -48,7 +50,7 @@ public class SupplierFactory {
/** 阿里云配置获取*/
public static AlibabaConfig getAlibabaConfig() {
if (alibabaConfig == null){
alibabaConfig = new AlibabaConfig();
alibabaConfig = AlibabaConfig.builder().build();
}
return alibabaConfig;
}
@ -56,7 +58,7 @@ public class SupplierFactory {
/** 华为云配置获取*/
public static HuaweiConfig getHuaweiConfig() {
if (huaweiConfig == null){
huaweiConfig = new HuaweiConfig();
huaweiConfig = HuaweiConfig.builder().build();
}
return huaweiConfig;
}
@ -64,7 +66,7 @@ public class SupplierFactory {
/** 合一短信配置获取*/
public static UniConfig getUniConfig() {
if (uniConfig == null){
uniConfig = new UniConfig();
uniConfig = UniConfig.builder().build();
}
return uniConfig;
}
@ -72,7 +74,7 @@ public class SupplierFactory {
/** 腾讯短信配置获取*/
public static TencentConfig getTencentConfig() {
if (tencentConfig == null){
tencentConfig = new TencentConfig();
tencentConfig = TencentConfig.builder().build();
}
return tencentConfig;
}
@ -80,7 +82,7 @@ public class SupplierFactory {
/** 云片短信配置获取*/
public static YunpianConfig getYunpianConfig() {
if (yunpianConfig == null){
yunpianConfig = new YunpianConfig();
yunpianConfig = YunpianConfig.builder().build();
}
return yunpianConfig;
}
@ -88,7 +90,7 @@ public class SupplierFactory {
/** 京东云短信配置获取 */
public static JdCloudConfig getJdCloudConfig() {
if (jdCloudConfig == null){
jdCloudConfig = new JdCloudConfig();
jdCloudConfig = JdCloudConfig.builder().build();
}
return jdCloudConfig;
}
@ -96,7 +98,7 @@ public class SupplierFactory {
/** 容联云短信配置获取 */
public static CloopenConfig getCloopenConfig() {
if (cloopenConfig == null){
cloopenConfig = new CloopenConfig();
cloopenConfig = CloopenConfig.builder().build();
}
return cloopenConfig;
}
@ -106,8 +108,72 @@ public class SupplierFactory {
*/
public static EmayConfig getEmayConfig() {
if (emayConfig == null) {
emayConfig = new EmayConfig();
emayConfig = EmayConfig.builder().build();
}
return emayConfig;
}
/**
* 设置 alibabaConfig
*/
public static void setAlibabaConfig(AlibabaConfig alibabaConfig) {
SupplierFactory.alibabaConfig = alibabaConfig;
SmsFactory.refresh(SupplierType.ALIBABA);
}
/**
* 设置 huaweiConfig
*/
public static void setHuaweiConfig(HuaweiConfig huaweiConfig) {
SupplierFactory.huaweiConfig = huaweiConfig;
SmsFactory.refresh(SupplierType.HUAWEI);
}
/**
* 设置 uniConfig
*/
public static void setUniConfig(UniConfig uniConfig) {
SupplierFactory.uniConfig = uniConfig;
SmsFactory.refresh(SupplierType.UNI_SMS);
}
/**
* 设置 tencentConfig
*/
public static void setTencentConfig(TencentConfig tencentConfig) {
SupplierFactory.tencentConfig = tencentConfig;
SmsFactory.refresh(SupplierType.TENCENT);
}
/**
* 设置 yunpianConfig
*/
public static void setYunpianConfig(YunpianConfig yunpianConfig) {
SupplierFactory.yunpianConfig = yunpianConfig;
SmsFactory.refresh(SupplierType.YUNPIAN);
}
/**
* 设置 jdCloudConfig
*/
public static void setJdCloudConfig(JdCloudConfig jdCloudConfig) {
SupplierFactory.jdCloudConfig = jdCloudConfig;
SmsFactory.refresh(SupplierType.JD_CLOUD);
}
/**
* 设置 cloopenConfig
*/
public static void setCloopenConfig(CloopenConfig cloopenConfig) {
SupplierFactory.cloopenConfig = cloopenConfig;
SmsFactory.refresh(SupplierType.CLOOPEN);
}
/**
* 设置 emayConfig
*/
public static void setEmayConfig(EmayConfig emayConfig) {
SupplierFactory.emayConfig = emayConfig;
SmsFactory.refresh(SupplierType.EMAY);
}
}

View File

@ -1,5 +1,9 @@
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;
import org.dromara.sms4j.api.SmsBlend;
@ -20,12 +24,10 @@ import org.dromara.sms4j.unisms.config.UniSmsConfig;
* @author :Wind
* 2023/4/8 15:55
**/
public class SmsFactory {
public abstract class SmsFactory {
private SmsFactory() {
}
;
/**
* createSmsBlend
* <p>获取各个厂商的实现类
@ -93,4 +95,14 @@ public class SmsFactory {
}
throw new SmsBlendException("An attempt to construct a SmsBlend object failed. Please check that the enumeration is valid");
}
/**
* refreshSqlConfig
* <p>重新读取sql配置
* @author :Wind
*/
public static void refreshSqlConfig() {
SupplierSqlConfig.refreshSqlConfig();
}
}

Binary file not shown.

View File

@ -1,18 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

View File

@ -1,14 +1,14 @@
package org.dromara.sms4j.emay.config;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* @author Richard
* @date 2023-04-11 12:00
*/
@Data
@Accessors(chain = true)
@Builder
public class EmayConfig {
/** appKey*/
private String appId ;

Binary file not shown.

View File

@ -1,18 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

View File

@ -1,10 +1,10 @@
package org.dromara.sms4j.huawei.config;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@Builder
public class HuaweiConfig {
/** appKey*/

Binary file not shown.

View File

@ -1,18 +0,0 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.7/apache-maven-3.8.7-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar

View File

@ -1,7 +1,11 @@
package org.dromara.sms4j.jdcloud.config;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.comm.config.BaseConfig;
/**
* 京东云短信配置属性
@ -10,21 +14,14 @@ import lombok.experimental.Accessors;
* @since 2023/4/10 20:01
*/
@Data
@Accessors(chain = true)
public class JdCloudConfig {
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class JdCloudConfig extends BaseConfig {
/** Access Key */
private String accessKeyId;
/** Access Key Secret */
private String accessKeySecret;
/** 短信签名 */
private String signature;
/** 模板 ID */
private String templateId;
/** 地域信息 */
/**
* 地域信息
*/
@Builder.Default
private String region = "cn-north-1";
}

View File

@ -1,29 +1,52 @@
package org.dromara.sms4j.tencent.config;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class TencentConfig {
/** 应用accessKey*/
private String accessKeyId;
/**
* 访问键秘钥
*/
private String accessKeySecret;
/**
* 短信签名
*/
private String signature;
/**
* 模板Id
*/
private String templateId;
/** 短信sdkAppId*/
private String sdkAppId;
/** 地域信息默认为 ap-guangzhou*/
private String territory ="ap-guangzhou";
/**请求超时时间 */
private Integer connTimeout = 60;
}
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";
}

View File

@ -1,67 +1,52 @@
package org.dromara.sms4j.tencent.config;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
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;
private SmsClient tencentBean( TencentConfig tencentConfig) {
Credential cred = new Credential(tencentConfig.getAccessKeyId(),tencentConfig.getAccessKeySecret());
HttpProfile httpProfile = new HttpProfile();
httpProfile.setReqMethod("POST");
httpProfile.setConnTimeout(tencentConfig.getConnTimeout());
httpProfile.setEndpoint("sms.tencentcloudapi.com");
ClientProfile clientProfile = new ClientProfile();
clientProfile.setSignMethod("HmacSHA256");
clientProfile.setHttpProfile(httpProfile);
return new SmsClient(cred, tencentConfig.getTerritory(),clientProfile);
}
/** 建造一个腾讯云的短信实现*/
public static TencentSmsImpl createTencentSms(TencentConfig tencentConfig){
if (tencentSmsConfig == null){
tencentSmsConfig = new TencentSmsConfig();
}
if (tencentSms == null){
tencentSms = new TencentSmsImpl(
tencentConfig,
tencentSmsConfig.tencentBean(tencentConfig),
BeanFactory.getExecutor(),
BeanFactory.getDelayedTime()
);
}
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;
}
}
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;
}
}

View File

@ -1,210 +1,205 @@
package org.dromara.sms4j.tencent.service;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
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.tencent.config.TencentConfig;
import lombok.extern.slf4j.Slf4j;
import java.util.*;
import java.util.concurrent.Executor;
@Slf4j
public class TencentSmsImpl implements SmsBlend {
private TencentConfig tencentSmsConfig;
private SmsClient client;
private Executor pool;
private DelayedTime delayed;
public TencentSmsImpl(TencentConfig tencentSmsConfig, SmsClient client, Executor pool, DelayedTime delayed) {
this.tencentSmsConfig = tencentSmsConfig;
this.client = client;
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) {
SmsResponse smsResponse = new SmsResponse();
try {
SendSmsRequest req = new SendSmsRequest();
req.setSignName(tencentSmsConfig.getSignature());
req.setTemplateId(templateId);
req.setSmsSdkAppId(tencentSmsConfig.getSdkAppId());
List<String> list = new ArrayList<>();
for (Map.Entry<String, String> entry : messages.entrySet()) {
list.add(entry.getValue());
}
String[] s = new String[list.size()];
req.setTemplateParamSet(list.toArray(s));
req.setPhoneNumberSet(new String[]{"+86" + phone});
SendSmsResponse res = client.SendSms(req);
String s1 = SendSmsResponse.toJsonString(res);
JSONObject jsonObject = JSON.parseObject(s1);
if (!"Ok".equals(jsonObject.getString("Code"))) {
smsResponse.setErrMessage(jsonObject.getString("Message"));
log.debug(smsResponse.getErrMessage());
}
smsResponse.setMessage(jsonObject.getString("Message"));
smsResponse.setBizId(res.getRequestId());
smsResponse.setData(jsonObject);
} catch (TencentCloudSDKException e) {
throw new SmsBlendException(e.getMessage());
}
return smsResponse;
}
@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) {
SmsResponse smsResponse = new SmsResponse();
try {
SendSmsRequest req = new SendSmsRequest();
req.setSignName(tencentSmsConfig.getSignature());
req.setTemplateId(templateId);
req.setSmsSdkAppId(tencentSmsConfig.getSdkAppId());
List<String> list = new ArrayList<>();
for (Map.Entry<String, String> entry : messages.entrySet()) {
list.add(entry.getValue());
}
String[] s = new String[list.size()];
req.setTemplateParamSet(list.toArray(s));
req.setPhoneNumberSet(arrayToString(phones));
SendSmsResponse res = client.SendSms(req);
String s1 = SendSmsResponse.toJsonString(res);
JSONObject jsonObject = JSON.parseObject(s1);
if (!"Ok".equals(jsonObject.getString("Code"))) {
smsResponse.setErrMessage(jsonObject.getString("Message"));
log.debug(jsonObject.getString("Message"));
}
smsResponse.setMessage(jsonObject.getString("Message"));
smsResponse.setBizId(res.getRequestId());
smsResponse.setData(jsonObject);
} catch (TencentCloudSDKException e) {
throw new SmsBlendException(e.getMessage());
}
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);
}
}

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;
}
}

View File

@ -1,21 +1,26 @@
package org.dromara.sms4j.unisms.config;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.comm.config.BaseConfig;
@Data
@Accessors(chain = true)
public class UniConfig {
/** 访问键标识*/
private String accessKeyId;
/** 访问键秘钥 简易模式不需要配置*/
private String accessKeySecret;
/** 是否为简易模式*/
@SuperBuilder
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class UniConfig extends BaseConfig {
/**
* 是否为简易模式
*/
@Builder.Default
private Boolean isSimple = true;
/** 短信签名*/
private String signature;
/** 模板Id*/
private String templateId;
/** 模板变量名称*/
/**
* 模板变量名称
*/
private String templateName;
}

View File

@ -9,7 +9,7 @@ public class YunPianSmsConfig {
public YunpianConfig yunpianConfig(){
return new YunpianConfig();
return YunpianConfig.builder().build();
}

View File

@ -1,10 +1,10 @@
package org.dromara.sms4j.yunpian.config;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@Builder
public class YunpianConfig {
/**
* 账号唯一标识