!195 集成梦网科技短信发送服务

Merge pull request !195 from 苏苏/Feat_montents
This commit is contained in:
风如歌 2025-06-13 10:05:33 +00:00 committed by Gitee
commit 65cad7d296
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
11 changed files with 352 additions and 3 deletions

12
README_MONTNETS.md Normal file
View File

@ -0,0 +1,12 @@
### 梦网 --- montnets
配置类:`org.dromara.sms4j.montnets.config.MontnetsConfig`
| 字段名称 | 作用 | 默认值 |
|-------------------|---------|-----------------------------|
| `accessKeyId` | 账号 | `null` |
| `accessKeySecret` | 密码 | `null` |
| `url` | 请求地址 | `http://ip:port` |
| `api` | 接口名称 | `/sms/v2/std/send_template` |
| `templateId` | 默认模板id | `null` |
| `templateParam` | 默认模板参数名 | `null` |

View File

@ -97,4 +97,8 @@ public abstract class SupplierConstant {
* 联通一信通 sms
*/
public static final String YIXINTONG = "yixintong";
/**
* 梦网 sms
*/
public static final String MONTNETS = "montnets";
}

View File

@ -41,6 +41,7 @@ import org.dromara.sms4j.jg.config.JgFactory;
import org.dromara.sms4j.lianlu.config.LianLuFactory;
import org.dromara.sms4j.luosimao.config.LuoSiMaoFactory;
import org.dromara.sms4j.mas.config.MasFactory;
import org.dromara.sms4j.montnets.config.MontnetsFactory;
import org.dromara.sms4j.netease.config.NeteaseFactory;
import org.dromara.sms4j.provider.config.SmsConfig;
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
@ -264,6 +265,7 @@ public class SEInitializer {
ProviderFactoryHolder.registerFactory(SubMailFactory.instance());
ProviderFactoryHolder.registerFactory(DanMiFactory.instance());
ProviderFactoryHolder.registerFactory(YiXintongFactory.instance());
ProviderFactoryHolder.registerFactory(MontnetsFactory.instance());
if (SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) {
ProviderFactoryHolder.registerFactory(JdCloudFactory.instance());
}

View File

@ -0,0 +1,41 @@
package org.dromara.sms4j.montnets.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.dromara.sms4j.comm.constant.SupplierConstant;
import org.dromara.sms4j.provider.config.BaseConfig;
/**
* @author SU
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class MontnetsConfig extends BaseConfig {
/**
* 请求地址
*/
private String url;
/**
* 接口名称
*/
private String api;
/**
* 模板code
*/
private String templateId;
/**
* 模板变量名称
*/
private String templateParam;
/**
* 获取供应商
*/
@Override
public String getSupplier() {
return SupplierConstant.MONTNETS;
}
}

View File

@ -0,0 +1,48 @@
package org.dromara.sms4j.montnets.config;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.dromara.sms4j.comm.constant.SupplierConstant;
import org.dromara.sms4j.montnets.service.MontnetsSmsImpl;
import org.dromara.sms4j.provider.factory.AbstractProviderFactory;
/**
* MontnetsSmsConfig
* <p> 梦网对象建造者
*
* @author :SU
* 2025/4/23 10:32
**/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class MontnetsFactory extends AbstractProviderFactory<MontnetsSmsImpl, MontnetsConfig> {
private static final MontnetsFactory INSTANCE = new MontnetsFactory();
/**
* 获取建造者实例
* @return 建造者实例
*/
public static MontnetsFactory instance() {
return INSTANCE;
}
/**
* 创建短信实现对象
* @param montnetsConfig 短信配置对象
* @return 短信实现对象
*/
@Override
public MontnetsSmsImpl createSms(MontnetsConfig montnetsConfig) {
return new MontnetsSmsImpl(montnetsConfig);
}
/**
* 获取供应商
* @return 供应商
*/
@Override
public String getSupplier() {
return SupplierConstant.MONTNETS;
}
}

View File

