diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/SupplierSqlConfig.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/SupplierSqlConfig.java
index 48c9bf92..b7c43438 100644
--- a/sms4j-core/src/main/java/org/dromara/sms4j/core/SupplierSqlConfig.java
+++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/SupplierSqlConfig.java
@@ -9,6 +9,7 @@ import org.dromara.sms4j.ctyun.config.CtyunConfig;
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.netease.config.NeteaseConfig;
import org.dromara.sms4j.provider.enumerate.SupplierType;
import org.dromara.sms4j.tencent.config.TencentConfig;
import org.dromara.sms4j.unisms.config.UniConfig;
@@ -29,7 +30,7 @@ public class SupplierSqlConfig {
* readSqlConfig
*
读取数据库配置信息
* @author :Wind
- */
+ */
public static void readSqlConfig(){
select = JDBCTool.selectConfig();
}
@@ -38,7 +39,7 @@ public class SupplierSqlConfig {
* refreshSqlConfig
*
读取并刷新数据库配置
* @author :Wind
- */
+ */
public static void refreshSqlConfig(){
readSqlConfig();
alibaba();
@@ -50,6 +51,7 @@ public class SupplierSqlConfig {
cloopen();
emay();
ctyun();
+ netease();
}
public SupplierSqlConfig() {
@@ -64,17 +66,17 @@ public class SupplierSqlConfig {
* alibaba
*
数据库读取并设置阿里云短信
* @author :Wind
- */
+ */
public static void alibaba(){
AlibabaConfig alibabaConfig = SmsUtil.jsonForObject(select.get(SupplierType.ALIBABA.getName()), AlibabaConfig.class);
- SupplierFactory.setAlibabaConfig(alibabaConfig);
+ SupplierFactory.setAlibabaConfig(alibabaConfig);
}
/**
* huawei
*
数据库读取并设置华为短信
* @author :Wind
- */
+ */
public static void huawei(){
HuaweiConfig huaweiConfig = SmsUtil.jsonForObject(select.get(SupplierType.HUAWEI.getName()), HuaweiConfig.class);
SupplierFactory.setHuaweiConfig(huaweiConfig);
@@ -84,7 +86,7 @@ public class SupplierSqlConfig {
* jingdong
*
数据库读取并设置京东短信
* @author :Wind
- */
+ */
public static void jingdong(){
JdCloudConfig jdCloudConfig = SmsUtil.jsonForObject(select.get(SupplierType.JD_CLOUD.getName()), JdCloudConfig.class);
SupplierFactory.setJdCloudConfig(jdCloudConfig);
@@ -94,7 +96,7 @@ public class SupplierSqlConfig {
* tencent
*
数据库读取并设置腾讯短信
* @author :Wind
- */
+ */
public static void tencent(){
TencentConfig tencentConfig = SmsUtil.jsonForObject(select.get(SupplierType.TENCENT.getName()), TencentConfig.class);
SupplierFactory.setTencentConfig(tencentConfig);
@@ -104,7 +106,7 @@ public class SupplierSqlConfig {
* uniSms
*
数据库读取并设置合一短信配置
* @author :Wind
- */
+ */
public static void uniSms(){
UniConfig uniConfig = SmsUtil.jsonForObject(select.get(SupplierType.UNI_SMS.getName()), UniConfig.class);
SupplierFactory.setUniConfig(uniConfig);
@@ -114,7 +116,7 @@ public class SupplierSqlConfig {
* yunPian
*
数据库读取并设置云片短信
* @author :Wind
- */
+ */
public static void yunPian(){
YunpianConfig yunpianConfig = SmsUtil.jsonForObject(select.get(SupplierType.YUNPIAN.getName()), YunpianConfig.class);
SupplierFactory.setYunpianConfig(yunpianConfig);
@@ -124,7 +126,7 @@ public class SupplierSqlConfig {
* cloopen
*
数据库读取并设置容联云短信
* @author :Wind
- */
+ */
public static void cloopen(){
CloopenConfig cloopenConfig = SmsUtil.jsonForObject(select.get(SupplierType.CLOOPEN.getName()), CloopenConfig.class);
SupplierFactory.setCloopenConfig(cloopenConfig);
@@ -143,9 +145,19 @@ public class SupplierSqlConfig {
* ctyun
*
数据库读取并设置天翼云短信
* @author :Wind
- */
+ */
public static void ctyun(){
CtyunConfig ctyunConfig = SmsUtil.jsonForObject(select.get(SupplierType.CTYUN.getName()), CtyunConfig.class);
SupplierFactory.setCtyunConfig(ctyunConfig);
}
+
+ /**
+ * netease
+ *
数据库读取并设置网易云短信
+ * @author : Adam
+ */
+ public static void netease(){
+ NeteaseConfig neteaseConfig = SmsUtil.jsonForObject(select.get(SupplierType.NETEASE.getName()), NeteaseConfig.class);
+ SupplierFactory.setNeteaseConfig(neteaseConfig);
+ }
}
diff --git a/sms4j-core/src/main/java/org/dromara/sms4j/core/config/SupplierFactory.java b/sms4j-core/src/main/java/org/dromara/sms4j/core/config/SupplierFactory.java
index d9765bf2..f8c75f3d 100644
--- a/sms4j-core/src/main/java/org/dromara/sms4j/core/config/SupplierFactory.java
+++ b/sms4j-core/src/main/java/org/dromara/sms4j/core/config/SupplierFactory.java
@@ -15,6 +15,8 @@ import org.dromara.sms4j.huawei.config.HuaweiConfig;
import org.dromara.sms4j.huawei.config.HuaweiFactory;
import org.dromara.sms4j.jdcloud.config.JdCloudConfig;
import org.dromara.sms4j.jdcloud.config.JdCloudFactory;
+import org.dromara.sms4j.netease.config.NeteaseConfig;
+import org.dromara.sms4j.netease.config.NeteaseFactory;
import org.dromara.sms4j.provider.enumerate.SupplierType;
import org.dromara.sms4j.tencent.config.TencentConfig;
import org.dromara.sms4j.tencent.config.TencentFactory;
@@ -96,12 +98,19 @@ public class SupplierFactory {
return CtyunFactory.instance().getConfig();
}
+ /**
+ * 网易云信配置获取
+ */
+ public static NeteaseConfig getNeteaseConfig() {
+ return NeteaseFactory.instance().getConfig();
+ }
+
/**
* setSupplierConfig
*
通用化set,用于设置
* @param t 配置对象
* @author :Wind
- */
+ */
public static void setSupplierConfig(T t) {
if (t instanceof AlibabaConfig) {
setAlibabaConfig((AlibabaConfig) t);
@@ -121,7 +130,9 @@ public class SupplierFactory {
setEmayConfig((EmayConfig) t);
} else if (t instanceof CtyunConfig) {
setCtyunConfig((CtyunConfig) t);
- }else {
+ } else if (t instanceof NeteaseConfig) {
+ setNeteaseConfig((NeteaseConfig) t);
+ } else {
throw new SmsBlendException("Loading failure! Please check the configuration type.");
}
}
@@ -197,4 +208,12 @@ public class SupplierFactory {
CtyunFactory.instance().setConfig(ctyunConfig);
SmsFactory.refresh(SupplierType.CTYUN);
}
+
+ /**
+ * 设置 neteaseConfig
+ */
+ public static void setNeteaseConfig(NeteaseConfig neteaseConfig) {
+ NeteaseFactory.instance().setConfig(neteaseConfig);
+ SmsFactory.refresh(SupplierType.NETEASE);
+ }
}
diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/netease/config/NeteaseConfig.java b/sms4j-provider/src/main/java/org/dromara/sms4j/netease/config/NeteaseConfig.java
new file mode 100644
index 00000000..a14182db
--- /dev/null
+++ b/sms4j-provider/src/main/java/org/dromara/sms4j/netease/config/NeteaseConfig.java
@@ -0,0 +1,56 @@
+package org.dromara.sms4j.netease.config;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.experimental.SuperBuilder;
+import org.dromara.sms4j.api.universal.SupplierConfig;
+import org.dromara.sms4j.comm.config.BaseConfig;
+
+/**
+ * @author adam
+ */
+@Data
+@SuperBuilder
+public class NeteaseConfig extends BaseConfig implements SupplierConfig {
+
+ /**
+ * 模板变量名称
+ */
+ private String templateId;
+
+ /**
+ * appKey
+ */
+ private String appKey;
+
+ /**
+ * secret
+ */
+ private String secret;
+
+ /**
+ * 模板短信请求地址
+ */
+ @Builder.Default
+ private String templateUrl = "https://api.netease.im/sms/sendtemplate.action";
+
+
+ /**
+ * 验证码短信请求地址
+ */
+ @Builder.Default
+ private String codeUrl = "https://api.netease.im/sms/sendcode.action";
+
+ /**
+ * 验证码验证请求地址
+ */
+ @Builder.Default
+ private String verifyUrl = "https://api.netease.im/sms/verifycode.action";
+
+ /**
+ * 是否需要支持短信上行。true:需要,false:不需要
+ * 说明:如果开通了短信上行抄送功能,该参数需要设置为true,其它情况设置无效
+ */
+ private final Boolean needUp;
+
+}
diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/netease/config/NeteaseFactory.java b/sms4j-provider/src/main/java/org/dromara/sms4j/netease/config/NeteaseFactory.java
new file mode 100644
index 00000000..36feb4ea
--- /dev/null
+++ b/sms4j-provider/src/main/java/org/dromara/sms4j/netease/config/NeteaseFactory.java
@@ -0,0 +1,81 @@
+package org.dromara.sms4j.netease.config;
+
+import org.dromara.sms4j.comm.factory.BeanFactory;
+import org.dromara.sms4j.netease.service.NeteaseSmsImpl;
+import org.dromara.sms4j.provider.base.BaseProviderFactory;
+
+/**
+ * NeteaseSmsConfig
+ * 网易云信短信
+ *
+ * @author :adam
+ * 2023-05-30
+ **/
+public class NeteaseFactory implements BaseProviderFactory {
+
+ private static NeteaseSmsImpl neteaseSms;
+
+ private static final NeteaseFactory INSTANCE = new NeteaseFactory();
+
+ private static final class ConfigHolder {
+ private static NeteaseConfig config = NeteaseConfig.builder().build();
+ }
+
+ private NeteaseFactory() {
+ }
+
+ /**
+ * 获取建造者实例
+ * @return 建造者实例
+ */
+ public static NeteaseFactory instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * 建造一个网易云的短信实现
+ */
+ @Override
+ public NeteaseSmsImpl createSms(NeteaseConfig neteaseConfig) {
+ if (neteaseSms == null) {
+ neteaseSms = new NeteaseSmsImpl(
+ neteaseConfig,
+ BeanFactory.getExecutor(),
+ BeanFactory.getDelayedTime()
+ );
+ }
+ return neteaseSms;
+ }
+
+ /**
+ * 刷新对象
+ */
+ @Override
+ public NeteaseSmsImpl refresh(NeteaseConfig neteaseConfig) {
+ neteaseSms = new NeteaseSmsImpl(
+ neteaseConfig,
+ BeanFactory.getExecutor(),
+ BeanFactory.getDelayedTime()
+ );
+ return neteaseSms;
+ }
+
+ /**
+ * 获取配置
+ * @return 配置对象
+ */
+ @Override
+ public NeteaseConfig getConfig() {
+ return ConfigHolder.config;
+ }
+
+ /**
+ * 设置配置
+ * @param config 配置对象
+ */
+ @Override
+ public void setConfig(NeteaseConfig config) {
+ ConfigHolder.config = config;
+ }
+
+}
diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/netease/service/NeteaseSmsImpl.java b/sms4j-provider/src/main/java/org/dromara/sms4j/netease/service/NeteaseSmsImpl.java
new file mode 100644
index 00000000..f12baa1e
--- /dev/null
+++ b/sms4j-provider/src/main/java/org/dromara/sms4j/netease/service/NeteaseSmsImpl.java
@@ -0,0 +1,134 @@
+package org.dromara.sms4j.netease.service;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.IdUtil;
+import com.alibaba.fastjson.JSON;
+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.delayedTime.DelayedTime;
+import org.dromara.sms4j.comm.exception.SmsBlendException;
+import org.dromara.sms4j.netease.config.NeteaseConfig;
+import org.dromara.sms4j.netease.utils.NeteaseUtils;
+
+import java.util.*;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Created with IntelliJ IDEA.
+ *
+ * @author adam
+ * @create 2023/5/29 17:34
+ */
+
+@Slf4j
+public class NeteaseSmsImpl extends AbstractSmsBlend {
+
+ private NeteaseConfig config;
+
+ public NeteaseSmsImpl(NeteaseConfig config, Executor pool, DelayedTime delayed) {
+ super(pool, delayed);
+ this.config = config;
+ }
+
+ /**
+ *
+ * @param phone 接收短信的手机号
+ * @param message 短信变量 ["xxx","yyy"]
+ * @return
+ */
+ @Override
+ @Restricted
+ public SmsResponse sendMessage(String phone, String message) {
+ Optional.ofNullable(phone).orElseThrow(() -> new SmsBlendException("手机号不能为空"));
+ Optional.ofNullable(config.getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
+ return getSmsResponse(config.getTemplateUrl(), Collections.singletonList(phone), message, config.getTemplateId());
+ }
+
+
+ /**
+ *
+ * @param phone
+ * @param templateId 模板id
+ * @param messages 短信变量 key为默认 params value为模板变量值 ["xxx","yyy"]
+ * @return
+ */
+ @Override
+ @Restricted
+ public SmsResponse sendMessage(String phone, String templateId, LinkedHashMap messages) {
+ Optional.ofNullable(phone).orElseThrow(() -> new SmsBlendException("手机号不能为空"));
+ Optional.ofNullable(config.getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
+ String messageStr = messages.get("params");
+ return getSmsResponse(config.getTemplateUrl(), Collections.singletonList(phone), messageStr, templateId);
+ }
+
+ @Override
+ @Restricted
+ public SmsResponse massTexting(List phones, String message) {
+ if (phones.size() < 1) {
+ throw new SmsBlendException("手机号不能为空");
+ }
+ if (phones.size() > 100) {
+ throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于100");
+ }
+ Optional.ofNullable(config.getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
+ return getSmsResponse(config.getTemplateUrl(), phones, config.getTemplateId(), message);
+ }
+
+ @Override
+ @Restricted
+ public SmsResponse massTexting(List phones, String templateId, LinkedHashMap messages) {
+ if (phones.size() > 100) {
+ throw new SmsBlendException("单次发送超过最大发送上限,建议每次群发短信人数低于100");
+ }
+ Optional.ofNullable(config.getTemplateId()).orElseThrow(() -> new SmsBlendException("模板ID不能为空"));
+ String messageStr = messages.get("message");
+ return getSmsResponse(config.getTemplateUrl(), phones, messageStr, templateId);
+ }
+
+ private SmsResponse getSmsResponse(String requestUrl, List phones, String message, String templateId) {
+ AtomicReference reference = new AtomicReference<>();
+ String nonce = IdUtil.fastSimpleUUID();
+ String curTime = String.valueOf(DateUtil.currentSeconds());
+ String checkSum = NeteaseUtils.getCheckSum(config.getSecret(), nonce, curTime);
+ Map body = new LinkedHashMap<>(4);
+ body.put("templateid", templateId);
+ body.put("mobiles", JSONArray.parseArray(JSON.toJSONString(phones)).toJSONString());
+ body.put("params", message);
+ body.put("needUp", config.getNeedUp());
+ http.post(requestUrl)
+ .addHeader("Content-Type", "application/x-www-form-urlencoded")
+ .addHeader("AppKey", config.getAppKey())
+ .addHeader("Nonce", nonce)
+ .addHeader("CurTime", curTime)
+ .addHeader("CheckSum", checkSum)
+ .addBody(body)
+ .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();
+ return reference.get();
+ }
+
+ private SmsResponse getResponse(JSONObject jsonObject) {
+ SmsResponse response = new SmsResponse();
+ Integer code = jsonObject.getInteger("code");
+ if (code > 200) {
+ response.setErrorCode(String.valueOf(code));
+ response.setErrMessage(jsonObject.getString("msg"));
+ } else {
+ response.setCode(String.valueOf(code));
+ response.setMessage(jsonObject.getString("msg"));
+ response.setData(jsonObject.get("obj"));
+ }
+ return response;
+ }
+
+}
diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/netease/utils/NeteaseUtils.java b/sms4j-provider/src/main/java/org/dromara/sms4j/netease/utils/NeteaseUtils.java
new file mode 100644
index 00000000..7eadc895
--- /dev/null
+++ b/sms4j-provider/src/main/java/org/dromara/sms4j/netease/utils/NeteaseUtils.java
@@ -0,0 +1,51 @@
+package org.dromara.sms4j.netease.utils;
+
+import org.dromara.sms4j.comm.exception.SmsBlendException;
+
+import java.security.MessageDigest;
+
+/**
+ * Created with IntelliJ IDEA.
+ *
+ * @author adam
+ * @create 2023/5/29 17:49
+ */
+
+public class NeteaseUtils {
+
+ /**
+ * 计算并获取CheckSum
+ * @param appSecret
+ * @param nonce
+ * @param curTime
+ * @return
+ */
+ public static String getCheckSum(String appSecret, String nonce, String curTime) {
+ return encode("sha1", appSecret + nonce + curTime);
+ }
+
+ private static String encode(String algorithm, String value) {
+ if (value == null) {
+ return null;
+ }
+ try {
+ MessageDigest messageDigest
+ = MessageDigest.getInstance(algorithm);
+ messageDigest.update(value.getBytes());
+ return getFormattedText(messageDigest.digest());
+ } catch (Exception e) {
+ throw new SmsBlendException(e.getMessage());
+ }
+ }
+ private static String getFormattedText(byte[] bytes) {
+ int len = bytes.length;
+ StringBuilder buf = new StringBuilder(len * 2);
+ for (int j = 0; j < len; j++) {
+ buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]);
+ buf.append(HEX_DIGITS[bytes[j] & 0x0f]);
+ }
+ return buf.toString();
+ }
+ private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5',
+ '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
+}
diff --git a/sms4j-provider/src/main/java/org/dromara/sms4j/provider/enumerate/SupplierType.java b/sms4j-provider/src/main/java/org/dromara/sms4j/provider/enumerate/SupplierType.java
index 9725047c..3d2cbb6c 100644
--- a/sms4j-provider/src/main/java/org/dromara/sms4j/provider/enumerate/SupplierType.java
+++ b/sms4j-provider/src/main/java/org/dromara/sms4j/provider/enumerate/SupplierType.java
@@ -8,6 +8,7 @@ import org.dromara.sms4j.ctyun.config.CtyunFactory;
import org.dromara.sms4j.emay.config.EmayFactory;
import org.dromara.sms4j.huawei.config.HuaweiFactory;
import org.dromara.sms4j.jdcloud.config.JdCloudFactory;
+import org.dromara.sms4j.netease.config.NeteaseFactory;
import org.dromara.sms4j.provider.base.BaseProviderFactory;
import org.dromara.sms4j.tencent.config.TencentFactory;
import org.dromara.sms4j.unisms.config.UniFactory;
@@ -38,6 +39,8 @@ public enum SupplierType {
EMAY("亿美软通", EmayFactory.instance()),
/** 天翼云 */
CTYUN("天翼云短信", CtyunFactory.instance()),
+ /** 网易云信 */
+ NETEASE("网易云短信", NeteaseFactory.instance())
;
diff --git a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SupplierConfig.java b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SupplierConfig.java
index ab1c56dd..98144247 100644
--- a/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SupplierConfig.java
+++ b/sms4j-spring-boot-starter/src/main/java/org/dromara/sms4j/starter/config/SupplierConfig.java
@@ -7,6 +7,7 @@ import org.dromara.sms4j.ctyun.config.CtyunConfig;
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.netease.config.NeteaseConfig;
import org.dromara.sms4j.tencent.config.TencentConfig;
import org.dromara.sms4j.unisms.config.UniConfig;
import org.dromara.sms4j.yunpian.config.YunpianConfig;
@@ -82,4 +83,14 @@ public class SupplierConfig {
protected CtyunConfig ctyunConfig(){
return SupplierFactory.getCtyunConfig();
}
+
+
+ /**
+ * 网易云信差异化配置
+ */
+ @Bean
+ @ConfigurationProperties(prefix = "sms.netease")
+ protected NeteaseConfig neteaseConfig(){
+ return SupplierFactory.getNeteaseConfig();
+ }
}