diff --git a/sms4j-comm/src/main/java/org/dromara/sms4j/comm/constant/SupplierConstant.java b/sms4j-comm/src/main/java/org/dromara/sms4j/comm/constant/SupplierConstant.java index 59de4921..31057f38 100644 --- a/sms4j-comm/src/main/java/org/dromara/sms4j/comm/constant/SupplierConstant.java +++ b/sms4j-comm/src/main/java/org/dromara/sms4j/comm/constant/SupplierConstant.java @@ -93,4 +93,8 @@ public abstract class SupplierConstant { * danmi sms */ public static final String DAN_MI = "danmi"; + /** + * 联通一信通 sms + */ + public static final String YIXINTONG = "yixintong"; } diff --git a/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java b/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java index fce1f3bc..18b718ef 100644 --- a/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java +++ b/sms4j-javase-plugin/src/main/java/org/dromara/sms4j/javase/config/SEInitializer.java @@ -50,6 +50,7 @@ import org.dromara.sms4j.qiniu.config.QiNiuFactory; import org.dromara.sms4j.submail.config.SubMailFactory; import org.dromara.sms4j.tencent.config.TencentFactory; import org.dromara.sms4j.unisms.config.UniFactory; +import org.dromara.sms4j.yixintong.config.YiXintongFactory; import org.dromara.sms4j.yunpian.config.YunPianFactory; import org.dromara.sms4j.zhutong.config.ZhutongFactory; @@ -266,6 +267,7 @@ public class SEInitializer { ProviderFactoryHolder.registerFactory(LuoSiMaoFactory.instance()); ProviderFactoryHolder.registerFactory(SubMailFactory.instance()); ProviderFactoryHolder.registerFactory(DanMiFactory.instance()); + ProviderFactoryHolder.registerFactory(YiXintongFactory.instance()); if (SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) { ProviderFactoryHolder.registerFactory(JdCloudFactory.instance()); } diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/config/YiXintongConfig.java b/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/config/YiXintongConfig.java new file mode 100644 index 00000000..36fabb2e --- /dev/null +++ b/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/config/YiXintongConfig.java @@ -0,0 +1,48 @@ +package org.dromara.sms4j.yixintong.config; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.sms4j.comm.constant.SupplierConstant; +import org.dromara.sms4j.provider.config.BaseConfig; + +/** + *

类名: YiXintongConfig + *

说明:联通一信通平台配置类 + *

所用到配置项:spCode、f、accessKeyId(用户名)、accessKeySecret(接口密钥)、signCode、templateId、retryInterval、maxRetries + * + * @author moat + * @create 2024-07-30 16:50 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class YiXintongConfig extends BaseConfig { + + /** + * 短信发送请求地址 + */ + private String requestUrl = "https://api.ums86.com:9600/sms/Api/Send.do"; + + /** + * 企业编号 + */ + private String spCode; + + /** + * 签名编号 + */ + private String signCode; + + /** + * 提交时检测方式 + * 1 --- 提交号码中有效的号码仍正常发出短信,无效的号码在返回参数faillist中列出 + * + * 不为1 或该参数不存在 --- 提交号码中只要有无效的号码,那么所有的号码都不发出短信,无效号码在返回参数faillist中列出 + */ + private String f = "1"; + + + @Override + public String getSupplier() { + return SupplierConstant.YIXINTONG; + } +} diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/config/YiXintongFactory.java b/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/config/YiXintongFactory.java new file mode 100644 index 00000000..4c9573ea --- /dev/null +++ b/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/config/YiXintongFactory.java @@ -0,0 +1,46 @@ +package org.dromara.sms4j.yixintong.config; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.sms4j.comm.constant.SupplierConstant; +import org.dromara.sms4j.provider.factory.AbstractProviderFactory; +import org.dromara.sms4j.yixintong.service.YiXintongSmsImpl; + +/** + *

类名: YiXintongFactory + *

说明:联通一信通平台短信对象建造 + * + * @author moat + * @create 2024-07-30 17:10 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class YiXintongFactory extends AbstractProviderFactory { + + private static final YiXintongFactory INSTANCE = new YiXintongFactory(); + + /** + * 获取建造者实例 + * @return 建造者实例 + */ + public static YiXintongFactory instance() { + return INSTANCE; + } + + /** + * createSms + *

建造一个短信实现对像 + */ + @Override + public YiXintongSmsImpl createSms(YiXintongConfig yiXintongConfig) { + return new YiXintongSmsImpl(yiXintongConfig); + } + + /** + * 获取供应商 + * @return 供应商 + */ + @Override + public String getSupplier() { + return SupplierConstant.YIXINTONG; + } +} diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/service/YiXintongSmsImpl.java b/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/service/YiXintongSmsImpl.java new file mode 100644 index 00000000..c6b2996b --- /dev/null +++ b/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/service/YiXintongSmsImpl.java @@ -0,0 +1,129 @@ +package org.dromara.sms4j.yixintong.service; + +import cn.hutool.core.util.StrUtil; +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.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.provider.service.AbstractSmsBlend; +import org.dromara.sms4j.yixintong.config.YiXintongConfig; +import org.dromara.sms4j.yixintong.utils.YiXintongUtils; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; + +/** + *

类名: YiXintongSmsImpl + *

说明:联通一信通 sms + * + * @author moat + * @create 2024-07-30 16:59 + */ +@Slf4j +public class YiXintongSmsImpl extends AbstractSmsBlend { + + private int retry = 0; + + public YiXintongSmsImpl(YiXintongConfig config, Executor pool, DelayedTime delayedTime) { + super(config, pool, delayedTime); + } + + public YiXintongSmsImpl(YiXintongConfig config) { + super(config); + } + + @Override + public String getSupplier() { + return SupplierConstant.YIXINTONG; + } + + @Override + public SmsResponse sendMessage(String phone, String message) { + return getSmsResponse(phone, message, getConfig().getTemplateId()); + } + + @Override + public SmsResponse massTexting(List phones, String message) { + return getSmsResponse(SmsUtils.joinComma(phones), message, getConfig().getTemplateId()); + } + + @Override + public SmsResponse sendMessage(String phone, LinkedHashMap messages) { + throw new SmsBlendException("不支持此方法"); + } + + @Override + public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap messages) { + throw new SmsBlendException("不支持此方法"); + } + + @Override + public SmsResponse massTexting(List phones, String templateId, LinkedHashMap messages) { + throw new SmsBlendException("不支持此方法"); + } + + + private SmsResponse getSmsResponse(String phone, String message, String templateId) { + final YiXintongConfig config = getConfig(); + if (StrUtil.isBlank(phone)){ + log.error("phone is required."); + throw new SmsBlendException("phone is required."); + } + if (StrUtil.isBlank(message)){ + log.error("message is required."); + throw new SmsBlendException("message is required."); + } + // 生成20位流水号 + String serialNumber = SmsUtils.getRandomInt(20); + + Map forms = new HashMap<>(); + forms.put("SpCode", config.getSpCode()); + forms.put("LoginName", config.getAccessKeyId()); + forms.put("Password", config.getAccessKeySecret()); + forms.put("MessageContent", message); + forms.put("UserNumber", phone); + forms.put("templateId", templateId); + forms.put("SerialNumber", serialNumber); + forms.put("ScheduleTime", ""); // 立即发送 + forms.put("f", config.getF()); + forms.put("signCode", config.getSignCode()); + + SmsResponse smsResponse; + try { + smsResponse = getResponse(YiXintongUtils.postForm(config.getRequestUrl(), forms)); + } catch (SmsBlendException e) { + smsResponse = errorResp(e.message); + } + if (smsResponse.isSuccess() || retry == config.getMaxRetries()) { + retry = 0; + return smsResponse; + } + return requestRetry(phone, message, templateId); + } + + + + private SmsResponse requestRetry(String phone, String message, String templateId) { + http.safeSleep(getConfig().getRetryInterval()); + retry ++; + log.warn("The SMS has been resent for the {}th time.", retry); + return getSmsResponse(phone, message, templateId); + } + + + /** + * 构造统一短信返回信息 + * @param body 原始响应信息 + * @return 短信返回信息 + */ + private SmsResponse getResponse(String body) { + return SmsRespUtils.resp(body, StrUtil.contains(body, "result=0&"), getConfigId()); + } + +} diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/utils/YiXintongUtils.java b/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/utils/YiXintongUtils.java new file mode 100644 index 00000000..83cb665c --- /dev/null +++ b/sms4j-provider/src/main/java/org/dromara/sms4j/yixintong/utils/YiXintongUtils.java @@ -0,0 +1,54 @@ +package org.dromara.sms4j.yixintong.utils; + +import cn.hutool.http.HttpRequest; +import cn.hutool.http.HttpResponse; +import org.dromara.sms4j.comm.exception.SmsBlendException; + +import java.util.Map; + +/** + *

类名: YiXintongUtils + *

说明:联通一信通工具类 + * + * @author moat + * @create 2024-07-31 9:55 + */ +public class YiXintongUtils { + + + + /** + * 发送post form请求 + * + * @param url 请求地址 + * @param forms 表单参数 + * @return 返回体 + */ + public static String postForm(String url, Map forms) { + return postForm(url, null, forms, "gbk"); + } + + + /** + * 发送post form请求 + * + * @param url 请求地址 + * @param headers 请求头 + * @param forms 表单参数 + * @param charset 字符集编码 + * @return 返回体 + */ + public static String postForm(String url, Map headers, Map forms, String charset) { + try (HttpResponse response = HttpRequest.post(url) + .addHeaders(headers) + .form(forms) + .charset(charset) + .execute()) { + return response.body(); + } catch (Exception e) { + throw new SmsBlendException(e.getMessage()); + } + } + + +} diff --git a/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java b/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java index b31578e5..6b658299 100644 --- a/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java +++ b/sms4j-solon-plugin/src/main/java/org/dromara/sms4j/solon/config/SmsBlendsInitializer.java @@ -40,6 +40,7 @@ import org.dromara.sms4j.solon.holder.SolonSmsDaoHolder; import org.dromara.sms4j.submail.config.SubMailFactory; import org.dromara.sms4j.tencent.config.TencentFactory; import org.dromara.sms4j.unisms.config.UniFactory; +import org.dromara.sms4j.yixintong.config.YiXintongFactory; import org.dromara.sms4j.yunpian.config.YunPianFactory; import org.dromara.sms4j.zhutong.config.ZhutongFactory; import org.noear.solon.core.AppContext; @@ -135,6 +136,7 @@ public class SmsBlendsInitializer { ProviderFactoryHolder.registerFactory(LuoSiMaoFactory.instance()); ProviderFactoryHolder.registerFactory(SubMailFactory.instance()); ProviderFactoryHolder.registerFactory(DanMiFactory.instance()); + ProviderFactoryHolder.registerFactory(YiXintongFactory.instance()); if(SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) { ProviderFactoryHolder.registerFactory(JdCloudFactory.instance()); } diff --git a/sms4j-spring-boot-example/src/main/resources/application.yml b/sms4j-spring-boot-example/src/main/resources/application.yml index 1780baae..4b906f27 100644 --- a/sms4j-spring-boot-example/src/main/resources/application.yml +++ b/sms4j-spring-boot-example/src/main/resources/application.yml @@ -155,6 +155,14 @@ sms: accessKeyId: ACCOUNT SID accessKeySecret: AUTH TOKEN action: 默认请求方法 distributor/sendSMS + # 一信通 + yixintong: + sp-code: xxxxxx #(必填)企业编号 + access-key-id: xxxxxx #(必填)用户名 + access-key-secret: 324gaxxxxxxxxxxxxxxxxx9sdf89 #(必填)接口密钥(正式帐户需要登陆平台,接口业务-接口申请右侧钥匙状图标查看或获取,接口密钥获取后十分钟生效) + template-id: #(可选)模板编号(若配置此参数,则会默认使用该模板,以便提高服务方性能) + sign-code: #(可选)短信前置签名编号(登陆平台-接口业务-我的签名查看) + f: 1 #(可选)默认为1,提交时检测方式 sms-oa: config-type: yaml diff --git a/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/Sms4jTest.java b/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/Sms4jTest.java index a45a69c8..cca4a1f3 100644 --- a/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/Sms4jTest.java +++ b/sms4j-spring-boot-example/src/test/java/org/dromara/sms4j/example/Sms4jTest.java @@ -1,5 +1,6 @@ package org.dromara.sms4j.example; +import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.ListUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.UUID; @@ -526,4 +527,31 @@ public class Sms4jTest { Assert.isTrue(smsResponse5.isSuccess()); } + + /** + * 联通一信通模板 + */ + @Test + public void yixintongSmsTest() { + if (StrUtil.isBlank(PHONE)) { + return; + } + + //短信发送模板:你有一项编号为{xxxxxxxxx}的事务需要处理{x} + //其中的{xxxxxx}代表短信模板中的变量部分,可变化,一个x代表一个字或者字符,{}为变量标识,在发送时不用传。实发变量字数小于等于x的个数。 + + // 单发 + String message1 = StrUtil.format("你有一项编号为{}的事务需要处理。", SmsUtils.getRandomInt(6)); + SmsResponse smsResponse1 = SmsFactory.getBySupplier(SupplierConstant.YIXINTONG).sendMessage(PHONE, message1); + log.info(JSONUtil.toJsonStr(smsResponse1)); + Assert.isTrue(smsResponse1.isSuccess()); + + // 群发 + List phones = CollectionUtil.toList(PHONE); + String message2 = StrUtil.format("你有一项编号为{}的事务需要处理。", SmsUtils.getRandomInt(6)); + SmsResponse smsResponse2 = SmsFactory.getBySupplier(SupplierConstant.YIXINTONG).massTexting(phones, message2); + log.info(JSONUtil.toJsonStr(smsResponse2)); + Assert.isTrue(smsResponse2.isSuccess()); + } + } diff --git a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java index 53fe8f30..cb373ddf 100644 --- a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java +++ b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SmsBlendsInitializer.java @@ -42,6 +42,7 @@ import org.dromara.sms4j.starter.adepter.ConfigCombineMapAdaptor; import org.dromara.sms4j.submail.config.SubMailFactory; import org.dromara.sms4j.tencent.config.TencentFactory; import org.dromara.sms4j.unisms.config.UniFactory; +import org.dromara.sms4j.yixintong.config.YiXintongFactory; import org.dromara.sms4j.yunpian.config.YunPianFactory; import org.dromara.sms4j.zhutong.config.ZhutongFactory; import org.springframework.beans.factory.ObjectProvider; @@ -150,6 +151,7 @@ public class SmsBlendsInitializer { ProviderFactoryHolder.registerFactory(LuoSiMaoFactory.instance()); ProviderFactoryHolder.registerFactory(SubMailFactory.instance()); ProviderFactoryHolder.registerFactory(DanMiFactory.instance()); + ProviderFactoryHolder.registerFactory(YiXintongFactory.instance()); if (SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) { if (SmsUtils.isClassExists("com.jdcloud.sdk.auth.CredentialsProvider")) { ProviderFactoryHolder.registerFactory(JdCloudFactory.instance());