新增助通短信平台,官网 ztinfo.cn

This commit is contained in:
dazer007 2023-07-03 18:31:47 +08:00
parent c495303d52
commit 41449a4752
10 changed files with 440 additions and 1 deletions

View File

@ -25,4 +25,8 @@ public class SmsResponse {
* @since 2.3.0 * @since 2.3.0
*/ */
private Object data; private Object data;
/** 状态码*/
private String code;
private String message;
} }

View File

@ -24,6 +24,8 @@ import org.dromara.sms4j.unisms.config.UniConfig;
import org.dromara.sms4j.unisms.config.UniFactory; import org.dromara.sms4j.unisms.config.UniFactory;
import org.dromara.sms4j.yunpian.config.YunPianFactory; import org.dromara.sms4j.yunpian.config.YunPianFactory;
import org.dromara.sms4j.yunpian.config.YunpianConfig; import org.dromara.sms4j.yunpian.config.YunpianConfig;
import org.dromara.sms4j.zhutong.config.ZhutongConfig;
import org.dromara.sms4j.zhutong.config.ZhutongFactory;
/** /**
* SupplierFactory * SupplierFactory
@ -105,6 +107,13 @@ public class SupplierFactory {
return NeteaseFactory.instance().getConfig(); return NeteaseFactory.instance().getConfig();
} }
/**
* 助通信配置获取
*/
public static ZhutongConfig getZhutongConfig() {
return ZhutongFactory.instance().getConfig();
}
/** /**
* setSupplierConfig * setSupplierConfig
* <p>通用化set用于设置 * <p>通用化set用于设置

View File

@ -13,6 +13,7 @@ import org.dromara.sms4j.provider.base.BaseProviderFactory;
import org.dromara.sms4j.tencent.config.TencentFactory; import org.dromara.sms4j.tencent.config.TencentFactory;
import org.dromara.sms4j.unisms.config.UniFactory; import org.dromara.sms4j.unisms.config.UniFactory;
import org.dromara.sms4j.yunpian.config.YunPianFactory; import org.dromara.sms4j.yunpian.config.YunPianFactory;
import org.dromara.sms4j.zhutong.config.ZhutongFactory;
/** /**
* SupplierType * SupplierType
@ -40,7 +41,9 @@ public enum SupplierType {
/** 天翼云 */ /** 天翼云 */
CTYUN("天翼云短信", CtyunFactory.instance()), CTYUN("天翼云短信", CtyunFactory.instance()),
/** 网易云信 */ /** 网易云信 */
NETEASE("网易云短信", NeteaseFactory.instance()) NETEASE("网易云短信", NeteaseFactory.instance()),
/** 助通短信 */
ZHUTONG("助通短信", ZhutongFactory.instance())
; ;

View File

@ -0,0 +1,36 @@
package org.dromara.sms4j.zhutong.config;
import lombok.*;
import lombok.experimental.SuperBuilder;
import org.dromara.sms4j.api.universal.SupplierConfig;
import org.dromara.sms4j.comm.config.BaseConfig;
/**
* 助通-自定义短信发送-配置
* @author dazer007
* @see BaseConfig
* 说明1accessKeyId ====> username 助通终端用户管理的用户名非登录账号密码请登录后台管理地址进行查看http://mix2.zthysms.com/login
* 说明2accessKeySecret ====> password 终端用户管理的密码
* 说明3signature ====> 短信签名可以为空为空发送自定义短信无需要提前创建短信模板; 不为空发送:模板短信
* 说明4templateId ====> 模板id可以为空为空发送自定义短信无需要提前创建短信模板; 不为空发送:模板短信
* 说明4templateName ====> 模板变量名称可以为空为空发送自定义短信无需要提前创建短信模板; 不为空发送:模板短信
*/
@Getter
@Setter
@SuperBuilder
@ToString(callSuper = true)
@NoArgsConstructor
public class ZhutongConfig extends BaseConfig implements SupplierConfig {
/**
* 模板变量名称
* 查看地址https://mix2.zthysms.com/index.html#/TemplateManagement
* 允许为空为空使用无模板形式发送短信
*/
private String templateName;
/**
* 默认请求地址
* 不同区域可切换请求地址也可以不修改请参考官方文档https://doc.zthysms.com/web/#/1/236
*/
@Builder.Default
private String requestUrl = "https://api.mix2.zthysms.com/";
}

View File