@ -0,0 +1,152 @@
package org.dromara.sms4j.montnets.service;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.net.URLDecoder;
import cn.hutool.core.net.URLEncodeUtil;
import cn.hutool.json.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.dromara.sms4j.api.entity.SmsResponse;
import org.dromara.sms4j.api.utils.SmsRespUtils;
import org.dromara.sms4j.comm.constant.Constant;
import org.dromara.sms4j.comm.constant.SupplierConstant;
import org.dromara.sms4j.comm.delayedTime.DelayedTime;
import org.dromara.sms4j.comm.exception.SmsBlendException;
import org.dromara.sms4j.comm.utils.SmsUtils;
import org.dromara.sms4j.montnets.config.MontnetsConfig;
import org.dromara.sms4j.montnets.utils.MontnetsUtils;
import org.dromara.sms4j.provider.service.AbstractSmsBlend;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Executor;
/**
* <p>类名: MontnetsSmsImpl
* <p>说明 梦网短信实现
*
* @author :SU
* 2025/4/23 10:32
**/
@Slf4j
public class MontnetsSmsImpl extends AbstractSmsBlend<MontnetsConfig> {
private int retry = 0;
/**
* MontnetsSmsImpl
* <p>构造器用于构造短信实现模块
*/
public MontnetsSmsImpl(MontnetsConfig config, Executor pool, DelayedTime delayedTime) {
super(config, pool, delayedTime);
}
/**
* MontnetsSmsImpl
* <p>构造器用于构造短信实现模块
*/
public MontnetsSmsImpl(MontnetsConfig config) {
super(config);
}
@Override
public String getSupplier() {
return SupplierConstant.MONTNETS;
}
@Override
public SmsResponse sendMessage(String phone, String message) {
LinkedHashMap<String, String> map = new LinkedHashMap<>(1);
map.put(getConfig().getTemplateParam(), message);
return sendMessage(phone, this.getConfig().getTemplateId(), map);
}
@Override
public SmsResponse sendMessage(String phone, LinkedHashMap<String, String> messages) {
if (Objects.isNull(messages)) {
messages = new LinkedHashMap<>();
}
return sendMessage(phone, getConfig().getTemplateId(), messages);
}
@Override
public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap<String, String> messages) {
if (Objects.isNull(messages)) {
messages = new LinkedHashMap<>();
}
String messageStr = formatMessage(messages);
return getSmsResponse(phone, messageStr, templateId);
}
@Override
public SmsResponse massTexting(List<String> phones, String message) {
LinkedHashMap<String, String> map = new LinkedHashMap<>();
map.put(getConfig().getTemplateParam(), message);
return massTexting(phones, getConfig().getTemplateId(), map);
}
@Override
public SmsResponse massTexting(List<String> phones, String templateId, LinkedHashMap<String, String> messages) {
if (Objects.isNull(messages)) {
messages = new LinkedHashMap<>();
}
String messageStr = formatMessage(messages);
return getSmsResponse(SmsUtils.addCodePrefixIfNot(phones), messageStr, templateId);
}
private SmsResponse getSmsResponse(String phone, String message, String templateId) {
String requestUrl;
String paramStr;
try {
requestUrl = MontnetsUtils.generateSendSmsRequestUrl(this.getConfig());
paramStr = MontnetsUtils.generateParamBody(this.getConfig(), phone, message, templateId);
} catch (Exception e) {
log.error("montnets send message error", e);
throw new SmsBlendException(e.getMessage());
}
log.debug("requestUrl {}", requestUrl);
Map<String, String> headers = MapUtil.newHashMap(1, true);
headers.put(Constant.CONTENT_TYPE, Constant.APPLICATION_JSON_UTF8);
SmsResponse smsResponse;
try {
smsResponse = this.getResponse(this.http.postJson(requestUrl, headers, paramStr));
} catch (SmsBlendException e) {
smsResponse = this.errorResp(e.message);
}
if (!smsResponse.isSuccess() && this.retry != this.getConfig().getMaxRetries()) {
return this.requestRetry(phone, message, templateId);
} else {
this.retry = 0;
return smsResponse;
}
}
private SmsResponse requestRetry(String phone, String message, String templateId) {
this.http.safeSleep(this.getConfig().getRetryInterval());
++this.retry;
log.warn("短信第 {} 次重新发送", this.retry);
return this.getSmsResponse(phone, message, templateId);
}
private SmsResponse getResponse(JSONObject resJson) {
return SmsRespUtils.resp(URLDecoder.decode(resJson.getStr("desc"), StandardCharsets.UTF_8), "0".equals(resJson.getStr("result")), getConfigId());
}
/**
* <p>说明格式化消息
*
* @param messages 模板参数key-value集合
* @return 格式化后的消息 key1=urlEncode(value1)&key2=urlEncode(value2)
*/
private String formatMessage(LinkedHashMap<String, String> messages) {
StringJoiner joiner = new StringJoiner("&");
for (Map.Entry<String, String> entry : messages.entrySet()) {
joiner.add(entry.getKey() + "=" + URLEncodeUtil.encode(entry.getValue(), StandardCharsets.UTF_8));
}
return URLEncodeUtil.encode(joiner.toString());
}
}

View File

@ -0,0 +1,62 @@
package org.dromara.sms4j.montnets.utils;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.json.JSONUtil;
import org.dromara.sms4j.comm.utils.SmsDateUtils;
import org.dromara.sms4j.montnets.config.MontnetsConfig;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* @author SU
* 2025/4/23 10:32
*/
public class MontnetsUtils {
public static String generateSendSmsRequestUrl(MontnetsConfig montnetsConfig) {
String url = montnetsConfig.getUrl();
String api = montnetsConfig.getApi();
return url + api;
}
/**
* 生成请求body参数
*
* @param montnetsConfig 短信配置
* @param phone 手机号
* @param message 短信内容
* @param templateId 模板ID
*/
public static Map<String, String> generateParamMap(MontnetsConfig montnetsConfig, String phone, String message, String templateId) {
Map<String, String> paramMap = new HashMap<>();
String accessKeyId = montnetsConfig.getAccessKeyId();
String accessKeySecret = montnetsConfig.getAccessKeySecret();
String timeStamp = SmsDateUtils.formatDateToStr(new Date(), "MMddHHmmss", SmsDateUtils.gmt8());
paramMap.put("userid", accessKeyId);
paramMap.put("pwd", DigestUtil.md5Hex(accessKeyId.toUpperCase(Locale.ENGLISH) + "00000000" + accessKeySecret + timeStamp));
paramMap.put("timestamp", timeStamp);
paramMap.put("mobile", phone);
paramMap.put("content", message);
paramMap.put("tmplid", templateId);
return paramMap;
}
/**
* 生成请求参数body字符串
*
* @param montnetsConfig 短信配置
* @param phone 手机号
* @param message 短信内容
* @param templateId 模板ID
*/
public static String generateParamBody(MontnetsConfig montnetsConfig, String phone, String message, String templateId) {
Map<String, String> paramMap = generateParamMap(montnetsConfig, phone, message, templateId);
return JSONUtil.toJsonStr(paramMap);
}
}

