1、限制 receiver 入参的大小

2、如果id类型是邮件,加入正则验证邮件合法性
3、加入 云片 短信渠道 做容灾和流量切换(未完成)
pull/11/head
3y 3 years ago
parent f16864096d
commit ac0174a0e8

@ -0,0 +1,45 @@
package com.java3y.austin.common.dto.account;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*
*
* [{"sms_20":{"url":"https://sms.yunpian.com/v2/sms/tpl_batch_send.json","apikey":"ca55d4c8544444444444622221b5cd7","tpl_id":"533332222282","supplierId":20,"supplierName":"云片"}}]
*
* @author 3y
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class YunPianSmsAccount {
/**
* apikey
*/
private String apikey;
/**
* tplId
*/
private String tplId;
/**
* api
*/
private String url;
/**
* Id
*/
private Integer supplierId;
/**
*
*/
private String supplierName;
}

@ -15,7 +15,8 @@ public enum SmsStatus {
SEND_SUCCESS(10,"调用渠道接口发送成功"),
RECEIVE_SUCCESS(20,"用户收到短信(收到渠道短信回执,状态成功)"),
RECEIVE_FAIL(30, "用户收不到短信(收到渠道短信回执,状态失败)");
RECEIVE_FAIL(30, "用户收不到短信(收到渠道短信回执,状态失败)"),
SEND_FAIL(40, "调用渠道接口发送失败");
private Integer code;
private String description;

@ -0,0 +1,81 @@
package com.java3y.austin.handler.domain.sms;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@NoArgsConstructor
@Data
public class YunPianSendResult {
/**
* totalCount
*/
@JSONField(name = "total_count")
private Integer totalCount;
/**
* totalFee
*/
@JSONField(name = "total_fee")
private String totalFee;
/**
* unit
*/
@JSONField(name = "unit")
private String unit;
/**
* data
*/
@JSONField(name = "data")
private List<DataDTO> data;
/**
* DataDTO
*/
@NoArgsConstructor
@Data
public static class DataDTO {
/**
* httpStatusCode
*/
@JSONField(name = "http_status_code")
private Integer httpStatusCode;
/**
* code
*/
@JSONField(name = "code")
private Integer code;
/**
* msg
*/
@JSONField(name = "msg")
private String msg;
/**
* count
*/
@JSONField(name = "count")
private Integer count;
/**
* fee
*/
@JSONField(name = "fee")
private Integer fee;
/**
* unit
*/
@JSONField(name = "unit")
private String unit;
/**
* mobile
*/
@JSONField(name = "mobile")
private String mobile;
/**
* sid
*/
@JSONField(name = "sid")
private Integer sid;
}
}

@ -0,0 +1,55 @@
package com.java3y.austin.handler.receipt;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
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.support.config.SupportThreadPoolConfig;
import com.java3y.austin.support.domain.SmsRecord;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.PullSmsSendStatus;
import com.tencentcloudapi.sms.v20210111.models.PullSmsSendStatusRequest;
import com.tencentcloudapi.sms.v20210111.models.PullSmsSendStatusResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
*
*
* @author 3y
*/
@Component
@Slf4j
public class SmsReceipt {
@Autowired
private TencentSmsReceipt tencentSmsReceipt;
@Autowired
private YunPianSmsReceipt yunPianSmsReceipt;
@PostConstruct
private void init() {
SupportThreadPoolConfig.getPendingSingleThreadPool().execute(() -> {
while (true) {
tencentSmsReceipt.pull();
yunPianSmsReceipt.pull();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
}
});
}
}

@ -13,13 +13,11 @@ import com.java3y.austin.support.dao.SmsRecordDao;
import com.java3y.austin.support.domain.SmsRecord;
import com.java3y.austin.support.utils.AccountUtils;
import com.tencentcloudapi.common.Credential;
import com.tencentcloudapi.common.exception.TencentCloudSDKException;
import com.tencentcloudapi.common.profile.ClientProfile;
import com.tencentcloudapi.common.profile.HttpProfile;
import com.tencentcloudapi.sms.v20210111.SmsClient;
import com.tencentcloudapi.sms.v20210111.models.*;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@ -30,7 +28,7 @@ import java.util.List;
/**
*
*
*
* @author 3y
*/
@ -45,60 +43,55 @@ public class TencentSmsReceipt {
@Autowired
private SmsRecordDao smsRecordDao;
@PostConstruct
private void init() {
/**
*
*/
public void pull() {
// 获取腾讯云账号信息
TencentSmsAccount account = accountUtils.getAccount(10, SendAccountConstant.SMS_ACCOUNT_KEY, SendAccountConstant.SMS_PREFIX, TencentSmsAccount.class);
SupportThreadPoolConfig.getPendingSingleThreadPool().execute(() -> {
while (true) {
try {
SmsClient client = getSmsClient(account);
// 每次拉取10条
PullSmsSendStatusRequest req = new PullSmsSendStatusRequest();
req.setLimit(10L);
req.setSmsSdkAppId(account.getSmsSdkAppId());
PullSmsSendStatusResponse resp = client.PullSmsSendStatus(req);
List<SmsRecord> smsRecordList = new ArrayList<>();
if (resp != null && resp.getPullSmsSendStatusSet() != null && resp.getPullSmsSendStatusSet().length > 0) {
log.debug("receipt sms:{}", JSON.toJSONString(resp.getPullSmsSendStatusSet()));
for (PullSmsSendStatus pullSmsSendStatus : resp.getPullSmsSendStatusSet()) {
SmsRecord smsRecord = SmsRecord.builder()
.sendDate(Integer.valueOf(DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN)))
.messageTemplateId(0L)
.phone(Long.valueOf(pullSmsSendStatus.getSubscriberNumber()))
.supplierId(account.getSupplierId())
.supplierName(account.getSupplierName())
.msgContent("")
.seriesId(pullSmsSendStatus.getSerialNo())
.chargingNum(0)
.status("SUCCESS".equals(pullSmsSendStatus.getReportStatus()) ? SmsStatus.RECEIVE_SUCCESS.getCode() : SmsStatus.RECEIVE_FAIL.getCode())
.reportContent(pullSmsSendStatus.getDescription())
.updated(Math.toIntExact(pullSmsSendStatus.getUserReceiveTime()))
.created(Math.toIntExact(DateUtil.currentSeconds()))
.build();
smsRecordList.add(smsRecord);
}
}
if (!CollUtil.isEmpty(smsRecordList)) {
smsRecordDao.saveAll(smsRecordList);
}
Thread.sleep(200);
} catch (Exception e) {
log.error("TencentSmsReceipt#init fail!{}", Throwables.getStackTraceAsString(e));
try {
SmsClient client = getSmsClient(account);
// 每次拉取10条
PullSmsSendStatusRequest req = new PullSmsSendStatusRequest();
req.setLimit(10L);
req.setSmsSdkAppId(account.getSmsSdkAppId());
PullSmsSendStatusResponse resp = client.PullSmsSendStatus(req);
List<SmsRecord> smsRecordList = new ArrayList<>();
if (resp != null && resp.getPullSmsSendStatusSet() != null && resp.getPullSmsSendStatusSet().length > 0) {
log.debug("receipt sms:{}", JSON.toJSONString(resp.getPullSmsSendStatusSet()));
for (PullSmsSendStatus pullSmsSendStatus : resp.getPullSmsSendStatusSet()) {
SmsRecord smsRecord = SmsRecord.builder()
.sendDate(Integer.valueOf(DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN)))
.messageTemplateId(0L)
.phone(Long.valueOf(pullSmsSendStatus.getSubscriberNumber()))
.supplierId(account.getSupplierId())
.supplierName(account.getSupplierName())
.msgContent("")
.seriesId(pullSmsSendStatus.getSerialNo())
.chargingNum(0)
.status("SUCCESS".equals(pullSmsSendStatus.getReportStatus()) ? SmsStatus.RECEIVE_SUCCESS.getCode() : SmsStatus.RECEIVE_FAIL.getCode())
.reportContent(pullSmsSendStatus.getDescription())
.updated(Math.toIntExact(pullSmsSendStatus.getUserReceiveTime()))
.created(Math.toIntExact(DateUtil.currentSeconds()))
.build();
smsRecordList.add(smsRecord);
}
}
});
if (!CollUtil.isEmpty(smsRecordList)) {
smsRecordDao.saveAll(smsRecordList);
}
} catch (Exception e) {
log.error("TencentSmsReceipt#init fail!{}", Throwables.getStackTraceAsString(e));
}
}
/**
* smsClient
*
* @param account
* @return
*/

@ -0,0 +1,25 @@
package com.java3y.austin.handler.receipt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
*
*
* @author 3y
*/
@Component
@Slf4j
public class YunPianSmsReceipt {
/**
*
*/
public void pull() {
}
}

@ -0,0 +1,114 @@
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.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.SmsScript;
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.*;
/**
* @author 3y
* @date 2022523
* https://www.yunpian.com/official/document/sms/zh_CN/domestic_list
*/
//@Service
@Slf4j
public class YunPianSmsScript 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);
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<>();
params.put("apikey", account.getApikey());
params.put("mobile", StringUtils.join(smsParam.getPhones(), StrUtil.C_COMMA));
params.put("tpl_id", account.getTplId());
params.put("tpl_value", "");
return params;
}
private List<SmsRecord> assembleSmsRecord(SmsParam smsParam, YunPianSendResult response, YunPianSmsAccount account) {
if (response == null || ArrayUtil.isEmpty(response.getData())) {
return null;
}
List<SmsRecord> smsRecordList = new ArrayList<>();
for (YunPianSendResult.DataDTO datum : response.getData()) {
SmsRecord smsRecord = SmsRecord.builder()
.sendDate(Integer.valueOf(DateUtil.format(new Date(), DatePattern.PURE_DATE_PATTERN)))
.messageTemplateId(smsParam.getMessageTemplateId())
.phone(Long.valueOf(datum.getMobile()))
.supplierId(account.getSupplierId())
.supplierName(account.getSupplierName())
.msgContent(smsParam.getContent())
.seriesId(String.valueOf(datum.getSid()))
.chargingNum(Math.toIntExact(datum.getCount()))
.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()))
.build();
smsRecordList.add(smsRecord);
}
return smsRecordList;
}
}

