1、接入云片短信渠道

2、根据配置动态流量配置短信
3、多一个短信渠道增加容灾
pull/11/head
3y 3 years ago
parent ac0174a0e8
commit c1184ac9a4

@ -73,4 +73,11 @@ public class SendAccountConstant {
public static final String SMS_PREFIX = "sms_";
/**
* code
*/
public static final Integer TENCENT_SMS_CODE = 10;
public static final Integer YUN_PIAN_SMS_CODE = 20;
}

@ -0,0 +1,30 @@
package com.java3y.austin.handler.domain.sms;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*
* @author 3y
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MessageTypeSmsConfig {
/**
* ()
*/
private Integer weights;
/**
* script
*/
private String scriptName;
}

@ -28,10 +28,4 @@ public class SmsParam {
*
*/
private String content;
/**
*
*/
private Integer sendAccount;
}

@ -76,6 +76,6 @@ public class YunPianSendResult {
* sid
*/
@JSONField(name = "sid")
private Integer sid;
private String sid;
}
}

@ -4,14 +4,11 @@ import com.alibaba.fastjson.JSON;
import com.google.common.base.Throwables;
import com.java3y.austin.common.domain.TaskInfo;
import com.java3y.austin.common.dto.model.MiniProgramContentModel;
import com.java3y.austin.common.dto.model.OfficialAccountsContentModel;
import com.java3y.austin.common.enums.ChannelType;
import com.java3y.austin.handler.domain.wechat.WeChatMiniProgramParam;
import com.java3y.austin.handler.domain.wechat.WeChatOfficialParam;
import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.handler.script.MiniProgramAccountService;
import com.java3y.austin.handler.script.OfficialAccountService;
import com.java3y.austin.handler.wechat.MiniProgramAccountService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@ -8,7 +8,7 @@ import com.java3y.austin.common.enums.ChannelType;
import com.java3y.austin.handler.domain.wechat.WeChatOfficialParam;
import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.handler.script.OfficialAccountService;
import com.java3y.austin.handler.wechat.OfficialAccountService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@ -3,14 +3,19 @@ package com.java3y.austin.handler.handler.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfig;
import com.google.common.base.Throwables;
import com.java3y.austin.common.constant.AustinConstant;
import com.java3y.austin.common.domain.TaskInfo;
import com.java3y.austin.common.dto.model.SmsContentModel;
import com.java3y.austin.common.enums.ChannelType;
import com.java3y.austin.handler.domain.sms.MessageTypeSmsConfig;
import com.java3y.austin.handler.domain.sms.SmsParam;
import com.java3y.austin.handler.handler.BaseHandler;
import com.java3y.austin.handler.handler.Handler;
import com.java3y.austin.handler.script.SmsScript;
import com.java3y.austin.handler.script.SmsScriptHolder;
import com.java3y.austin.support.dao.SmsRecordDao;
import com.java3y.austin.support.domain.SmsRecord;
import lombok.extern.slf4j.Slf4j;
@ -18,6 +23,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Random;
/**
*
@ -36,7 +42,10 @@ public class SmsHandler extends BaseHandler implements Handler {
private SmsRecordDao smsRecordDao;
@Autowired
private SmsScript smsScript;
private SmsScriptHolder smsScriptHolder;
@ApolloConfig("boss.austin")
private Config config;
@Override
@ -45,14 +54,20 @@ public class SmsHandler extends BaseHandler implements Handler {
.phones(taskInfo.getReceiver())
.content(getSmsContent(taskInfo))
.messageTemplateId(taskInfo.getMessageTemplateId())
.sendAccount(taskInfo.getSendAccount())
.build();
try {
List<SmsRecord> recordList = smsScript.send(smsParam);
if (!CollUtil.isEmpty(recordList)) {
smsRecordDao.saveAll(recordList);
/**
* 1
* 2
*/
MessageTypeSmsConfig[] messageTypeSmsConfigs = loadBalance(getMessageTypeSmsConfig(taskInfo.getMsgType()));
for (MessageTypeSmsConfig messageTypeSmsConfig : messageTypeSmsConfigs) {
List<SmsRecord> recordList = smsScriptHolder.route(messageTypeSmsConfig.getScriptName()).send(smsParam);
if (CollUtil.isNotEmpty(recordList)) {
smsRecordDao.saveAll(recordList);
return true;
}
}
return true;
} catch (Exception e) {
log.error("SmsHandler#handler fail:{},params:{}",
Throwables.getStackTraceAsString(e), JSON.toJSONString(smsParam));
@ -60,6 +75,71 @@ public class SmsHandler extends BaseHandler implements Handler {
return false;
}
/**
*
*
*
* @param messageTypeSmsConfigs
*/
private MessageTypeSmsConfig[] loadBalance(List<MessageTypeSmsConfig> messageTypeSmsConfigs) {
int total = 0;
for (MessageTypeSmsConfig channelConfig : messageTypeSmsConfigs) {
total += channelConfig.getWeights();
}
// 生成一个随机数[1,total],看落到哪个区间
Random random = new Random();
int index = random.nextInt(total) + 1;
MessageTypeSmsConfig supplier = null;
MessageTypeSmsConfig supplierBack = null;
for (int i = 0; i < messageTypeSmsConfigs.size(); ++i) {
if (index <= messageTypeSmsConfigs.get(i).getWeights()) {
supplier = messageTypeSmsConfigs.get(i);
int j = (i + 1) % messageTypeSmsConfigs.size(); // 取下一个供应商
if (i == j) {
return new MessageTypeSmsConfig[]{supplier};
}
supplierBack = messageTypeSmsConfigs.get(j);
return new MessageTypeSmsConfig[]{supplier, supplierBack};
}
index -= messageTypeSmsConfigs.get(i).getWeights();
}
return null;
}
/**
* ()
* <p>
*
* keymsg_type_sms_config
* value[{"message_type_10":[{"weights":80,"scriptName":"TencentSmsScript"},{"weights":20,"scriptName":"YunPianSmsScript"}]},{"message_type_20":[{"weights":20,"scriptName":"YunPianSmsScript"}]},{"message_type_30":[{"weights":20,"scriptName":"TencentSmsScript"}]},{"message_type_40":[{"weights":20,"scriptName":"TencentSmsScript"}]}]
* TencentSmsScript 80%YunPianSmsScript20%
* YunPianSmsScript
* TencentSmsScript
*
* @param msgType
* @return
*/
private List<MessageTypeSmsConfig> getMessageTypeSmsConfig(Integer msgType) {
String apolloKey = "msg_type_sms_config";
String messagePrefix = "message_type_";
String property = config.getProperty(apolloKey, AustinConstant.APOLLO_DEFAULT_VALUE_JSON_ARRAY);
JSONArray jsonArray = JSON.parseArray(property);
for (int i = 0; i < jsonArray.size(); i++) {
JSONArray array = jsonArray.getJSONObject(i).getJSONArray(messagePrefix + msgType);
if (CollUtil.isNotEmpty(array)) {
List<MessageTypeSmsConfig> result = JSON.parseArray(JSON.toJSONString(array), MessageTypeSmsConfig.class);
return result;
}
}
return null;
}
/**
*
* <p>

@ -0,0 +1,42 @@
package com.java3y.austin.handler.script;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
/**
* sms
*
* @author 3y
*/
@Slf4j
public abstract class BaseSmsScript implements SmsScript {
@Autowired
private SmsScriptHolder smsScriptHolder;
@PostConstruct
public void registerProcessScript() {
if (ArrayUtils.isEmpty(this.getClass().getAnnotations())) {
log.error("BaseSmsScript can not find annotation!");
return;
}
Annotation handlerAnnotations = null;
for (Annotation annotation : this.getClass().getAnnotations()) {
if (annotation instanceof SmsScriptHandler) {
handlerAnnotations = annotation;
break;
}
}
if (handlerAnnotations == null) {
log.error("handler annotations not declared");
return;
}
//注册handler
smsScriptHolder.putHandler(((SmsScriptHandler) handlerAnnotations).value(), this);
}
}

@ -17,8 +17,8 @@ public interface SmsScript {
*
* @param smsParam
* @return
* @throws Exception
*/
List<SmsRecord> send(SmsParam smsParam) throws Exception;
List<SmsRecord> send(SmsParam smsParam);
}

@ -0,0 +1,27 @@
package com.java3y.austin.handler.script;
import org.springframework.stereotype.Component;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
*
* @author 3y
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Component
public @interface SmsScriptHandler {
/**
*
*
* @return
*/
String value();
}

@ -0,0 +1,25 @@
package com.java3y.austin.handler.script;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* sendAccount->SmsScript
*
* @author 3y
*/
@Component
public class SmsScriptHolder {
private Map<String, SmsScript> handlers = new HashMap<>(8);
public void putHandler(String scriptName, SmsScript handler) {
handlers.put(scriptName, handler);
}
public SmsScript route(String scriptName) {
return handlers.get(scriptName);
}
}

@ -4,11 +4,15 @@ import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Throwables;
import com.java3y.austin.common.constant.SendAccountConstant;
import com.java3y.austin.common.dto.account.TencentSmsAccount;
import com.java3y.austin.common.enums.SmsStatus;
import com.java3y.austin.handler.domain.sms.SmsParam;
import com.java3y.austin.common.dto.account.TencentSmsAccount;
import com.java3y.austin.handler.script.BaseSmsScript;
import com.java3y.austin.handler.script.SmsScript;
import com.java3y.austin.handler.script.SmsScriptHandler;
import com.java3y.austin.support.domain.SmsRecord;
import com.java3y.austin.support.utils.AccountUtils;
import com.tencentcloudapi.common.Credential;
@ -20,7 +24,6 @@ import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import com.tencentcloudapi.sms.v20210111.models.SendStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
@ -33,9 +36,10 @@ import java.util.List;
* 2. 使SDK
* 3. 使API Explorer
*/
@Service
@Slf4j
public class TencentSmsScript implements SmsScript {
@SmsScriptHandler("TencentSmsScript")
public class TencentSmsScript extends BaseSmsScript implements SmsScript {
private static final Integer PHONE_NUM = 11;
@ -43,12 +47,17 @@ public class TencentSmsScript implements SmsScript {
private AccountUtils accountUtils;
@Override
public List<SmsRecord> send(SmsParam smsParam) throws Exception {
TencentSmsAccount tencentSmsAccount = accountUtils.getAccount(smsParam.getSendAccount(), SendAccountConstant.SMS_ACCOUNT_KEY, SendAccountConstant.SMS_PREFIX, TencentSmsAccount.class);
SmsClient client = init(tencentSmsAccount);
SendSmsRequest request = assembleReq(smsParam, tencentSmsAccount);
SendSmsResponse response = client.SendSms(request);
return assembleSmsRecord(smsParam, response, tencentSmsAccount);
public List<SmsRecord> send(SmsParam smsParam) {
try {
TencentSmsAccount tencentSmsAccount = accountUtils.getAccount(SendAccountConstant.TENCENT_SMS_CODE, SendAccountConstant.SMS_ACCOUNT_KEY, SendAccountConstant.SMS_PREFIX, TencentSmsAccount.class);
SmsClient client = init(tencentSmsAccount);
SendSmsRequest request = assembleReq(smsParam, tencentSmsAccount);
SendSmsResponse response = client.SendSms(request);
return assembleSmsRecord(smsParam, response, tencentSmsAccount);
} catch (Exception e) {
log.error("TencentSmsScript#send fail:{},params:{}", Throwables.getStackTraceAsString(e), JSON.toJSONString(smsParam));
return null;
}
}

@ -3,34 +3,24 @@ package com.java3y.austin.handler.script.impl;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.base.Throwables;
import com.java3y.austin.common.constant.SendAccountConstant;
import com.java3y.austin.common.dto.account.TencentSmsAccount;
import com.java3y.austin.common.dto.account.YunPianSmsAccount;
import com.java3y.austin.common.enums.SmsStatus;
import com.java3y.austin.handler.domain.sms.SmsParam;
import com.java3y.austin.handler.domain.sms.YunPianSendResult;
import com.java3y.austin.handler.script.BaseSmsScript;
import com.java3y.austin.handler.script.SmsScript;
import com.java3y.austin.handler.script.SmsScriptHandler;
import com.java3y.austin.support.domain.SmsRecord;
import com.java3y.austin.support.utils.AccountUtils;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.SendSmsRequest;
import com.tencentcloudapi.sms.v20210111.models.SendSmsResponse;
import com.tencentcloudapi.sms.v20210111.models.SendStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
@ -39,38 +29,43 @@ import java.util.*;
* @date 2022523
* https://www.yunpian.com/official/document/sms/zh_CN/domestic_list
*/
//@Service
@Slf4j
public class YunPianSmsScript implements SmsScript {
@SmsScriptHandler("YunPianSmsScript")
public class YunPianSmsScript extends BaseSmsScript implements SmsScript {
@Autowired
private AccountUtils accountUtils;
@Override
public List<SmsRecord> send(SmsParam smsParam) throws Exception {
YunPianSmsAccount account = accountUtils.getAccount(smsParam.getSendAccount(), SendAccountConstant.SMS_ACCOUNT_KEY, SendAccountConstant.SMS_PREFIX, YunPianSmsAccount.class);
Map<String, String> params = assembleParam(smsParam, account);
String result = HttpRequest.post(account.getUrl())
.header(Header.CONTENT_TYPE.getValue(), ContentType.FORM_URLENCODED.getValue())
.header(Header.ACCEPT.getValue(), ContentType.JSON.getValue())
.body(JSON.toJSONString(params))
.timeout(2000)
.execute().body();
YunPianSendResult yunPianSendResult = JSON.parseObject(result, YunPianSendResult.class);
public List<SmsRecord> send(SmsParam smsParam) {
try {
YunPianSmsAccount account = accountUtils.getAccount(SendAccountConstant.YUN_PIAN_SMS_CODE, SendAccountConstant.SMS_ACCOUNT_KEY, SendAccountConstant.SMS_PREFIX, YunPianSmsAccount.class);
Map<String, Object> params = assembleParam(smsParam, account);
String result = HttpRequest.post(account.getUrl())
.header(Header.CONTENT_TYPE.getValue(), "application/x-www-form-urlencoded;charset=utf-8;")
.header(Header.ACCEPT.getValue(), "application/json;charset=utf-8;")
.form(params)
.timeout(2000)
.execute().body();
YunPianSendResult yunPianSendResult = JSON.parseObject(result, YunPianSendResult.class);
return assembleSmsRecord(smsParam, yunPianSendResult, account);
} catch (Exception e) {
log.error("YunPianSmsScript#send fail:{},params:{}", Throwables.getStackTraceAsString(e), JSON.toJSONString(smsParam));
return null;
}
return assembleSmsRecord(smsParam, yunPianSendResult, account);
}
/**
*
*
* @param smsParam
* @param account
* @return
*/
private Map<String, String> assembleParam(SmsParam smsParam, YunPianSmsAccount account) {
Map<String, String> params = new HashMap<>();
private Map<String, Object> assembleParam(SmsParam smsParam, YunPianSmsAccount account) {
Map<String, Object> params = new HashMap<>();
params.put("apikey", account.getApikey());
params.put("mobile", StringUtils.join(smsParam.getPhones(), StrUtil.C_COMMA));
params.put("tpl_id", account.getTplId());
@ -94,9 +89,9 @@ public class YunPianSmsScript implements SmsScript {
.supplierId(account.getSupplierId())
.supplierName(account.getSupplierName())
.msgContent(smsParam.getContent())
.seriesId(String.valueOf(datum.getSid()))
.seriesId(datum.getSid())
.chargingNum(Math.toIntExact(datum.getCount()))
.status("0".equals(datum.getCode())?SmsStatus.SEND_SUCCESS.getCode():SmsStatus.SEND_FAIL.getCode())
.status("0".equals(datum.getCode()) ? SmsStatus.SEND_SUCCESS.getCode() : SmsStatus.SEND_FAIL.getCode())
.reportContent(datum.getMsg())
.created(Math.toIntExact(DateUtil.currentSeconds()))
.updated(Math.toIntExact(DateUtil.currentSeconds()))
@ -109,6 +104,5 @@ public class YunPianSmsScript implements SmsScript {
}
}

@ -1,4 +1,4 @@
package com.java3y.austin.handler.script;
package com.java3y.austin.handler.wechat;
import com.java3y.austin.handler.domain.wechat.WeChatMiniProgramParam;

@ -1,4 +1,4 @@
package com.java3y.austin.handler.script;
package com.java3y.austin.handler.wechat;
import com.java3y.austin.handler.domain.wechat.WeChatOfficialParam;
import me.chanjar.weixin.mp.bean.template.WxMpTemplateMessage;

@ -1,4 +1,4 @@
package com.java3y.austin.handler.script.impl;
package com.java3y.austin.handler.wechat.impl;
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaSubscribeService;
@ -9,7 +9,7 @@ import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import com.java3y.austin.common.constant.SendAccountConstant;
import com.java3y.austin.common.dto.account.WeChatMiniProgramAccount;
import com.java3y.austin.handler.domain.wechat.WeChatMiniProgramParam;
import com.java3y.austin.handler.script.MiniProgramAccountService;
import com.java3y.austin.handler.wechat.MiniProgramAccountService;
import com.java3y.austin.support.utils.AccountUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

@ -1,9 +1,9 @@
package com.java3y.austin.handler.script.impl;
package com.java3y.austin.handler.wechat.impl;
import com.java3y.austin.common.constant.SendAccountConstant;
import com.java3y.austin.common.dto.account.WeChatOfficialAccount;
import com.java3y.austin.handler.domain.wechat.WeChatOfficialParam;
import com.java3y.austin.handler.script.OfficialAccountService;
import com.java3y.austin.handler.wechat.OfficialAccountService;
import com.java3y.austin.support.utils.AccountUtils;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpService;

@ -21,7 +21,7 @@ public class AccountUtils {
private Config config;
/**
* (key:smsAccount)[{"sms_10":{"url":"sms.tencentcloudapi.com","region":"ap-guangzhou","secretId":"AKIDhDUUDfffffMEqBF1WljQq","secretKey":"B4h39yWnfffff7D2btue7JErDJ8gxyi","smsSdkAppId":"140025","templateId":"11897","signName":"Java3y公众号","supplierId":10,"supplierName":"腾讯云"}}]
* (key:smsAccount)[{"sms_10":{"url":"sms.tencentcloudapi.com","region":"ap-guangzhou","secretId":"AKIDhDUUDfrmQnvmMfhSw95lCMEqBF1WljQq","secretKey":"B4h39yWnnX0C6k7D2btue7JErDJ8gxyi","smsSdkAppId":"1400592125","templateId":"1182097","signName":"Java3y公众号","supplierId":10,"supplierName":"腾讯云"}},{"sms_20":{"url":"https://sms.yunpian.com/v2/sms/tpl_batch_send.json","apikey":"ca55d4c856d72e5d589361c4511b5cd7","tpl_id":"5236082","supplierId":20,"supplierName":"云片"}}]
* (key:emailAccount)[{"email_10":{"host":"smtp.qq.com","port":465,"user":"403686131@qq.com","pass":"","from":"403686131@qq.com"}}]
* (key:enterpriseWechatAccount)[{"enterprise_wechat_10":{"corpId":"wwf87603333e00069c","corpSecret":"-IFWxS2222QxzPIorNVUQn144444D915DM","agentId":10044442,"token":"rXROB3333Kf6i","aesKey":"MKZtoFxHIM44444M7ieag3r9ZPUsl"}}]
* (key:dingDingRobotAccount) [{"ding_ding_robot_10":{"secret":"SEC996d8d9d4768aded74114faae924f229229de444475a1c295d64fedf","webhook":"https://oapi.dingtalk.com/robot/send?access_token=8d03b644ffb6534b203d87333367328b0c3003d164715d2c6c6e56"}}]
@ -42,4 +42,7 @@ public class AccountUtils {
return null;
}
}

Loading…
Cancel
Save