@ -0,0 +1,67 @@
package org.dromara.sms4j.zhutong.config;
import org.dromara.sms4j.comm.factory.BeanFactory;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
import org.dromara.sms4j.zhutong.service.ZhutongSmsImpl;
public class ZhutongFactory implements BaseProviderFactory<ZhutongSmsImpl, ZhutongConfig> {
private static ZhutongSmsImpl ZhutongSmsImpl;
private static final ZhutongFactory INSTANCE = new ZhutongFactory();
private static final class ConfigHolder {
private static ZhutongConfig config = ZhutongConfig.builder().build();
}
private ZhutongFactory() {
}
/**
* 获取建造者实例
* @return 建造者实例
*/
public static ZhutongFactory instance() {
return INSTANCE;
}
/**
* 建造一个助通短信实现
*/
@Override
public ZhutongSmsImpl createSms(ZhutongConfig ZhutongConfig){
if (ZhutongSmsImpl == null){
ZhutongSmsImpl = createMultitonSms(ZhutongConfig);
}
return ZhutongSmsImpl;
}
@Override
public ZhutongSmsImpl createMultitonSms(ZhutongConfig zhutongConfig) {
return new ZhutongSmsImpl(zhutongConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
}
/** 刷新对象*/
@Override
public ZhutongSmsImpl refresh(ZhutongConfig zhutongConfig){
ZhutongSmsImpl = new ZhutongSmsImpl(zhutongConfig, BeanFactory.getExecutor(), BeanFactory.getDelayedTime());
return ZhutongSmsImpl;
}
/**
* 获取配置
* @return 配置对象
*/
@Override
public ZhutongConfig getConfig() {
return ZhutongFactory.ConfigHolder.config;
}
/**
* 设置配置
* @param config 配置对象
*/
@Override
public void setConfig(ZhutongConfig config) {
ZhutongFactory.ConfigHolder.config = config;
}
}

View File

@ -0,0 +1,8 @@
/**
* zt助通短信
* 平台官网https://www.ztinfo.cn/products/sms
* 文档地址https://doc.zthysms.com/web/#/1/236
* 管理后台地址必须要验证码http://mix2.zthysms.com/login
* 这里使用的接口自定义短信发送sendSms模板短信发送sendSmsTp
*/
package org.dromara.sms4j.zhutong;

View File

@ -0,0 +1,231 @@
package org.dromara.sms4j.zhutong.service;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.AbstractSmsBlend;
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.zhutong.config.ZhutongConfig;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
/**
* <p>助通短信发送
* <p>1. 自定义短信发送 无需定义模板 https://doc.zthysms.com/web/#/1/14
* <p>2. 模板短信发送 需定义模板 https://doc.zthysms.com/web/#/1/13
*/
@Slf4j
public class ZhutongSmsImpl extends AbstractSmsBlend {
private final ZhutongConfig zhutongConfig;
/**
* ZhutongSmsImpl
* <p>构造器用于构造短信实现模块
*/
public ZhutongSmsImpl(ZhutongConfig zhutongConfig, Executor pool, DelayedTime delayedTime) {
super(pool, delayedTime);
this.zhutongConfig = zhutongConfig;
}
@Override
@Restricted
public SmsResponse sendMessage(String phone, String message) {
//如果模板id为空 or 模板变量名称为空使用无模板的自定义短信发送
if (StrUtil.hasBlank(zhutongConfig.getSignature(), zhutongConfig.getTemplateId(), zhutongConfig.getTemplateName())) {
return getSmsResponse(phone, message);
}
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put(zhutongConfig.getTemplateName(), message);
return sendMessage(phone, zhutongConfig.getTemplateId(), map);
}
@Override
@Restricted
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
return getSmsResponseTemplate(templateId, phone, messages);
}
@Override
@Restricted
public SmsResponse massTexting(List<String> phones, String message) {
//如果模板id为空 or 模板变量名称为空使用无模板的自定义短信发送
if (StrUtil.hasBlank(zhutongConfig.getSignature(), zhutongConfig.getTemplateId(), zhutongConfig.getTemplateName())) {
return getSmsResponse(phones, message);
}
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put(zhutongConfig.getTemplateName(), message);
return massTexting(phones, zhutongConfig.getTemplateId(), map);
}
@Override
@Restricted
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
return getSmsResponseTemplate(templateId, phones, messages);
}
/**
* 发送 自定义短信https://doc.zthysms.com/web/#/1/14
*/
protected SmsResponse getSmsResponse(List<String> phones, String content) {
String requestUrl = zhutongConfig.getRequestUrl();
String username = zhutongConfig.getAccessKeyId();
String password = zhutongConfig.getAccessKeySecret();
validator(requestUrl, username, password);
if (CollectionUtil.isEmpty(phones)) {
throw new SmsBlendException("助通短信:手机号不能为空!");
}
if (phones.size() >= 2000) {
throw new SmsBlendException("助通短信手机号码最多支持2000个");
}
if (StrUtil.isBlank(content)) {
throw new SmsBlendException("助通短信:发送内容不能为空!");
}
if (content.length() >= 1000) {
throw new SmsBlendException("助通短信发送内容不能超过1000个字符");
}
if (!content.contains("")) {
throw new SmsBlendException("助通短信自定义短信发送内容必须包含签名信息【助通科技】您的验证码是8888");
}
String urls = requestUrl + "v2/sendSms";
long tKey = System.currentTimeMillis() / 1000;
Map<String, String> json = new HashMap<>(5);
//账号
json.put("username", username);
//密码
json.put("password", SecureUtil.md5(SecureUtil.md5(password) + tKey));
//tKey
json.put("tKey", tKey + "");
//手机号
json.put("mobile", StrUtil.join(StrPool.COMMA, phones));
//内容
json.put("content", content);
AtomicReference<SmsResponse> reference = new AtomicReference<>();
http.post(urls)
.addHeader("Content-Type", Constant.APPLICATION_JSON_UTF8)
.addBody(JSONUtil.toJsonStr(json))
.onSuccess(((data, req, res) -> reference.set(this.getResponse(res.get(JSONObject.class)))))
.onError((ex, req, res) -> reference.set(this.getResponse(res.get(JSONObject.class))))
.execute();
log.info("助通短信 URL={} json={} 响应值为={}", urls, json, reference.get());
return reference.get();
}
protected SmsResponse getSmsResponse(String mobile, String content) {
return getSmsResponse(ListUtil.of(mobile), content);
}
/**
* 发送 模板短信https://doc.zthysms.com/web/#/1/13
*/
protected SmsResponse getSmsResponseTemplate(String templateId, List<String> phones, LinkedHashMap<String, String> messages) {
String requestUrl = zhutongConfig.getRequestUrl();
String username = zhutongConfig.getAccessKeyId();
String password = zhutongConfig.getAccessKeySecret();
String signature = zhutongConfig.getSignature();
validator(requestUrl, username, password);
if (StrUtil.isBlank(signature)) {
throw new SmsBlendException("助通短信模板短信中已报备的签名signature不能为空");
}
//助通短信签名需要包含这里自助添加
if (!signature.startsWith("")) {
signature = "" + signature;
}
if (!signature.endsWith("")) {
signature = signature + "";
}
if (StrUtil.isBlank(templateId)) {
throw new SmsBlendException("助通短信模板短信模板id不能为空");
}
//地址
String urls = requestUrl + "v2/sendSmsTp";
//请求入参
JSONObject requestJson = new JSONObject();
//账号
requestJson.put("username", username);
//tKey
long tKey = System.currentTimeMillis() / 1000;
requestJson.put("tKey", tKey);
//明文密码
requestJson.put("password", SecureUtil.md5(SecureUtil.md5(password) + tKey));
//模板ID
requestJson.put("tpId", templateId);
//签名
requestJson.put("signature", signature);
//扩展号
requestJson.put("ext", "");
//自定义参数
requestJson.put("extend", "");
//发送记录集合
JSONArray records = new JSONArray();
{
for (String mobile : phones) {
JSONObject record = new JSONObject();
//手机号
record.put("mobile", mobile);
//替换变量
JSONObject param = new JSONObject();
param.putAll(messages);
record.put("tpContent", param);
records.add(record);
}
}
requestJson.put("records", records);
AtomicReference<SmsResponse> reference = new AtomicReference<>();
http.post(urls)
.addHeader("Content-Type", Constant.APPLICATION_JSON_UTF8)
.addBody(requestJson.getInnerMap())
.onSuccess(((data, req, res) -> reference.set(this.getResponse(res.get(JSONObject.class)))))
.onError((ex, req, res) -> reference.set(this.getResponse(res.get(JSONObject.class))))
.execute();
log.info("助通短信 URL={} json={} 响应值为={}", urls, requestJson.toJSONString(), reference.get());
return reference.get();
}
protected SmsResponse getSmsResponseTemplate(String templateId, String mobile, LinkedHashMap<String, String> content) {
return getSmsResponseTemplate(templateId, ListUtil.of(mobile), content);
}
private SmsResponse getResponse(JSONObject jsonObject) {
SmsResponse response = new SmsResponse();
response.setSuccess(jsonObject.getInteger("code") <= 200);
response.setData(jsonObject);
return response;
}
private void validator(String requestUrl, String username, String password) {
if (StrUtil.isBlank(requestUrl)) {
throw new SmsBlendException("助通短信requestUrl不能为空");
}
if (!requestUrl.endsWith("/")) {
throw new SmsBlendException("助通短信requestUrl必须以'/'结尾!");
}
if (StrUtil.hasBlank(username, password)) {
throw new SmsBlendException("助通短信账号username、密码password不能为空");
}
}
}

