Compare commits

...

2 Commits

  1. 8
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/equManagement/EquBaseController.java
  2. 1
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/domain/dto/EquBaseQueryDto.java
  3. 3
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/domain/vo/GatherEntities.java
  4. 14
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/job/GatherJob.java
  5. 8
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/mapper/EquBaseMapper.java
  6. 5
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/mapper/EquGatherMapper.java
  7. 1
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/mapper/EquOidMapper.java
  8. 2
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/IScheduleRulesService.java
  9. 4
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/MemoryToolService.java
  10. 149
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/AlertLaunchServiceImpl.java
  11. 4
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/AlertRuleServiceImpl.java
  12. 142
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/AlertSenderServiceImpl.java
  13. 105
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/GatherValidator.java
  14. 100
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/MemoryToolServiceImpl.java
  15. 405
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/ScheduleRulesServiceImpl.java
  16. 26
      ALOps_sys_backend/alops-system/src/main/resources/mapper/system/EquBaseMapper.xml
  17. 14
      ALOps_sys_backend/alops-system/src/main/resources/mapper/system/EquGatherMapper.xml
  18. 5
      ALOps_sys_backend/alops-system/src/main/resources/mapper/system/EquOidMapper.xml

@ -61,7 +61,7 @@ public class EquBaseController extends BaseController
/**
* 查询多台设备的最新监测信息
**/
@PreAuthorize("@ss.hasPermi('device:files:query')")
@PreAuthorize("@ss.hasPermi('device:files:querymonitorpage')")
@GetMapping("/monitorpage")
@ApiOperation("分页查询多台设备的最新监测信息")
public TableDataInfo getMonitorList() {
@ -88,7 +88,7 @@ public class EquBaseController extends BaseController
* 获取设备最新时间的历史监控信息
* @param id 查询对象包含设备ID开始时间和结束时间
*/
@PreAuthorize("@ss.hasPermi('device:files:query')")
@PreAuthorize("@ss.hasPermi('device:files:querylatest')")
@GetMapping(value = "/latest/{id}") // URL清晰地表达了意图:获取历史监控信息
@ApiOperation("获取设备最新时间的历史监控信息")
@Anonymous
@ -102,7 +102,7 @@ public class EquBaseController extends BaseController
* 获取设备指定时间段内的历史监控信息
* @param equBaseQueryDto 查询对象包含设备ID开始时间和结束时间
*/
@PreAuthorize("@ss.hasPermi('device:files:query')")
@PreAuthorize("@ss.hasPermi('device:files:queryhistory')")
@GetMapping(value = "/history") // URL清晰地表达了意图:获取历史监控信息
@ApiOperation("获取设备指定时间段内的历史监控信息")
@Anonymous
@ -131,7 +131,7 @@ public class EquBaseController extends BaseController
/**
* 统计楼层设备分布
*/
@PreAuthorize("@ss.hasPermi('device:files:statistic')")
@PreAuthorize("@ss.hasPermi('device:files:statisticfloor')")
@GetMapping("/floorStatistic")
@ApiOperation("统计楼层设备分布信息")
public AjaxResult getFloorDeviceStatistic() {

@ -26,6 +26,7 @@ public class EquBaseQueryDto implements Serializable {
/** 安装位置 ID (location, varchar) */
private String location;
private String floor;
/** 起始时间 (min_time) */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date minTime;

@ -15,6 +15,7 @@ import java.util.Map;
public class GatherEntities {
private EquGather equGather; // 主表
private Map<String, List<Object>> subEntities = new HashMap<>();
private Map<String, List<Object>> subEntities = new HashMap<>();//插入表
private Map<String, Object> updateEntities = new HashMap<>();//更新表
}

@ -46,11 +46,11 @@ public class GatherJob implements Job {
// scheduler.deleteJob(jobKey);
boolean deleted = scheduler.deleteJob(jobKey);
if (deleted) {
iAlertSenderService.sendInternalMessage("采集任务取消成功","任务执行完此次,已取消后续任务","2");
iAlertSenderService.sendInternalMessage("采集整体任务取消成功","任务执行完此次,已取消后续任务","2");
log.info("规则[{}] 已标记为取消状态(4),对应定时任务已删除: {}", rule.getId(), jobKey);
} else {
log.warn("规则[{}] 尝试取消定时任务失败: {}", rule.getId(), jobKey);
iAlertSenderService.sendInternalMessage("采集任务取消失败","尝试取消定时任务失败,请检查调度器状态","2");
iAlertSenderService.sendInternalMessage("采集整体任务取消失败","尝试取消定时任务失败,请检查调度器状态","2");
}
return;
}
@ -67,7 +67,7 @@ public class GatherJob implements Job {
if ( now.after(end)) {
rule.setStatus(2); // 超出范围标记完成
scheduleRulesMapper.updateScheduleRules(rule);
iAlertSenderService.sendInternalMessage("采集任务成功","按照频率进行采集的任务执行完成","2");
iAlertSenderService.sendInternalMessage("采集整体任务成功","按照频率进行采集的任务执行完成","2");
return;
}
@ -81,10 +81,10 @@ public class GatherJob implements Job {
if (rule.getRuleType() == 1 ) {
rule.setStatus(2);
String Content=String.format("定时采集任务执行完成",
String Content=String.format("定时采集任务整体执行完成",
rule.getId(), rule.getRuleName());
iAlertSenderService.sendInternalMessage("采集任务成功" ,Content,"2" );
iAlertSenderService.sendInternalMessage("采集任务整体成功" ,Content,"2" );
scheduleRulesMapper.updateScheduleRules(rule);
}
@ -100,10 +100,10 @@ public class GatherJob implements Job {
rule.setStatus(3);
scheduleRulesMapper.updateScheduleRules(rule);
try {
String Content=String.format("采集任务执行失败,规则ID=%s,规则名称=%s,原因:%s",
String Content=String.format("采集任务整体执行失败,规则ID=%s,规则名称=%s,原因:%s",
rule.getId(), rule.getRuleName(), e.getMessage());
iAlertSenderService.sendInternalMessage("采集任务失败" ,Content,"2" );
iAlertSenderService.sendInternalMessage("采集任务整体执行失败" ,Content,"2" );
log.info("已发送失败通知给运维人员");

@ -22,6 +22,14 @@ import org.springframework.data.repository.query.Param;
public interface EquBaseMapper
{
String selectEquNameById(@Param("equId") String equId);
/**
* 批量更新 equ_base 表的 version 字段
* @param list 待更新的实体列表每个实体必须包含 id version
* @return 更新的行数
*/
int updateEquBaseVersionBatch(@Param("list") List<EquBase> list);
/**
* 查询状态为1的设备及其最新采集信息和类型名称

@ -16,6 +16,11 @@ import java.util.List;
public interface EquGatherMapper
{
/**
* 根据设备ID列表查询每台设备的最新采集ID
*/
List<String> selectLatestGatherIdsByEquIds(@Param("equIds") List<String> equIds);
// 更新 health 字段
int updateHealth(String collectId, Double totalHealth);

@ -22,6 +22,7 @@ public interface EquOidMapper
* @return OID列表
*/
List<EquOid> findByManuIdAndStatus(@Param("manufacturerId") String manufacturerId);
List<EquOid> findByManuIdAndStatus2(@Param("manufacturerId") String manufacturerId);
/**
* 查询OID配置信息
*

@ -19,8 +19,6 @@ import java.util.concurrent.ExecutionException;
public interface IScheduleRulesService
{
public Boolean checkRunningOrWaitingRules() throws SchedulerException;
public Boolean executeRule(ScheduleRules newRule) throws SchedulerException, ExecutionException, InterruptedException;

@ -16,4 +16,8 @@ public interface MemoryToolService {
EquBase device,
Map<String, Object> snmpResults);
public int persistAll(List<GatherEntities> allEntities);
}

@ -9,9 +9,12 @@ import com.alops.system.mapper.AlertRuleMapper;
import com.alops.system.mapper.EquBaseMapper;
import com.alops.system.service.IAlertLaunchService;
import com.alops.system.service.IAlertSenderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
@ -38,16 +41,39 @@ public class AlertLaunchServiceImpl implements IAlertLaunchService {
private EquBaseMapper equBaseMapper;
private static final Logger log = LoggerFactory.getLogger(AlertLaunchServiceImpl.class);
private final ExecutorService executor = Executors.newFixedThreadPool(5);
/**
* 执行立即预警
* 执行立即预警,这里捕捉异常不影响采集操作
*/
@Transactional(rollbackFor = Exception.class)
// @Override
// public void executeImmediateAlerts() {
// Integer maxBatch = alertMessageMapper.selectMaxBatch();
// Integer currentBatch = (maxBatch == null ? 0 : maxBatch) + 1;
//
// List<AlertRule> rules = alertRuleMapper.selectAlertRule();
//
// Map<String, Integer> totalFailureCount = new HashMap<>();
// for (AlertRule rule : rules) {
// Map<String, Integer> map = processRule(rule, currentBatch); // 直接同步调用
// map.forEach((equId, count) -> totalFailureCount.merge(equId, count, Integer::sum));
// }
//
// List<Map<String, Object>> updateList = totalFailureCount.entrySet().stream()
// .map(entry -> {
// Map<String, Object> map = new HashMap<>();
// map.put("equId", entry.getKey());
// map.put("count", entry.getValue());
// return map;
// })
// .collect(Collectors.toList());
// if (!updateList.isEmpty()) {
// equBaseMapper.batchUpdateFailureFrequency(updateList); // 出异常会触发回滚
// }
// }
@Override
public void executeImmediateAlerts() {
Integer maxBatch = alertMessageMapper.selectMaxBatch();
Integer currentBatch = (maxBatch == null ? 0 : maxBatch) + 1;
@ -55,24 +81,35 @@ public class AlertLaunchServiceImpl implements IAlertLaunchService {
List<AlertRule> rules = alertRuleMapper.selectAlertRule();
Map<String, Integer> totalFailureCount = new HashMap<>();
for (AlertRule rule : rules) {
Map<String, Integer> map = processRule(rule, currentBatch); // 直接同步调用
try {
Map<String, Integer> map = processRule(rule, currentBatch); // 规则级容错
map.forEach((equId, count) -> totalFailureCount.merge(equId, count, Integer::sum));
} catch (Exception e) {
// 防御性捕获,保证循环继续
log.error("【规则执行异常】规则ID={} 名称='{}'", rule.getId(), rule.getRuleName(), e);
}
}
// 更新设备故障频率(核心统计数据异常才回滚)
if (!totalFailureCount.isEmpty()) {
try {
List<Map<String, Object>> updateList = totalFailureCount.entrySet().stream()
.map(entry -> {
Map<String, Object> map = new HashMap<>();
map.put("equId", entry.getKey());
map.put("count", entry.getValue());
return map;
})
.collect(Collectors.toList());
if (!updateList.isEmpty()) {
equBaseMapper.batchUpdateFailureFrequency(updateList); // 出异常会触发回滚
}).collect(Collectors.toList());
equBaseMapper.batchUpdateFailureFrequency(updateList);
} catch (Exception e) {
log.error("更新设备故障频率异常", e);
throw new RuntimeException("更新设备故障频率失败", e); // 核心异常才抛
}
}
}
// public void executeImmediateAlerts() throws ExecutionException, InterruptedException {
// // 1. 查询当前最大批次,没有则返回 0
@ -112,14 +149,29 @@ public class AlertLaunchServiceImpl implements IAlertLaunchService {
/**
* 同步插入消息
*/
@Transactional
// @Transactional
// public void insertAlertMessages(List<AlertMessage> messages) {
// if (messages == null || messages.isEmpty()) {
// return;
// }
// int rows = alertMessageMapper.batchInsert(messages);
// if (rows != messages.size()) {
// throw new RuntimeException("部分消息未插入成功");
// }
// }
public void insertAlertMessages(List<AlertMessage> messages) {
if (messages == null || messages.isEmpty()) {
return;
}
try {
int rows = alertMessageMapper.batchInsert(messages);
if (rows != messages.size()) {
throw new RuntimeException("部分消息未插入成功");
log.warn("部分预警消息未成功插入,预期={}条,实际={}条", messages.size(), rows);
}
} catch (Exception e) {
log.error("插入预警消息失败: {}", e.getMessage(), e);
// 不抛出异常,继续处理
}
}
@ -128,12 +180,62 @@ public class AlertLaunchServiceImpl implements IAlertLaunchService {
* 处理单条预警规则
*/
private Map<String, Integer> processRule(AlertRule rule, Integer currentBatch) {
// private Map<String, Integer> processRule(AlertRule rule, Integer currentBatch) {
// Map<String, Integer> failureCountMap = new HashMap<>();
// try {
// List<AlertMessage> messages = new ArrayList<>();
// List<String> msgs = new ArrayList<>();
//
// boolean isChangedRule = "CHANGED".equalsIgnoreCase(rule.getAlertSql());
// List<Map<String, Object>> queryResults = isChangedRule
// ? handleChangedRule(rule)
// : jdbcTemplate.queryForList(rule.getAlertSql());
//
// for (Map<String, Object> row : queryResults) {
// AlertMessage msg = new AlertMessage();
// msg.setLevel(rule.getSeverity());
// msg.setType(rule.getType());
// msg.setOccurTime(new Date());
// msg.setStatus("UNHANDLED");
//
// String equId = row.get("equ_id") != null ? row.get("equ_id").toString() : null;
// msg.setEquId(equId);
// msg.setEquType(row.get("equ_type") != null ? row.get("equ_type").toString() : null);
//// msg.setEquType(row.get("equ_name") != null ? row.get("equ_name").toString() : null);
// String msgdes = fillTemplate(rule.getAlertTemplate(), row, rule);
//
// msg.setBatch(currentBatch);
// msg.setMessage(msgdes);
// msg.setAlertRuleId(rule.getId());
// messages.add(msg);
// msgs.add(msgdes);
//
// // 统计每个设备出现的次数
// if (equId != null) {
// failureCountMap.put(equId, failureCountMap.getOrDefault(equId, 0) + 1);
// }
// }
//
// if (!messages.isEmpty()) {
// alertSender.sendAlert(msgs, rule.getAlertWay(), rule.getAlertStation());
// insertAlertMessages(messages);
// }
//
// } catch (Exception e) {
// e.printStackTrace();
// }
// return failureCountMap;
// }
/**
* 单条规则执行逻辑独立事务执行避免互相影响
*/
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Map<String, Integer> processRule(AlertRule rule, Integer currentBatch) {
Map<String, Integer> failureCountMap = new HashMap<>();
try {
List<AlertMessage> messages = new ArrayList<>();
List<String> msgs = new ArrayList<>();
try {
boolean isChangedRule = "CHANGED".equalsIgnoreCase(rule.getAlertSql());
List<Map<String, Object>> queryResults = isChangedRule
? handleChangedRule(rule)
@ -149,7 +251,6 @@ public class AlertLaunchServiceImpl implements IAlertLaunchService {
String equId = row.get("equ_id") != null ? row.get("equ_id").toString() : null;
msg.setEquId(equId);
msg.setEquType(row.get("equ_type") != null ? row.get("equ_type").toString() : null);
// msg.setEquType(row.get("equ_name") != null ? row.get("equ_name").toString() : null);
String msgdes = fillTemplate(rule.getAlertTemplate(), row, rule);
msg.setBatch(currentBatch);
@ -158,34 +259,40 @@ public class AlertLaunchServiceImpl implements IAlertLaunchService {
messages.add(msg);
msgs.add(msgdes);
// 统计每个设备出现的次数
if (equId != null) {
failureCountMap.put(equId, failureCountMap.getOrDefault(equId, 0) + 1);
}
}
// ⚙ 发送预警 & 入库
if (!messages.isEmpty()) {
alertSender.sendAlert(msgs, rule.getAlertWay(), rule.getAlertStation());
insertAlertMessages(messages);
}
log.info("规则执行成功 → 规则ID={} 名称='{}',生成预警 {} 条",
rule.getId(), rule.getRuleName(), messages.size());
} catch (Exception e) {
e.printStackTrace();
// ⚠ 独立事务自动回滚,不影响其他规则
log.error("【规则执行异常】规则ID={} 名称='{}',SQL={},原因:{}",
rule.getId(), rule.getRuleName(), rule.getAlertSql(), e.getMessage(), e);
}
return failureCountMap;
}
private String fillTemplate(String template, Map<String, Object> row, AlertRule rule) {
String msg = template;
Map<String, Object> ruleMap = convertRuleToMap(rule);
//从预警规则中获取 预警规则名称,级别,方式,到站方 预警规则内容描述,预警规则创新时间,表格名称
for (Map.Entry<String, Object> entry : ruleMap.entrySet()) {
msg = msg.replace("{" + entry.getKey() + "}", entry.getValue() != null ? entry.getValue().toString() : "");
msg = msg.replace("{" + entry.getKey() + "}", entry.getValue() != null ? entry.getValue().toString() : "为空");
}
//设备id,设备类型,你要查参数 ,针对表格内容具体id,
for (Map.Entry<String, Object> entry : row.entrySet()) {
msg = msg.replace("{" + entry.getKey() + "}", entry.getValue() != null ? entry.getValue().toString() : "");
msg = msg.replace("{" + entry.getKey() + "}", entry.getValue() != null ? entry.getValue().toString() : "为空");
}
return msg;
}
@ -234,8 +341,10 @@ public class AlertLaunchServiceImpl implements IAlertLaunchService {
" WHERE t3.equ_id = g1.equ_id " +
" ) " +
") " +
"AND g1.`" + col + "` <> g2.`" + col + "`";
"AND (g1." + col + " <> g2." + col + " OR g1." + col + " IS NULL OR g2." + col + " IS NULL)";
System.out.println(sql);
System.out.println(jdbcTemplate.queryForList(sql));
// 4. 返回查询结果
return jdbcTemplate.queryForList(sql);
}

@ -215,7 +215,7 @@ public class AlertRuleServiceImpl implements IAlertRuleService
switch (op) {
case "=": case "!=": case "<": case "<=": case ">": case ">=":
return column + " " + op + " " + formatValue(value, valueType);
return "(" + column + " " + op + " " + formatValue(value, valueType) + " OR " + column + " IS NULL)";
case "contains":
return column + " LIKE '%" + escapeLike(value) + "%'";
@ -246,7 +246,7 @@ public class AlertRuleServiceImpl implements IAlertRuleService
throw new IllegalArgumentException(op + " 运算符需要两个值: " + value);
}
String range = formatValue(vals[0].trim(), valueType) + " AND " + formatValue(vals[1].trim(), valueType);
return column + (op.equals("between") ? " BETWEEN " : " NOT BETWEEN ") + range;
return "(" + column + (op.equals("between") ? " BETWEEN " : " NOT BETWEEN ") + range + " OR " + column + " IS NULL)";
case "in":
case "not in":

@ -47,21 +47,45 @@ public class AlertSenderServiceImpl implements IAlertSenderService {
* @param alertWay "email" "message"
* @param target 预警目标邮箱或角色ID
*/
@Override
// @Override
// public void sendAlert(List<String> alertMessages, String alertWay, String target) {
// if (alertMessages == null || alertMessages.isEmpty()) return;
//
// // 将多条消息合并成一条
// String combinedMessage = String.join("\n", alertMessages);
// //运维人员2
// sendInternalMessage("预警信息", combinedMessage, "2");
//
// if ("email".equalsIgnoreCase(alertWay)) {
// sendEmail(combinedMessage, target);
// } else {
// throw new IllegalArgumentException("未知预警方式:" + alertWay);
// }
//
// }
public void sendAlert(List<String> alertMessages, String alertWay, String target) {
if (alertMessages == null || alertMessages.isEmpty()) return;
// 将多条消息合并成一条
String combinedMessage = String.join("\n", alertMessages);
//运维人员2
sendInternalMessage("预警信息",combinedMessage, "2");
// 发送站内消息(运维人员2)
try {
sendInternalMessage("预警信息", combinedMessage, "2");
} catch (Exception e) {
log.error("发送站内消息失败: {}", e.getMessage(), e);
}
// 发送邮件或其他方式
if ("email".equalsIgnoreCase(alertWay)) {
try {
sendEmail(combinedMessage, target);
} catch (Exception e) {
log.info("发送邮件失败: {}", e.getMessage(), e);
}
} else {
throw new IllegalArgumentException("未知预警方式:" + alertWay);
log.info("未知预警方式:{}", alertWay);
}
}
@ -109,58 +133,134 @@ public class AlertSenderServiceImpl implements IAlertSenderService {
//
// }
// public void sendInternalMessage(String title, String content, String roleIdStr) {
// long roleId = Long.parseLong(roleIdStr);
// List<SysUser> users = userMapper.selectByRoleId(roleId);
// if (users == null || users.isEmpty()) return;
// //1.插入站内信表
// List<InternalMessage> messages = new ArrayList<>();
// for (SysUser user : users) {
// InternalMessage msg = new InternalMessage();
// msg.setUserId(user.getUserId());
// msg.setContent(content); // 一次插入整条合并后的消息
// msg.setCreateTime(new Date());
// msg.setStatus("unread");
// msg.setTitle(title);
// messages.add(msg);
// }
// List<Map<String, Object>> result = insertMessages(messages);
//
// //2.发送实时消息
// for (Map<String, Object> item : result) {
// Long userId = (Long) item.get("userId");
// Long messageId = (Long) item.get("id");
//
// // 调用 WebSocket 工具发送
// webSocketService.sendAlertMessage(userId, messageId, title, content);
// }
//
// log.info("共发送站内信 {} 条(角色ID={})", result.size(), roleId);
// }
//实现容错性,就是插入站内信,发送通知失败,不会滚事务,只记录
public void sendInternalMessage(String title, String content, String roleIdStr) {
long roleId = Long.parseLong(roleIdStr);
long roleId;
try {
roleId = Long.parseLong(roleIdStr);
} catch (NumberFormatException e) {
log.error("角色ID格式错误: {}", roleIdStr, e);
return;
}
List<SysUser> users = userMapper.selectByRoleId(roleId);
if (users == null || users.isEmpty()) return;
//1.插入站内信表
if (users == null || users.isEmpty()) {
log.warn("角色ID={} 没有用户可发送站内信", roleId);
return;
}
List<InternalMessage> messages = new ArrayList<>();
for (SysUser user : users) {
InternalMessage msg = new InternalMessage();
msg.setUserId(user.getUserId());
msg.setContent(content); // 一次插入整条合并后的消息
msg.setContent(content);
msg.setCreateTime(new Date());
msg.setStatus("unread");
msg.setTitle(title);
messages.add(msg);
}
List<Map<String, Object>> result = insertMessages(messages);
//2.发送实时消息
List<Map<String, Object>> result = new ArrayList<>();
try {
result = insertMessages(messages); // 插入站内信
} catch (Exception e) {
log.error("插入站内信失败: {}", e.getMessage(), e);
}
// 发送实时消息(WebSocket),单条失败不影响其他
for (Map<String, Object> item : result) {
try {
Long userId = (Long) item.get("userId");
Long messageId = (Long) item.get("id");
// 调用 WebSocket 工具发送
webSocketService.sendAlertMessage(userId, messageId, title, content);
} catch (Exception e) {
log.error("发送实时站内信失败 userId={} messageId={}: {}",
item.get("userId"), item.get("id"), e.getMessage(), e);
}
}
log.info("共发送站内信 {} 条(角色ID={})", result.size(), roleId);
log.info("尝试发送站内信 {} 条(角色ID={})", result.size(), roleId);
}
@Transactional(rollbackFor = Exception.class)
// @Transactional(rollbackFor = Exception.class)
// public List<Map<String, Object>> insertMessages(List<InternalMessage> messages) {
// if (messages == null || messages.isEmpty()) {
// return Collections.emptyList();
// }
//
// int rows = messageMapper.batchInsert(messages);
// if (rows != messages.size()) {
// throw new RuntimeException("部分消息未插入成功,事务回滚");
// }
//
// // 组装返回结果
// List<Map<String, Object>> resultList = new ArrayList<>();
// for (InternalMessage msg : messages) {
// Map<String, Object> map = new HashMap<>();
// map.put("id", msg.getId()); // 主键已被MyBatis自动回填
// map.put("userId", msg.getUserId());
// resultList.add(map);
// }
//
// return resultList;
// }
//}
@Transactional
public List<Map<String, Object>> insertMessages(List<InternalMessage> messages) {
if (messages == null || messages.isEmpty()) {
return Collections.emptyList();
}
List<Map<String, Object>> resultList = new ArrayList<>();
try {
int rows = messageMapper.batchInsert(messages);
if (rows != messages.size()) {
throw new RuntimeException("部分消息未插入成功,事务回滚");
log.warn("部分站内信未成功插入,预期={}条,实际={}条", messages.size(), rows);
}
// 组装返回结果
List<Map<String, Object>> resultList = new ArrayList<>();
for (InternalMessage msg : messages) {
Map<String, Object> map = new HashMap<>();
map.put("id", msg.getId()); // 主键已被MyBatis自动回填
map.put("id", msg.getId());
map.put("userId", msg.getUserId());
resultList.add(map);
}
} catch (Exception e) {
log.error("插入站内信异常", e);
// 不抛异常 → 外层事务不会回滚
}
return resultList;
}
}

@ -0,0 +1,105 @@
package com.alops.system.service.impl;
import com.alops.system.domain.EquOid;
import com.alops.system.domain.vo.GatherEntities;
import com.alops.system.mapper.EquBaseMapper;
import com.alops.system.service.IAlertSenderService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
@Service
public class GatherValidator {
public static class ValidationResult {
private List<GatherEntities> successList;
private List<Map<String, String>> failed; // 每个元素包含 equId 和 name
public ValidationResult(List<GatherEntities> successList, List<Map<String, String>> failed) {
this.successList = successList;
this.failed = failed;
}
public List<GatherEntities> getSuccessList() {
return successList;
}
public List<Map<String, String>> getFailed() {
return failed;
}
}
private static final Logger log = LoggerFactory.getLogger(ScheduleRulesServiceImpl.class);
@Autowired
public EquBaseMapper equBaseMapper;
@Autowired
public IAlertSenderService iAlertSenderService;
/**
* 检查 successList 中每台设备的完整性
* 如果所有设备都失败successList 为空抛异常
*/
public ValidationResult validateBeforeDbOperation(
List<GatherEntities> successList,
List<Map<String, String>> initialFailed, // 可以传入之前收集的失败设备
Map<String, List<EquOid>> oidMap) {
List<GatherEntities> validSuccessList = new ArrayList<>();
List<Map<String, String>> failed = new ArrayList<>(initialFailed);
for (GatherEntities g : successList) {
String equId = g.getEquGather() != null ? g.getEquGather().getEquId() : null;
String equName = equId != null ? equBaseMapper.selectEquNameById(equId) : "未知设备";
boolean hasEmptySubTable = g.getEquGather() == null ||
g.getSubEntities().values().stream()
.anyMatch(list -> list == null || list.isEmpty());
// 更新型 equ_base 检查
List<EquOid> oids = oidMap.getOrDefault(equId, Collections.emptyList());
boolean hasEnable1 = oids.stream().anyMatch(oid -> oid.getEnabled() == 1);
Object updateEntity = g.getUpdateEntities().get("equ_base");
boolean updateEquBaseMissing = hasEnable1 && updateEntity == null;
if (hasEmptySubTable || updateEquBaseMissing) {
Map<String, String> failInfo = new HashMap<>();
failInfo.put("equId", equId);
failInfo.put("equName", equName);
failed.add(failInfo);
// 记录日志
List<String> missingTables = new ArrayList<>();
if (g.getEquGather() == null) {
missingTables.add("equ_gather 主表为空");
}
g.getSubEntities().forEach((tableName, list) -> {
if (list == null || list.isEmpty()) missingTables.add(tableName);
});
if (updateEquBaseMissing) missingTables.add("更新型 equ_base 为空");
log.error("【采集失败设备】设备 [{}] 缺少数据:{}", equName, String.join(", ", missingTables));
String content = String.format("设备 [%s] 采集失败,缺少数据:%s",
equName, String.join(", ", missingTables));
iAlertSenderService.sendInternalMessage("采集失败设备", content, "2");
} else {
validSuccessList.add(g);
log.info("设备 [{}] 采集成功,数据完整", equName);
}
}
// 如果所有设备都失败
if (validSuccessList.isEmpty()) {
log.error("本次采集失败,所有设备采集数据不完整或采集失败,保持旧数据完好。失败设备列表:{}",
failed.stream()
.map(f -> f.get("equName"))
.collect(Collectors.joining(", ")));
throw new RuntimeException("本次采集失败,所有设备采集数据不完整或采集失败!");
}
return new ValidationResult(validSuccessList, failed);
}
}

@ -52,6 +52,7 @@ public class MemoryToolServiceImpl implements MemoryToolService {
// 2. 临时存放单值字段
Map<String, String> equGatherFields = new HashMap<>();
Map<String, Object> updateEntities = new HashMap<>();
// 3. 遍历 snmpResults
Map<String, List<Object>> listEntitiesMap = new HashMap<>(); // key: 表名, value: 实体列表
@ -59,6 +60,22 @@ public class MemoryToolServiceImpl implements MemoryToolService {
String param = entry.getKey();
Object value = entry.getValue();
// 🟩 如果是 equ_base,则单独处理为更新型实体
if ("equ_base".equalsIgnoreCase(param)) {
if (value instanceof Map) {
Map<String, Object> row = (Map<String, Object>) value;
Object entity = createEntityForTable("equ_base", row);
if (entity != null) {
// 填入设备ID(更新时使用)
fillField(entity, "equ_base", "id", device.getId());
updateEntities.put("equ_base", entity);
}
}
continue; // 跳过普通插入逻辑
}
//插入行实体
if (value instanceof List) {
// 处理 List 类型,转换成实体对象,表名直接用 param
@ -102,7 +119,7 @@ public class MemoryToolServiceImpl implements MemoryToolService {
GatherEntities result = new GatherEntities();
result.setEquGather(equGather);
result.getSubEntities().putAll(listEntitiesMap);
result.getUpdateEntities().putAll(updateEntities);
return result;
}
@ -156,7 +173,34 @@ public class MemoryToolServiceImpl implements MemoryToolService {
}
}
public void insertEntityList(String tableName, List<Object> entities){
if (entities == null || entities.isEmpty()) {
throw new IllegalArgumentException("批量插入实体列表为空,表:" + tableName);
}
try {
switch (tableName) {
case "equ_interface":
equInterfaceMapper.insertBatch((List<EquInterface>)(List<?>) entities);
break;
case "equ_routing":
equRoutingMapper.insertBatchRouting((List<EquRouting>)(List<?>) entities);
break;
case "equ_veneer":
equVeneerMapper.insertBatchVeneer((List<EquVeneer>)(List<?>) entities);
break;
case "equ_vlan":
equVlanMapper.insertBatchVlan((List<EquVlan>)(List<?>) entities);
break;
default:
throw new UnsupportedOperationException("未实现的表批量插入:" + tableName);
}
} catch (Exception e) {
// 打印日志并抛异常,保证事务回滚
log.error("批量插入表 {} 失败, 数据量: {}", tableName, entities.size(), e);
throw new RuntimeException("批量插入表 " + tableName + " 失败", e);
}
}
/**
* 根据表名和字段生成对应实体对象
@ -182,6 +226,10 @@ public class MemoryToolServiceImpl implements MemoryToolService {
EquVlan equVlan=new EquVlan();
rowData.forEach((k, v) -> fillField(equVlan, tableName, k, v != null ? v.toString() : null));
return equVlan;
case "equ_base":
EquBase equBase =new EquBase();
rowData.forEach((k, v) -> fillField(equBase, tableName, k, v != null ? v.toString() : null));
return equBase;
// 可以继续加其他表
default:
return null;
@ -191,27 +239,30 @@ public class MemoryToolServiceImpl implements MemoryToolService {
/**
* 批量插入数据库按表名分发到对应 Mapper
*/
private void insertEntityList(String tableName, List<Object> entities) {
if (entities.isEmpty()) return;
switch (tableName) {
case "equ_interface":
equInterfaceMapper.insertBatch((List<EquInterface>)(List<?>) entities);
break;
case "equ_routing":
equRoutingMapper.insertBatchRouting((List<EquRouting>)(List<?>) entities);
break;
case "equ_veneer":
equVeneerMapper.insertBatchVeneer((List<EquVeneer>)(List<?>) entities);
break;
case "equ_vlan":
equVlanMapper.insertBatchVlan((List<EquVlan>)(List<?>) entities);
break;
// 其他表继续添加
}
}
// private void insertEntityList(String tableName, List<Object> entities) {
// if (entities.isEmpty()) return;
//
// switch (tableName) {
// case "equ_interface":
// equInterfaceMapper.insertBatch((List<EquInterface>)(List<?>) entities);
// break;
// case "equ_routing":
//
// equRoutingMapper.insertBatchRouting((List<EquRouting>)(List<?>) entities);
// break;
// case "equ_veneer":
//
// equVeneerMapper.insertBatchVeneer((List<EquVeneer>)(List<?>) entities);
// break;
// case "equ_vlan":
// equVlanMapper.insertBatchVlan((List<EquVlan>)(List<?>) entities);
// break;
// // 其他表继续添加
// }
// }
/**
* 根据表名和字段名给实体赋值
@ -222,9 +273,9 @@ public class MemoryToolServiceImpl implements MemoryToolService {
* @param value 要赋的值String 类型可根据属性类型转换
*/
public void fillField(Object entity, String tableName, String fieldName, String value) {
if (entity == null || fieldName == null || value == null) {
return;
}
// if (entity == null || fieldName == null || value == null) {
// throw new IllegalArgumentException("实体对象、字段名或值不能为空");
// }
try {
Field field = null;
@ -241,7 +292,8 @@ public class MemoryToolServiceImpl implements MemoryToolService {
} catch (NoSuchFieldException ignored) {
log.warn("实体 {} 没有字段 {}(驼峰:{}),表名 {}",
clazz.getSimpleName(), fieldName, camelName, tableName);
return; // 找不到就直接返回
throw new RuntimeException(String.format("实体 %s 没有字段 %s(驼峰:%s),表名 %s",
clazz.getSimpleName(), fieldName, camelName, tableName));
}
}
@ -270,6 +322,8 @@ public class MemoryToolServiceImpl implements MemoryToolService {
} catch (Exception e) {
log.error("实体 {} 字段 {} 赋值失败: {}", entity.getClass().getSimpleName(), fieldName, e.getMessage(), e);
// throw new RuntimeException(String.format("实体 %s 字段 %s 赋值失败: %s",
// entity.getClass().getSimpleName(), fieldName, e.getMessage()), e);
}
}

@ -21,6 +21,7 @@ import org.quartz.SchedulerException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@ -50,6 +51,15 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
@Autowired
private IAlertLaunchService iAlertLaunchService;
@Autowired
private EquInterfaceMapper equInterfaceMapper;
@Autowired
private EquRoutingMapper equRoutingMapper;
@Autowired
private EquVeneerMapper equVeneerMapper;
@Autowired
private EquVlanMapper equVlanMapper;
@Autowired
private IQuartzService quartzService;
@ -57,9 +67,16 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
private MemoryToolService memoryToolService;
@Autowired
private IAlertSenderService iAlertSenderService;
@Autowired
public GatherValidator gatherValidator;
private static final Logger log = LoggerFactory.getLogger(ScheduleRulesServiceImpl.class);
private final JdbcTemplate jdbcTemplate;
// 构造器注入
public ScheduleRulesServiceImpl(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
private static final Logger log = LoggerFactory.getLogger(ScheduleRulesServiceImpl.class);
@Override
@ -99,34 +116,7 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
public Boolean executeRule(ScheduleRules newRule) throws SchedulerException, ExecutionException, InterruptedException {
// // 1. 获取所有正在执行(1)和待执行(0)的规则
// List<ScheduleRules> runningOrWaiting = scheduleRulesMapper.selectList();
// if (runningOrWaiting!=null) {
// for (ScheduleRules rule : runningOrWaiting) {
// switch (rule.getStatus()) {
// case 1: // 正在执行
// log.info("规则[{}]:正在执行上一个任务,等待上一个任务完成……", rule.getId());
// waitTaskFinish(rule.getId());
// log.info("规则[{}]:完成上一个任务任务,开始新的任务…", rule.getId());
// if (rule.getRuleType() == 2 || rule.getRuleType() == 1) {
// rule.setStatus(2);
// } else if (rule.getRuleType() == 0) {
// rule.setStatus(4);
// }
// scheduleRulesMapper.updateScheduleRules(rule);
// break;
// case 0: // 待执行
// if (rule.getRuleType() == 0 || rule.getRuleType() == 1) {
// quartzService.deleteJob(rule.getId());
// }
// rule.setStatus(4);
// scheduleRulesMapper.updateScheduleRules(rule);
// break;
// default:
// break;
// }
// }
// }
// 2. 执行新规则
try {
Date now = new Date();
@ -151,7 +141,7 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
scheduleRulesMapper.updateScheduleRules(newRule);
String content = String.format("立即任务执行完成", newRule.getRuleName());
iAlertSenderService.sendInternalMessage("采集任务成功",content, "2");
iAlertSenderService.sendInternalMessage("采集任务整体执行成功",content, "2");
} catch (Exception e) {
newRule.setStatus(3); // 执行失败
@ -159,7 +149,7 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
String content = String.format("立即任务执行失败:%s", e.getMessage());
try {
iAlertSenderService.sendInternalMessage("采集任务失败",content, "2");
iAlertSenderService.sendInternalMessage("采集任务整体执行失败",content, "2");
} catch (Exception ne) {
log.error("发送站内信失败: {}", ne.getMessage(), ne);
}
@ -192,96 +182,8 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
}
}
/**
* 删除所有设备相关表数据保证事务一致性
*/
@Transactional(rollbackFor = Exception.class)
public void deleteAllEquipmentData() {
equBaseMapper.deleteEquInterface();
equBaseMapper.deleteEquRouting();
equBaseMapper.deleteEquVeneer();
equBaseMapper.deleteEquVlan();
}
//往上抛异常
// @Override
// public int executeImmediately() throws ExecutionException, InterruptedException {
// // 删除之前的信息
// deleteAllEquipmentData();
//
// List<EquBase> devices = equBaseMapper.findEquBaseList();
// if (devices.isEmpty()) return Code.FAIL;
//
// Map<String, List<EquOid>> oidMap = new HashMap<>();
// for (EquBase dev : devices) {
// oidMap.put(dev.getEquManuId(), equOidMapper.findByManuIdAndStatus(dev.getEquManuId()));
// }
//
// ExecutorService executor = Executors.newFixedThreadPool(10);
// List<Future<GatherEntities>> futures = new ArrayList<>();
//
// for (EquBase device : devices) {
// futures.add(executor.submit(() -> {
// long start = System.currentTimeMillis();
//
// //获取对应oid
// List<EquOid> oids = oidMap.getOrDefault(device.getEquManuId(), Collections.emptyList());
// if (oids.isEmpty()) {
// System.err.println("⚠ 设备[" + device.getName() + "]未配置OID,跳过");
// return null;
// }
// //采集对应结果
// Map<String, Object> snmpResults = snmpService.query(device.getIp(), oids);
// if (snmpResults == null || snmpResults.isEmpty()) {
// System.err.println("⚠ 设备[" + device.getName() + "] SNMP采集为空,跳过");
// return null;
// }
//
// long afterSnmp = System.currentTimeMillis();
// System.out.println("SNMP查询耗时: " + (afterSnmp - start) + " ms");
// //生成对应实体
// GatherEntities entities = memoryToolService.buildAndInsertEntity(device, snmpResults);
//
// if (entities == null || entities.getEquGather() == null) {
// System.err.println("⚠ 设备[" + device.getName() + "] 构建实体失败,跳过");
// return null;
// }
//
// long afterBuild = System.currentTimeMillis();
// System.out.println("buildAndInsertEntity耗时: " + (afterBuild - afterSnmp) + " ms");
//
// //构建健康度
// if (entities != null && entities.getEquGather() != null) {
// Double temperature = parseDouble(snmpResults.get("temperature"));
// Double cpuUsage = parseDouble(snmpResults.get("cpu_utilization"));
// Double memUsage = parseDouble(snmpResults.get("memory_utilization"));
// double tempHealth = calculateTemperatureHealth(temperature);
// double cpuHealth = calculateCpuHealth(cpuUsage);
// double memHealth = calculateMemoryHealth(memUsage);
// double totalHealth = Math.max(Math.max(tempHealth, 1), Math.max(cpuHealth, memHealth));
// entities.getEquGather().setHealth(BigDecimal.valueOf(totalHealth));
// return entities;
// }
// return null;
// }));
// }
// //收集采集结果,批量存入数据库
// List<GatherEntities> allEntities = new ArrayList<>();
// try {
// for (Future<GatherEntities> f : futures) {
// GatherEntities g = f.get(); // 阻塞等待线程完成,如果线程异常会抛ExecutionException
// if (g == null) throw new RuntimeException("设备采集返回 null,可能采集失败");
// allEntities.add(g);
// }
// } catch (ExecutionException | InterruptedException e) {
// e.printStackTrace();
// executor.shutdownNow(); // 出异常直接中断剩余线程
// throw e; // 抛给调用方 Job 捕获
// } finally {
// executor.shutdown();
// }
// return persistAndUpdate(allEntities);
//
// }
@Transactional(rollbackFor = Exception.class)
@Override
@ -293,12 +195,11 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
return Code.FAIL;
}
// 构建设备 OID 映射
// 构建设备 OID 映射(插入)
Map<String, List<EquOid>> oidMap = new HashMap<>();
for (EquBase dev : devices) {
oidMap.put(dev.getEquManuId(), equOidMapper.findByManuIdAndStatus(dev.getEquManuId()));
}
// 并发采集
ExecutorService executor = Executors.newFixedThreadPool(10);
List<Future<GatherEntities>> futures = new ArrayList<>();
@ -345,130 +246,189 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
// 收集采集结果
List<GatherEntities> successList = new ArrayList<>();
List<String> failed = new ArrayList<>();
List<Map<String, String>> failed = new ArrayList<>(); // 包含设备ID和设备名称
for (int i = 0; i < futures.size(); i++) {
EquBase dev = devices.get(i);
try {
GatherEntities g = futures.get(i).get();
if (g != null) successList.add(g);
else failed.add(dev.getName());
else failed.add(Map.of("equId", dev.getId(), "equName", dev.getName()));
} catch (Exception e) {
failed.add(dev.getName());
failed.add(Map.of("equId", dev.getId(), "equName", dev.getName()));
log.error("收集设备 [{}] 结果失败", dev.getName(), e);
}
}
//关闭线程池
executor.shutdown();
// 过滤 successList 中不完整的设备,记录失败设备,equ_gather和equ_base只要有数据就行
GatherValidator.ValidationResult validationResult =
gatherValidator.validateBeforeDbOperation(successList, failed, oidMap);
// // ✅ 完整性校验:找出所有子表为空的设备
// List<String> invalidDevices = successList.stream()
// .filter(g -> g.getSubEntities() == null
// || g.getSubEntities().isEmpty()
// || g.getSubEntities().values().stream().anyMatch(list -> list == null || list.isEmpty()))
// .map(g -> g.getEquGather().getEquId())
// .collect(Collectors.toList());
//
// // 如果全部失败或者发现不完整设备,抛异常
// if (successList.isEmpty() || !invalidDevices.isEmpty()) {
// String msg = "本次采集失败,保持旧数据完好。失败设备:" +
// String.join(", ", failed) +
// (invalidDevices.isEmpty() ? "" : ",采集数据不完整设备:" + String.join(", ", invalidDevices));
// log.error(msg);
// throw new RuntimeException(msg); // 异常抛出去,外层可捕获发送邮件
// }
// ✅ 完整性校验:找出所有子表为空的设备及对应子表
Map<String, List<String>> invalidDeviceMap = successList.stream()
.map(g -> {
String equId = g.getEquGather().getEquId();
// 找出这个设备下不完整的子表
List<String> emptyTables = new ArrayList<>();
List<String> failedEquIds = validationResult.getFailed().stream()
.map(f -> f.get("equId"))
.collect(Collectors.toList());
// subEntities 为空或没有数据
if (g.getSubEntities() == null || g.getSubEntities().isEmpty()) {
emptyTables.add("所有子表为空");
} else {
// 检查每个子表的值
g.getSubEntities().forEach((tableName, list) -> {
if (list == null || list.isEmpty()) {
emptyTables.add(tableName);
}
});
}
// 事务内:删除成功设备依附表 + 插入成功设备数据 + 执行预警
deleteAllEquipmentData(failedEquIds); // 删除依附表,不含 equ_gather
// 如果有任何空子表,返回 (设备ID, 空表列表)
return emptyTables.isEmpty() ? null : Map.entry(equId, emptyTables);
})
.filter(Objects::nonNull)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
// 插入成功设备数据(内部失败会抛异常,触发事务回滚)
persistAndUpdate(successList);
// ✅ 提取不完整设备ID列表
List<String> invalidDevices = new ArrayList<>(invalidDeviceMap.keySet());
// ✅ 如果全部失败或有不完整设备,抛异常
if (successList.isEmpty() || !invalidDevices.isEmpty()) {
StringBuilder msg = new StringBuilder("本次采集失败,保持旧数据完好。");
// 发送成功日志
log.info("本次采集成功 {} 台,删除旧表并写入最新数据,失败设备:{}", successList.size(), failed);
if (!failed.isEmpty()) {
msg.append("失败设备:").append(String.join(", ", failed));
return Code.SUCCESS;
}
if (!invalidDeviceMap.isEmpty()) {
msg.append(";采集数据不完整设备如下:");
invalidDeviceMap.forEach((equId, tables) ->
msg.append("\n设备[").append(equId).append("] 缺少表:")
.append(String.join(", ", tables)));
@Transactional(rollbackFor = Exception.class)
public void deleteAllEquipmentData(List<String> failedEquIds) {
// 1 查询失败设备最新采集ID
List<String> excludeGatherIds = failedEquIds == null || failedEquIds.isEmpty() ?
Collections.emptyList() : equGatherMapper.selectLatestGatherIdsByEquIds(failedEquIds);
// 2 依附表列表
String[] dependentTables = {"equ_interface", "equ_routing", "equ_veneer", "equ_vlan"};
for (String table : dependentTables) {
int deleted;
if (excludeGatherIds.isEmpty()) {
// 没有失败设备,直接清空表
deleted = jdbcTemplate.update("DELETE FROM " + table);
} else {
// 使用占位符方式安全删除
String inSql = excludeGatherIds.stream().map(id -> "?").collect(Collectors.joining(","));
String sql = String.format("DELETE FROM %s WHERE equ_gather_id NOT IN (%s)", table, inSql);
deleted = jdbcTemplate.update(sql, excludeGatherIds.toArray());
}
log.error(msg.toString());
throw new RuntimeException(msg.toString());
log.info("表 {} 删除 {} 条数据,保留失败设备采集ID: {}", table, deleted, excludeGatherIds);
}
// 事务内:删除旧表 + 插入成功设备数据+执行预警
deleteAllEquipmentData(); // 删除依附表,不含 equ_gather
int inserted = persistAndUpdate(successList);
if (inserted <= 0) {
throw new RuntimeException("成功采集设备写入失败,回滚事务");
}
//发送一个采集成功的信
log.info("本次采集成功 {} 台,删除旧表并写入最新数据,失败设备:{}", successList.size(), failed);
return Code.SUCCESS;
log.info("依附表数据删除完成,共保留 {} 个失败设备最新采集ID", excludeGatherIds.size());
}
/**
* 批量落库更新采集次数执行最新预警
* 事务保证如果任何步骤失败整个操作回滚
*/
@Transactional(rollbackFor = Exception.class)
public int persistAndUpdate(List<GatherEntities> allEntities) throws ExecutionException, InterruptedException {
public int persistAndUpdate(List<GatherEntities> allEntities) {
if (allEntities == null || allEntities.isEmpty()) {
return 0;
throw new RuntimeException("待插入实体为空,无法落库");
}
try {
// ===== 1. 主表批量插入 =====
List<EquGather> gatherList = allEntities.stream()
.map(GatherEntities::getEquGather)
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (!gatherList.isEmpty()) {
equGatherMapper.batchInsertEquGather(gatherList);
log.info("主表批量插入完成,数量:{}", gatherList.size());
}
// ===== 2. 子表按表名分组批量插入 =====
Map<String, List<Object>> groupedSubEntities = new HashMap<>();
for (GatherEntities g : allEntities) {
g.getSubEntities().forEach((table, list) -> {
if (list != null && !list.isEmpty()) {
groupedSubEntities.computeIfAbsent(table, k -> new ArrayList<>()).addAll(list);
}
});
}
// 批量落库
long persistStart = System.currentTimeMillis();
int result = memoryToolService.persistAll(allEntities);
long persistEnd = System.currentTimeMillis();
System.out.println("批量落库耗时: " + (persistEnd - persistStart) + " ms");
for (Map.Entry<String, List<Object>> entry : groupedSubEntities.entrySet()) {
String tableName = entry.getKey();
List<Object> list = entry.getValue();
insertEntityList(tableName, list); // 内部异常直接抛出
log.info("子表 {} 批量插入完成,数量:{}", tableName, list.size());
}
// 更新采集次数
Set<String> idSet = allEntities.stream()
.filter(g -> g.getEquGather() != null)
.map(g -> g.getEquGather().getEquId())
// ===== 3. 更新 equ_base =====
List<EquBase> updateList = allEntities.stream()
.map(GatherEntities::getUpdateEntities)
.filter(map -> map != null && map.get("equ_base") != null)
.map(map -> (EquBase) map.get("equ_base"))
.filter(Objects::nonNull)
.collect(Collectors.toList());
if (!updateList.isEmpty()) {
equBaseMapper.updateEquBaseVersionBatch(updateList);
log.info("equ_base 批量更新完成,数量:{}", updateList.size());
}
// ===== 4. 更新采集次数 =====
Set<String> equIds = allEntities.stream()
.map(GatherEntities::getEquGather)
.filter(Objects::nonNull)
.map(EquGather::getEquId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
if (!idSet.isEmpty()) {
equBaseMapper.incrementCollectCount(new ArrayList<>(idSet));
if (!equIds.isEmpty()) {
equBaseMapper.incrementCollectCount(new ArrayList<>(equIds));
log.info("采集次数更新完成,设备数量:{}", equIds.size());
}
// 执行最新预警
// ===== 5. 执行预警(容错,不影响主事务) =====
try {
iAlertLaunchService.executeImmediateAlerts();
} catch (Exception e) {
log.error("预警执行异常,不影响采集事务", e);
}
log.info("采集完一次!!!");
return result;
log.info("批量落库、更新完成,共处理设备:{}", allEntities.size());
return allEntities.size();
} catch (Exception e) {
log.error("批量落库失败,事务回滚", e);
throw new RuntimeException("批量落库失败,事务回滚", e);
}
}
// @Transactional(rollbackFor = Exception.class)
// public int persistAndUpdate(List<GatherEntities> allEntities) throws ExecutionException, InterruptedException {
// if (allEntities == null || allEntities.isEmpty()) {
// return 0;
// }
//
// // 批量落库
//
// int result = memoryToolService.persistAll(allEntities);
//
// //批量跟新
// List<EquBase> updateList = allEntities.stream()
// .map(GatherEntities::getUpdateEntities)
// .filter(map -> map != null && map.get("equ_base") != null)
// .map(map -> (EquBase) map.get("equ_base"))
// .filter(Objects::nonNull)
// .collect(Collectors.toList());
//
// if (!updateList.isEmpty()) {
// equBaseMapper.updateEquBaseVersionBatch(updateList);
// }
//
// // 更新采集次数
// Set<String> idSet = allEntities.stream()
// .filter(g -> g.getEquGather() != null)
// .map(g -> g.getEquGather().getEquId())
// .filter(Objects::nonNull)
// .collect(Collectors.toSet());
// if (!idSet.isEmpty()) {
// equBaseMapper.incrementCollectCount(new ArrayList<>(idSet));
// }
//
// try {
// iAlertLaunchService.executeImmediateAlerts(); // 内部规则级容错
// } catch (Exception e) {
// log.error("预警执行异常,统计预警次数出现错误,不影响采集", e);
// }
// log.info("采集完一次!!!");
// return result;
// }
// @Override
// public int executeImmediately() throws ExecutionException, InterruptedException {
@ -564,6 +524,37 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
//
// }
public void insertEntityList(String tableName, List<Object> entities){
if (entities == null || entities.isEmpty()) {
throw new IllegalArgumentException("批量插入实体列表为空,表:" + tableName);
}
try {
switch (tableName) {
case "equ_interface":
equInterfaceMapper.insertBatch((List<EquInterface>)(List<?>) entities);
break;
case "equ_routing":
equRoutingMapper.insertBatchRouting((List<EquRouting>)(List<?>) entities);
break;
case "equ_veneer":
equVeneerMapper.insertBatchVeneer((List<EquVeneer>)(List<?>) entities);
break;
case "equ_vlan":
equVlanMapper.insertBatchVlan((List<EquVlan>)(List<?>) entities);
break;
default:
throw new UnsupportedOperationException("未实现的表批量插入:" + tableName);
}
} catch (Exception e) {
// 打印日志并抛异常,保证事务回滚
log.error("批量插入表 {} 失败, 数据量: {}", tableName, entities.size(), e);
throw new RuntimeException("批量插入表 " + tableName + " 失败", e);
}
}
// 健康度计算函数示例
private int calculateTemperatureHealth(Double temp) {
if (temp == null) return 4; // 无数据视为需维护

@ -77,6 +77,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</collection>
</resultMap>
<select id="selectEquNameById" parameterType="string" resultType="string">
SELECT name
FROM equ_base
WHERE id = #{equId}
</select>
<update id="updateEquBaseVersionBatch" parameterType="java.util.List">
<foreach collection="list" item="item" separator=";">
UPDATE equ_base
<set>
<if test="version != null">
version = #{item.version}
</if>
</set>
WHERE id = #{item.id}
</foreach>
</update>
<sql id="selectEquBaseVo">
select id,equ_number, name, version, ip, manufacture, location, equ_type, in_charge, install_date, life_cycle, warranty, create_by, create_date, status, device_level, supplier_id from equ_base
</sql>
@ -216,10 +235,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<where>
<if test="minTime != null">
AND b.create_date &gt;= #{minTime}
AND b.install_date &gt;= #{minTime}
</if>
<if test="maxTime != null">
AND b.create_date &lt;= #{maxTime}
AND b.install_date &lt;= #{maxTime}
</if>
<!-- 模糊查询:设备名称 -->
@ -231,6 +250,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="location != null and location != ''">
AND b.location LIKE CONCAT('%', #{location}, '%')
</if>
<if test="floor != null and floor != ''">
AND b.floor = #{floor}
</if>
<!-- 精确查询:状态 -->
<if test="status != null and status != ''">

@ -26,6 +26,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</resultMap>
<select id="selectLatestGatherIdsByEquIds" resultType="String">
SELECT id
FROM equ_gather g
WHERE equ_id IN
<foreach item="id" collection="equIds" open="(" separator="," close=")">
#{id}
</foreach>
AND gather_time = (
SELECT MAX(gather_time)
FROM equ_gather t
WHERE t.equ_id = g.equ_id
)
</select>
<!-- updateHealth 方法 -->
<update id="updateHealth">
UPDATE equ_gather

@ -65,9 +65,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
update_time AS updateTime
FROM equ_oid
WHERE manufacturer_id = #{manufacturerId}
AND enabled = 2
AND enabled IN (1, 2)
ORDER BY id
</select>
<select id="selectEquOidById" parameterType="Long" resultMap="EquOidResult">
<include refid="selectEquOidVo"/>
where id = #{id}

Loading…
Cancel
Save