View File

@ -33,6 +33,7 @@ import org.dromara.sms4j.jg.config.JgFactory;
import org.dromara.sms4j.lianlu.config.LianLuFactory;
import org.dromara.sms4j.luosimao.config.LuoSiMaoFactory;
import org.dromara.sms4j.mas.config.MasFactory;
import org.dromara.sms4j.montnets.config.MontnetsFactory;
import org.dromara.sms4j.netease.config.NeteaseFactory;
import org.dromara.sms4j.provider.config.SmsConfig;
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
@ -150,6 +151,7 @@ public class SmsBlendsInitializer {
ProviderFactoryHolder.registerFactory(SubMailFactory.instance());
ProviderFactoryHolder.registerFactory(DanMiFactory.instance());
ProviderFactoryHolder.registerFactory(YiXintongFactory.instance());
ProviderFactoryHolder.registerFactory(MontnetsFactory.instance());
if (SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) {
if (SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) {
ProviderFactoryHolder.registerFactory(JdCloudFactory.instance());

View File

@ -169,6 +169,20 @@ sms:
template-id: #(可选)模板编号(若配置此参数,则会默认使用该模板,以便提高服务方性能)
sign-code: #(可选)短信前置签名编号(登陆平台-接口业务-我的签名查看)
f: 1 #(可选)默认为1提交时检测方式
# 梦网
montnets:
# 您的accessKey
access-key-id: 您的accessKey
# 您的accessKeySecret
access-key-secret: 您的accessKeySecret
# 服务地址
url: 服务地址 http://ip:port
# 接口地址
api: 接口地址 /sms/v2/std/send_template
# 默认模板id
templateId: 默认模板id
# 默认模板参数名
templateParam: 默认模板参数名
sms-oa:
config-type: yaml

View File

@ -281,7 +281,7 @@ public class Sms4jTest {
log.info(JSONUtil.toJsonStr(smsResponse3));
Assert.isTrue(smsResponse3.isSuccess());
SmsResponse smsResponse4 = dz.massTexting(phones, "" ,messages);
SmsResponse smsResponse4 = dz.massTexting(phones, "", messages);
log.info(JSONUtil.toJsonStr(smsResponse4));
Assert.isTrue(smsResponse4.isSuccess());
@ -559,4 +559,14 @@ public class Sms4jTest {
Assert.isTrue(smsResponse2.isSuccess());
}
/**
* 梦网短信
*/
@Test
public void montnetsSmsTest() {
// 梦网
SmsResponse smsResponse = SmsFactory.getBySupplier(SupplierConstant.MONTNETS).sendMessage(PHONE, SmsUtils.getRandomInt(6));
Assert.isTrue(smsResponse.isSuccess());
}
}

View File

@ -33,6 +33,7 @@ import org.dromara.sms4j.jg.config.JgFactory;
import org.dromara.sms4j.lianlu.config.LianLuFactory;
import org.dromara.sms4j.luosimao.config.LuoSiMaoFactory;
import org.dromara.sms4j.mas.config.MasFactory;
import org.dromara.sms4j.montnets.config.MontnetsFactory;
import org.dromara.sms4j.netease.config.NeteaseFactory;
import org.dromara.sms4j.provider.config.SmsConfig;
import org.dromara.sms4j.provider.factory.BaseProviderFactory;
@ -84,7 +85,7 @@ public class SmsBlendsInitializer {
SmsProxyFactory.addPreProcessor(new CoreMethodParamValidateProcessor(null));
}
//注册执行器实现
if(this.smsConfig.getRestricted()){
if (this.smsConfig.getRestricted()) {
SmsProxyFactory.addPreProcessor(new RestrictedProcessor());
SmsProxyFactory.addPreProcessor(new BlackListProcessor());
SmsProxyFactory.addPreProcessor(new BlackListRecordingProcessor());
@ -150,6 +151,7 @@ public class SmsBlendsInitializer {
ProviderFactoryHolder.registerFactory(SubMailFactory.instance());
ProviderFactoryHolder.registerFactory(DanMiFactory.instance());
ProviderFactoryHolder.registerFactory(YiXintongFactory.instance());
ProviderFactoryHolder.registerFactory(MontnetsFactory.instance());
if (SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) {
if (SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) {
ProviderFactoryHolder.registerFactory(JdCloudFactory.instance());