View File

@ -27,3 +27,10 @@ sms:
statusCallBack: statusCallBack:
#华为分配的app请求地址 #华为分配的app请求地址
url: https://XXXXX.cn-north-4.XXXXXXXX.com:443 url: https://XXXXX.cn-north-4.XXXXXXXX.com:443
zhutong:
#助通终端用户管理的用户名 username,非登录账号密码请登录后台管理地址进行查看http://mix2.zthysms.com/login
accessKeyId: tushu1122XXX
#助通终端用户管理的用户名 passwrod
accessKeySecret: UbXXX4SL
#短信签名具体在这里查看审核过的短信签名https://mix2.zthysms.com/index.html#/SignatureManagement
signature: 上海千XXXX

View File

@ -1,5 +1,6 @@
package org.dromara.sms4j.example; package org.dromara.sms4j.example;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
@ -11,6 +12,8 @@ import org.dromara.sms4j.provider.enumerate.SupplierType;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import java.util.LinkedHashMap;
@Slf4j @Slf4j
@SpringBootTest @SpringBootTest
class Sms4jTest { class Sms4jTest {
@ -130,4 +133,65 @@ class Sms4jTest {
Assert.isTrue(Long.parseLong(smsResponse.getCode()) <= 200 && smsResponse.isSuccess()); Assert.isTrue(Long.parseLong(smsResponse.getCode()) <= 200 && smsResponse.isSuccess());
} }
/**
* 助通短信测试1无模板
*/
@Test
public void zhutongSms1Test() {
if (StrUtil.isBlank(PHONE)) {
return;
}
// 助通短信短信
String msg = StrUtil.format("【图书商城】您好,你的验证码是{}5分钟失效", SmsUtil.getRandomInt(6));
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.ZHUTONG).sendMessage(PHONE, msg);
log.info(JSONUtil.toJsonStr(smsResponse));
Assert.isTrue(smsResponse.isSuccess());
}
/**
* 助通短信测试2有模板
*/
@Test
public void zhutongSmsTest2Template() {
if (StrUtil.isBlank(PHONE)) {
return;
}
// 助通短信短信
LinkedHashMap<String, String> messages = new LinkedHashMap<>(1);
messages.put("code", SmsUtil.getRandomInt(6));
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.ZHUTONG).sendMessage(PHONE, "59264", messages);
log.info(JSONUtil.toJsonStr(smsResponse));
Assert.isTrue(smsResponse.isSuccess());
}
/**
* 助通短信测试3无模板群发
*/
@Test
public void zhutongSms3MoreTest() {
if (StrUtil.isBlank(PHONE)) {
return;
}
// 助通短信短信
String msg = StrUtil.format("【图书商城】您好,你的验证码是{}5分钟失效", SmsUtil.getRandomInt(6));
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.ZHUTONG).massTexting(ListUtil.of(PHONE, "17312345678"), msg);
log.info(JSONUtil.toJsonStr(smsResponse));
Assert.isTrue(smsResponse.isSuccess());
}
/**
* 助通短信测试4有模板 多人群发
*/
@Test
public void zhutongSms4TemplateTest() {
if (StrUtil.isBlank(PHONE)) {
return;
}
// 助通短信短信
LinkedHashMap<String, String> messages = new LinkedHashMap<>(1);
messages.put("code", SmsUtil.getRandomInt(6));
SmsResponse smsResponse = SmsFactory.createSmsBlend(SupplierType.ZHUTONG).massTexting(ListUtil.of(PHONE, "17312345678"), "59264", messages);
log.info(JSONUtil.toJsonStr(smsResponse));
Assert.isTrue(smsResponse.isSuccess());
}
} }

View File

@ -11,6 +11,7 @@ import org.dromara.sms4j.netease.config.NeteaseConfig;
import org.dromara.sms4j.tencent.config.TencentConfig; import org.dromara.sms4j.tencent.config.TencentConfig;
import org.dromara.sms4j.unisms.config.UniConfig; import org.dromara.sms4j.unisms.config.UniConfig;
import org.dromara.sms4j.yunpian.config.YunpianConfig; import org.dromara.sms4j.yunpian.config.YunpianConfig;
import org.dromara.sms4j.zhutong.config.ZhutongConfig;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -93,4 +94,13 @@ public class SupplierConfig {
protected NeteaseConfig neteaseConfig(){ protected NeteaseConfig neteaseConfig(){
return SupplierFactory.getNeteaseConfig(); return SupplierFactory.getNeteaseConfig();
} }
/**
* 助通信差异化配置
*/
@Bean
@ConfigurationProperties(prefix = "sms.zhutong")
protected ZhutongConfig zhutongConfig(){
return SupplierFactory.getZhutongConfig();
}
} }