@ -15,6 +15,7 @@ import com.java3y.austin.support.pipeline.ProcessContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
@ -29,18 +30,23 @@ import java.util.stream.Collectors;
@Service
public class AfterParamCheckAction implements BusinessProcess<SendTaskModel> {
public static final String PHONE_REGEX_EXP = "^((13[0-9])|(14[5,7,9])|(15[0-3,5-9])|(166)|(17[0-9])|(18[0-9])|(19[1,8,9]))\\d{8}$";
public static final String EMAIL_REGEX_EXP = "[a-zA-Z0-9]+@[a-zA-Z0-9]+\\\\.[a-zA-Z0-9]+";
public static final HashMap<Integer, String> CHANNEL_REGEX_EXP = new HashMap<>();
static {
CHANNEL_REGEX_EXP.put(IdType.PHONE.getCode(), PHONE_REGEX_EXP);
CHANNEL_REGEX_EXP.put(IdType.EMAIL.getCode(), EMAIL_REGEX_EXP);
}
@Override
public void process(ProcessContext<SendTaskModel> context) {
SendTaskModel sendTaskModel = context.getProcessModel();
List<TaskInfo> taskInfo = sendTaskModel.getTaskInfo();
// 1. 过滤掉不合法的手机号
filterIllegalPhoneNum(taskInfo);
// 2.
// 1. 过滤掉不合法的手机号、邮件
filterIllegalReceiver(taskInfo);
if (CollUtil.isEmpty(taskInfo)) {
context.setNeedBreak(true).setResponse(BasicResultVO.fail(RespStatusEnum.CLIENT_BAD_PARAMETERS));
@ -48,31 +54,35 @@ public class AfterParamCheckAction implements BusinessProcess<SendTaskModel> {
}
/**
*
*
*
*
* @param taskInfo
*/
private void filterIllegalPhoneNum(List<TaskInfo> taskInfo) {
private void filterIllegalReceiver(List<TaskInfo> taskInfo) {
Integer idType = CollUtil.getFirst(taskInfo.iterator()).getIdType();
Integer sendChannel = CollUtil.getFirst(taskInfo.iterator()).getSendChannel();
if (IdType.PHONE.getCode().equals(idType) && ChannelType.SMS.getCode().equals(sendChannel)) {
Iterator<TaskInfo> iterator = taskInfo.iterator();
filter(taskInfo, CHANNEL_REGEX_EXP.get(idType));
}
// 利用正则找出不合法的手机号
while (iterator.hasNext()) {
TaskInfo task = iterator.next();
Set<String> illegalPhone = task.getReceiver().stream()
.filter(phone -> !ReUtil.isMatch(PHONE_REGEX_EXP, phone))
.collect(Collectors.toSet());
/**
*
*
* @param taskInfo
* @param regexExp
*/
private void filter(List<TaskInfo> taskInfo, String regexExp) {
Iterator<TaskInfo> iterator = taskInfo.iterator();
while (iterator.hasNext()) {
TaskInfo task = iterator.next();
Set<String> illegalPhone = task.getReceiver().stream()
.filter(phone -> !ReUtil.isMatch(regexExp, phone))
.collect(Collectors.toSet());
if (CollUtil.isNotEmpty(illegalPhone)) {
task.getReceiver().removeAll(illegalPhone);
log.error("messageTemplateId:{} find illegal phone!{}", task.getMessageTemplateId(), JSON.toJSONString(illegalPhone));
}
if (CollUtil.isEmpty(task.getReceiver())) {
iterator.remove();
}
if (CollUtil.isNotEmpty(illegalPhone)) {
task.getReceiver().removeAll(illegalPhone);
log.error("messageTemplateId:{} find illegal receiver!{}", task.getMessageTemplateId(), JSON.toJSONString(illegalPhone));
}
if (CollUtil.isEmpty(task.getReceiver())) {
iterator.remove();
}
}
}

@ -11,6 +11,7 @@ import com.java3y.austin.support.pipeline.ProcessContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@ -23,6 +24,11 @@ import java.util.stream.Collectors;
@Service
public class PreParamCheckAction implements BusinessProcess<SendTaskModel> {
/**
*
*/
private static final Integer BATCH_RECEIVER_SIZE = 100;
@Override
public void process(ProcessContext<SendTaskModel> context) {
SendTaskModel sendTaskModel = context.getProcessModel();
@ -30,13 +36,13 @@ public class PreParamCheckAction implements BusinessProcess<SendTaskModel> {
Long messageTemplateId = sendTaskModel.getMessageTemplateId();
List<MessageParam> messageParamList = sendTaskModel.getMessageParamList();
// 没有传入 消息模板Id 或者 messageParam
// 1.没有传入 消息模板Id 或者 messageParam
if (messageTemplateId == null || CollUtil.isEmpty(messageParamList)) {
context.setNeedBreak(true).setResponse(BasicResultVO.fail(RespStatusEnum.CLIENT_BAD_PARAMETERS));
return;
}
// 过滤 receiver=null 的messageParam
// 2.过滤 receiver=null 的messageParam
List<MessageParam> resultMessageParamList = messageParamList.stream()
.filter(messageParam -> !StrUtil.isBlank(messageParam.getReceiver()))
.collect(Collectors.toList());
@ -44,7 +50,13 @@ public class PreParamCheckAction implements BusinessProcess<SendTaskModel> {
context.setNeedBreak(true).setResponse(BasicResultVO.fail(RespStatusEnum.CLIENT_BAD_PARAMETERS));
return;
}
sendTaskModel.setMessageParamList(resultMessageParamList);
// 3.过滤receiver大于100的请求
if (messageParamList.stream().anyMatch(messageParam -> messageParam.getReceiver().split(StrUtil.COMMA).length > BATCH_RECEIVER_SIZE)) {
context.setNeedBreak(true).setResponse(BasicResultVO.fail(RespStatusEnum.TOO_MANY_RECEIVER));
return;
}
}
}

@ -23,6 +23,7 @@ public class MessageParam {
/**
* @Description:
* ,
* 100
*
*/
private String receiver;

@ -197,14 +197,4 @@ public class DataServiceImpl implements DataService {
}
return businessId;
}
public static void main(String[] args) {
Long time = 1653140847 * 1000L;
String format = DateUtil.format(new Date(time), DatePattern.NORM_DATETIME_PATTERN);
System.out.println(format
);
}
}

Loading…
Cancel
Save