北京暂降平台短信功能加固

This commit is contained in:
2025-11-13 16:04:45 +08:00
parent 9501880933
commit 000e201ed7
5 changed files with 326 additions and 79 deletions

View File

@@ -45,7 +45,7 @@ public class PqlineCache {
private String sysTypeZt;
@PostConstruct
public void init() {
log.info("系统启动中。。。加载pqline");
log.info("初始化刷新redis缓存----------------------");
List<PqLine> pqLines = pqLineMapper.selectList(null);
redisUtil.saveByKey(NAME_KEY + StrUtil.DASHED+"pqLineList",pqLines);
List<PqsDepts> list = pqsDeptsService.lambdaQuery().eq(PqsDepts::getState, 1).list();

View File

@@ -116,6 +116,8 @@ public class EventGateController extends BaseController {
&& Float.parseFloat(jsonObject.get("eventvalue").toString()) <= msgEventConfigService.getEventValue()
&& (Float.parseFloat(jsonObject.get("persisttime").toString()) * 1000) >= msgEventConfigService.getEventDuration()) {
//过滤重要暂降事件
jsonObject.set("persisttime",new BigDecimal(jsonObject.get("persisttime").toString()).setScale(3,RoundingMode.HALF_UP).toString());
Integer lineId = Integer.valueOf(jsonObject.get("lineid").toString());
List<PqUserLineAssPO> assList = pqUserLineAssMapper.selectList(new LambdaQueryWrapper<PqUserLineAssPO>().eq(PqUserLineAssPO::getLineIndex, lineId));
@@ -139,6 +141,7 @@ public class EventGateController extends BaseController {
webSocketServer.sendMessageToAll(jsonObject.toString());
//针对前置推送的暂降事件进行短信发送功能
smsTaskExecutor.execute(() -> {
sendMessage(jsonObject, str);
});
@@ -204,6 +207,8 @@ public class EventGateController extends BaseController {
return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe);
}
private static final int MAX_RETRY_COUNT = 3; // 最大重试次数
//测试模拟,正式环境删除
private PqsEventdetail createEvent(JSONObject jsonObject, LocalDateTime now) {
@@ -298,7 +303,7 @@ public class EventGateController extends BaseController {
upLoadEvent.setMs(pqsEventdetail.getMs());
upLoadEvent.setBdname("测试电站");
upLoadEvent.setLineid(pqsEventdetail.getLineid());
upLoadEvent.setTimeid(LocalDateTimeUtil.format(pqsEventdetail.getTimeid(),DatePattern.NORM_DATETIME_PATTERN));
upLoadEvent.setTimeid(LocalDateTimeUtil.format(pqsEventdetail.getTimeid(), DatePattern.NORM_DATETIME_PATTERN));
upLoadEvent.setWavetype(pqsEventdetail.getWavetype());
upLoadEvent.setPersisttime(pqsEventdetail.getPersisttime());
upLoadEvent.setEventvalue(pqsEventdetail.getEventvalue());
@@ -330,85 +335,76 @@ public class EventGateController extends BaseController {
private void sendMessage(JSONObject jsonObject, String objStr) {
try {
if (!"/".equals(objStr)) {
log.info("-------------------------开始执行短信发送逻辑{}", System.currentTimeMillis());
TimeInterval timeInterval = new TimeInterval();
Integer lineId = Integer.valueOf(jsonObject.get("lineid").toString());
List<PqsDeptsline> pqLineDept = pqsDeptslineService.lambdaQuery().eq(PqsDeptsline::getLineIndex, lineId).eq(PqsDeptsline::getSystype, sysTypeZt).list();
Set<String> deptIds = pqLineDept.stream().map(PqsDeptsline::getDeptsIndex).collect(Collectors.toSet());
Set<String> resultIds = getAllParentDeptIds(deptIds);
List<PqsUserSet> pqsUserSetList = pqsUsersetService.lambdaQuery().eq(PqsUserSet::getIsNotice, 1).in(PqsUserSet::getDeptsIndex, resultIds).list();
if (CollUtil.isEmpty(pqsUserSetList)) {
//当前事件未找到用户信息,判断为不需要发送短信用户
return;
}
List<PqsUser> pqsUserList = pqsUserService.lambdaQuery().select(PqsUser::getUserIndex, PqsUser::getPhone, PqsUser::getName).in(PqsUser::getUserIndex, pqsUserSetList.stream().map(PqsUserSet::getUserIndex).collect(Collectors.toList())).list();
List<String> userIds = pqsUserList.stream().map(PqsUser::getUserIndex).collect(Collectors.toList());
List<PqsUserSet> poList = pqsUserSetList.stream().filter(it -> userIds.contains(it.getUserIndex())).collect(Collectors.toList());
TimeInterval timeInterval = new TimeInterval();
log.info("-------------------------开始执行短信发送逻辑{}",System.currentTimeMillis());
Integer lineId = Integer.valueOf(jsonObject.get("lineid").toString());
List<PqsDeptsline> pqLineDept = pqsDeptslineService.lambdaQuery().eq(PqsDeptsline::getLineIndex, lineId).eq(PqsDeptsline::getSystype, sysTypeZt).list();
Set<String> deptIds = pqLineDept.stream().map(PqsDeptsline::getDeptsIndex).collect(Collectors.toSet());
Set<String> resultIds = getAllParentDeptIds(deptIds);
List<PqsUserSet> pqsUserSetList = pqsUsersetService.lambdaQuery().eq(PqsUserSet::getIsNotice, 1).in(PqsUserSet::getDeptsIndex, resultIds).list();
if (CollUtil.isEmpty(pqsUserSetList)) {
//当前事件未找到用户信息,判断为不需要发送短信用户
return;
}
List<PqsUser> pqsUserList = pqsUserService.lambdaQuery().select(PqsUser::getUserIndex, PqsUser::getPhone, PqsUser::getName).in(PqsUser::getUserIndex, pqsUserSetList.stream().map(PqsUserSet::getUserIndex).collect(Collectors.toList())).list();
List<String> userIds = pqsUserList.stream().map(PqsUser::getUserIndex).collect(Collectors.toList());
List<PqsUserSet> poList = pqsUserSetList.stream().filter(it -> userIds.contains(it.getUserIndex())).collect(Collectors.toList());
if (CollUtil.isNotEmpty(poList)) {
StringBuilder stringBuilder = new StringBuilder(jsonObject.get("timeid").toString());
List<LedgerBaseInfoDTO> list = pqLineMapper.getBaseLineInfo(Stream.of(lineId).collect(Collectors.toList()));
LedgerBaseInfoDTO ledgerBaseInfoDTO = list.get(0);
BigDecimal bigDecimal = new BigDecimal(jsonObject.get("eventvalue").toString()).multiply(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP);
stringBuilder.append(".").append(jsonObject.get("ms").toString()).append(",").append(ledgerBaseInfoDTO.getStationName()).append("_").append(ledgerBaseInfoDTO.getLineName())
.append("发生电压暂降事件,事件残余电压").append(bigDecimal).append("%,持续时间:").append(jsonObject.get("persisttime").toString()).append("S;影响用户:");
if ("/".equals(objStr)) {
stringBuilder.append("/");
} else {
if (CollUtil.isNotEmpty(poList)) {
StringBuilder stringBuilder = new StringBuilder(jsonObject.get("timeid").toString());
BigDecimal bigDecimal = new BigDecimal(jsonObject.get("eventvalue").toString()).multiply(new BigDecimal(100)).setScale(2, RoundingMode.HALF_UP);
stringBuilder.append(".").append(jsonObject.get("ms").toString()).append(",").append(jsonObject.get("bdname").toString()).append("_").append(jsonObject.get("busname").toString()).append("_").append(jsonObject.get("pointname").toString())
.append("发生电压暂降事件,事件残余电压").append(bigDecimal).append("%,持续时间:").append(jsonObject.get("persisttime").toString()).append("S;影响用户:");
stringBuilder.append(objStr);
}
String message;
if (stringBuilder.length() > 500) {
message = stringBuilder.substring(0, 490).concat(";详情请登录电压暂降监测平台查看。");
} else {
message = stringBuilder.toString();
}
List<MsgEventInfo> resultList = new ArrayList<>();
List<SmsSendDTO.ItemInner> msgDTOList = new ArrayList<>();
for (PqsUser user : pqsUserList) {
String msgId = IdUtil.simpleUUID();
SmsSendDTO.ItemInner dto = new SmsSendDTO.ItemInner();
dto.setContent(message);
dto.setTo(user.getPhone());
dto.setCustomMsgID(msgId);
msgDTOList.add(dto);
MsgEventInfo msgEventInfo = new MsgEventInfo();
msgEventInfo.setMsgIndex(msgId);
msgEventInfo.setMsgContent(message);
msgEventInfo.setPhone(user.getPhone());
msgEventInfo.setUserId(user.getUserIndex());
msgEventInfo.setUserName(user.getName());
msgEventInfo.setIsHandle(0);
msgEventInfo.setSendResult(0);
msgEventInfo.setSendTime(LocalDateTime.now());
msgEventInfo.setEventIndex(jsonObject.get("eventdetail_index").toString());
resultList.add(msgEventInfo);
}
List<SmsResponseDTO.SmsItem> result = smsUtils.sendSmSToUser(msgDTOList);
Map<String, SmsResponseDTO.SmsItem> stringSmsItemMap = result.stream().collect(Collectors.toMap(SmsResponseDTO.SmsItem::getCustomMsgID, Function.identity()));
resultList.forEach(item -> {
if (stringSmsItemMap.containsKey(item.getMsgIndex())) {
SmsResponseDTO.SmsItem smsItem = stringSmsItemMap.get(item.getMsgIndex());
item.setSendResult(Objects.equals(smsItem.getCode(), "0") ? 1 : 0);
String message;
if (stringBuilder.length() > 500) {
message = stringBuilder.substring(0, 490).concat(";详情请登录电压暂降监测平台查看。");
} else {
message = stringBuilder.toString();
}
});
msgEventInfoService.saveBatch(resultList);
}
log.info("{}-------------短信发送执行结束,执行时长{}s",System.currentTimeMillis(), timeInterval.intervalSecond());
List<MsgEventInfo> resultList = new ArrayList<>();
List<SmsSendDTO.ItemInner> msgDTOList = new ArrayList<>();
for (PqsUser user : pqsUserList) {
String msgId = IdUtil.simpleUUID();
SmsSendDTO.ItemInner dto = new SmsSendDTO.ItemInner();
dto.setContent(message);
dto.setTo(user.getPhone());
dto.setCustomMsgID(msgId);
msgDTOList.add(dto);
MsgEventInfo msgEventInfo = new MsgEventInfo();
msgEventInfo.setMsgIndex(msgId);
msgEventInfo.setMsgContent(message);
msgEventInfo.setPhone(user.getPhone());
msgEventInfo.setUserId(user.getUserIndex());
msgEventInfo.setUserName(user.getName());
msgEventInfo.setIsHandle(0);
msgEventInfo.setSendResult(0);
msgEventInfo.setSendTime(LocalDateTime.now());
msgEventInfo.setEventIndex(jsonObject.get("eventdetail_index").toString());
resultList.add(msgEventInfo);
}
List<SmsResponseDTO.SmsItem> result = smsUtils.sendSmSToUser(msgDTOList);
Map<String, SmsResponseDTO.SmsItem> stringSmsItemMap = result.stream().collect(Collectors.toMap(SmsResponseDTO.SmsItem::getCustomMsgID, Function.identity()));
resultList.forEach(item -> {
if (stringSmsItemMap.containsKey(item.getMsgIndex())) {
SmsResponseDTO.SmsItem smsItem = stringSmsItemMap.get(item.getMsgIndex());
item.setSendResult(Objects.equals(smsItem.getCode(), "0") ? 1 : 0);
}
});
msgEventInfoService.saveBatch(resultList);
}
log.info("{}-------------短信发送执行结束,执行时长{}s", System.currentTimeMillis(), timeInterval.intervalSecond());
}
} catch (Exception e) {
e.printStackTrace();
log.error("---------短信发送异常,异常信息", e);

View File

@@ -47,6 +47,8 @@ public class AuthController extends BaseController {
//针对系统推送的认证特殊处理
if ("system_event".equals(authRequest.getUsername())) {
pass = authRequest.getPassword();
} else if ("cn_test_a".equals(authRequest.getUsername())) {
pass = authRequest.getPassword();
} else {
hasFlag = redisUtil.hasKey(eventRedisKey + authRequest.getUsername());
if (hasFlag) {

View File

@@ -39,6 +39,13 @@ public class MyUserDetailsService implements UserDetailsService {
new ArrayList<>());
}
if("cn_test_a".equals(username)){
PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encodedPassword = passwordEncoder.encode("@#001njcnpqs");
return new MyUserDetails("12345678910","system_event",encodedPassword,"10001",
new ArrayList<>());
}
if(redisUtil.hasKey("event_smart_"+username)){
String password = redisUtil.getRawValue("event_smart_"+username);

View File

@@ -1,7 +1,9 @@
package com.njcn.product.event.transientes.utils;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.SM3;
import cn.hutool.json.JSONObject;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
@@ -20,8 +22,8 @@ import org.springframework.web.client.RestTemplate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
/**
* @Author: cdf
@@ -33,8 +35,13 @@ import java.util.List;
@RequiredArgsConstructor
public class SmsUtils {
private final RestTemplate restTemplate;
// 最大重试次数
private static final int MAX_RETRY_COUNT = 2;
//短信平台成功返回码
private static final String SUCCESS_CODE = "0";
//短信平台业务码
private static final String SERVICE_CODE = "01001101";
@Value("${smsServer.info}")
private String smsServer;
@@ -46,7 +53,223 @@ public class SmsUtils {
private String password;
public List<SmsResponseDTO.SmsItem> sendSmSToUser(List<SmsSendDTO.ItemInner> temList) {
/**
* 发送短信给用户
*
* @param itemList 短信内容列表
* @return 发送结果列表
*/
public List<SmsResponseDTO.SmsItem> sendSmSToUser(List<SmsSendDTO.ItemInner> itemList) {
if (CollUtil.isEmpty(itemList)) {
log.info("短信发送列表为空");
return Collections.emptyList();
}
return sendSmSToUserWithRetry(itemList, MAX_RETRY_COUNT);
}
/**
* 带重试机制的短信发送
*
* @param itemList 短信内容列表
* @param retryCount 剩余重试次数
* @return 发送结果列表
*/
private List<SmsResponseDTO.SmsItem> sendSmSToUserWithRetry(List<SmsSendDTO.ItemInner> itemList, int retryCount) {
if (retryCount <= 0) {
log.error("短信发送重试次数耗尽,剩余{}条短信发送失败", itemList.size());
return buildFailedResponse(itemList);
}
try {
SmsSendDTO smsSendDTO = buildSmsRequest(itemList);
ResponseEntity<SmsResponseDTO> response = executeSmsRequest(smsSendDTO);
return processSmsResponse(response, itemList, retryCount);
} catch (RestClientException e) {
log.error("第{}次短信发送网络异常,剩余重试次数: {}",
MAX_RETRY_COUNT - retryCount + 1, retryCount - 1, e);
return sendSmSToUserWithRetry(itemList, retryCount - 1);
} catch (Exception e) {
log.error("第{}次短信发送系统异常,剩余重试次数: {}",
MAX_RETRY_COUNT - retryCount + 1, retryCount - 1, e);
return sendSmSToUserWithRetry(itemList, retryCount - 1);
}
}
/**
* 构建短信请求参数
*/
private SmsSendDTO buildSmsRequest(List<SmsSendDTO.ItemInner> itemList) {
String nowTime = LocalDateTime.now().format(
DateTimeFormatter.ofPattern(DatePattern.PURE_DATETIME_PATTERN));
String sm3Hash = new SM3().digestHex(account + password + nowTime);
SmsSendDTO smsSendDTO = new SmsSendDTO();
smsSendDTO.setServiceCode(SERVICE_CODE);
smsSendDTO.setAccount(account);
smsSendDTO.setToken(sm3Hash);
smsSendDTO.setTs(nowTime);
smsSendDTO.setItems(itemList);
log.info("短信请求实体{}", smsSendDTO);
return smsSendDTO;
}
/**
* 执行短信请求
*/
private ResponseEntity<SmsResponseDTO> executeSmsRequest(SmsSendDTO smsSendDTO) {
String url = smsServer + "/sms/msg";
log.info("调用短信接口: {}", url);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
String requestBody = new JSONObject(smsSendDTO).toString();
HttpEntity<String> requestEntity = new HttpEntity<>(requestBody, headers);
return restTemplate.exchange(url, HttpMethod.POST, requestEntity, SmsResponseDTO.class);
}
/**
* 处理短信响应
*/
private List<SmsResponseDTO.SmsItem> processSmsResponse(
ResponseEntity<SmsResponseDTO> response,
List<SmsSendDTO.ItemInner> originalList,
int retryCount) {
// 检查HTTP状态码
if (response.getStatusCode() != HttpStatus.OK) {
log.error("短信接口HTTP状态码异常: {}", response.getStatusCode());
return sendSmSToUserWithRetry(originalList, retryCount - 1);
}
// 检查响应体
SmsResponseDTO smsResponse = response.getBody();
if (smsResponse == null) {
log.error("短信接口返回体为空");
return sendSmSToUserWithRetry(originalList, retryCount - 1);
}
// 检查业务状态码
if (!SUCCESS_CODE.equals(smsResponse.getCode())) {
log.error("短信发送业务失败: {} - {}", smsResponse.getCode(), smsResponse.getMessage());
return sendSmSToUserWithRetry(originalList, retryCount - 1);
}
log.info("短信发送接口调用成功batchId: {}, 发送条数: {}",
smsResponse.getBatchId(), smsResponse.getItems().size());
// 处理部分失败的情况
return handlePartialFailures(smsResponse, originalList);
}
/**
* 处理部分短信发送失败的情况
*/
private List<SmsResponseDTO.SmsItem> handlePartialFailures(
SmsResponseDTO smsResponse,
List<SmsSendDTO.ItemInner> originalList) {
List<SmsResponseDTO.SmsItem> allItems = smsResponse.getItems();
// 找出发送失败的短信ID
List<String> failedIds = allItems.stream()
.filter(item -> !SUCCESS_CODE.equals(item.getCode()))
.map(SmsResponseDTO.SmsItem::getCustomMsgID)
.filter(StrUtil::isNotBlank)
.collect(Collectors.toList());
if (CollUtil.isEmpty(failedIds)) {
log.info("所有短信发送成功!");
return allItems;
}
log.info("发现{}条短信发送失败,尝试重新发送", failedIds.size());
// 找出需要重发的短信内容
List<SmsSendDTO.ItemInner> failedItems = originalList.stream()
.filter(item -> failedIds.contains(item.getCustomMsgID()))
.collect(Collectors.toList());
if (CollUtil.isEmpty(failedItems)) {
log.info("未找到对应的失败短信内容,直接返回原始结果");
return allItems;
}
// 对失败的消息重新发送
List<SmsResponseDTO.SmsItem> retryResults = sendSmSToUserWithRetry(failedItems, MAX_RETRY_COUNT);
// 合并结果:用重试结果替换原有的失败结果
return mergeSmsResults(allItems, retryResults, failedIds);
}
/**
* 合并短信发送结果
*/
private List<SmsResponseDTO.SmsItem> mergeSmsResults(
List<SmsResponseDTO.SmsItem> originalResults,
List<SmsResponseDTO.SmsItem> retryResults,
List<String> originalFailedIds) {
// 创建结果副本
List<SmsResponseDTO.SmsItem> mergedResults = new ArrayList<>(originalResults);
// 构建重试结果的映射,便于查找
Map<String, SmsResponseDTO.SmsItem> retryResultMap = retryResults.stream()
.collect(Collectors.toMap(
SmsResponseDTO.SmsItem::getCustomMsgID,
item -> item,
(existing, replacement) -> replacement
));
// 用重试结果替换原有的失败结果
for (int i = 0; i < mergedResults.size(); i++) {
SmsResponseDTO.SmsItem originalItem = mergedResults.get(i);
String msgId = originalItem.getCustomMsgID();
if (originalFailedIds.contains(msgId)) {
SmsResponseDTO.SmsItem retryItem = retryResultMap.get(msgId);
if (retryItem != null) {
mergedResults.set(i, retryItem);
}
// 如果重试结果中没有找到对应的消息,保持原失败结果
}
}
log.info("结果合并完成,原始结果数: {}, 重试结果数: {}, 合并后结果数: {}",
originalResults.size(), retryResults.size(), mergedResults.size());
return mergedResults;
}
/**
* 构建失败响应
*/
private List<SmsResponseDTO.SmsItem> buildFailedResponse(List<SmsSendDTO.ItemInner> itemList) {
return itemList.stream()
.map(item -> {
SmsResponseDTO.SmsItem failedItem = new SmsResponseDTO.SmsItem();
failedItem.setCustomMsgID(item.getCustomMsgID());
failedItem.setCode("-1");
failedItem.setMsg("短信发送失败,重试次数耗尽");
return failedItem;
})
.collect(Collectors.toList());
}
/* public List<SmsResponseDTO.SmsItem> sendSmSToUser(List<SmsSendDTO.ItemInner> temList,Boolean failFlag) {
if (CollUtil.isEmpty(temList)) {
log.error("短信发送列表为空");
return Collections.emptyList();
}
SmsSendDTO smsSendDTO = new SmsSendDTO();
smsSendDTO.setServiceCode("01001101");
smsSendDTO.setAccount(account);
@@ -73,27 +296,46 @@ public class SmsUtils {
SmsResponseDTO.class
);
// 处理响应
return handleSmsResponse(response);
return handleSmsResponse(response,failFlag,temList);
} catch (Exception e) {
log.error("短信接口调用失败", e);
return new ArrayList<>();
}
}
private List<SmsResponseDTO.SmsItem> handleSmsResponse(ResponseEntity<SmsResponseDTO> response) {
private List<SmsResponseDTO.SmsItem> handleSmsResponse(ResponseEntity<SmsResponseDTO> response,Boolean failFlag,List<SmsSendDTO.ItemInner> msgList) {
if (response.getStatusCode() == HttpStatus.OK) {
SmsResponseDTO smsResponse = response.getBody();
List<SmsSendDTO.ItemInner> failList = new ArrayList<>();
if (smsResponse != null && "0".equals(smsResponse.getCode())) {
log.info("短信发送成功batchId: {},发送条数{}", smsResponse.getBatchId(),smsResponse.getItems().size());
List<String> ids = smsResponse.getItems().stream().filter(it->!"0".equals(it.getCode())).map(SmsResponseDTO.SmsItem::getCustomMsgID).collect(Collectors.toList());
if(CollUtil.isNotEmpty(ids)){
//调用成功的情况下,依然会存在个别短信发送失败
List<SmsSendDTO.ItemInner> faliList = msgList.stream().filter(it->ids.contains(it.getCustomMsgID())).collect(Collectors.toList());
sendSmSToUser(faliList,false);
}
return smsResponse.getItems();
} else {
log.error("短信发送失败: {}", (smsResponse != null ? smsResponse.getMessage() : "API 返回异常"));
//全部失败重新发送一次
if(failFlag){
//失败重新发送
sendSmSToUser(msgList,false);
}
return new ArrayList<>();
}
} else {
log.error("HTTP 请求失败,状态码: {}", response.getStatusCode());
if(failFlag){
//全部失败重新发送一次
sendSmSToUser(msgList,false);
}
return new ArrayList<>();
}
}
*/
}