# Conflicts:
#	ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/exceptionController/AlertMessageController.java
dev
lzh1232 8 months ago
commit e096d8e458
  1. 8
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/equManagement/EquBaseController.java
  2. 3
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/equManagement/ScheduleRulesController.java
  3. 13
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/exceptionController/AlertMessageController.java
  4. 10
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/exceptionController/AlertRuleController.java
  5. 2
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/exceptionController/equAssessment.java
  6. 2
      ALOps_sys_backend/alops-admin/src/main/java/com/alops/web/controller/message/messageController.java
  7. 26
      ALOps_sys_backend/alops-chat/src/main/java/com/alops/chat/service/AItokenService.java
  8. 2
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/domain/dto/AlertDto.java
  9. 211
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/domain/vo/AlertMessages.java
  10. 3
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/job/GatherJob.java
  11. 3
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/mapper/AlertMessageMapper.java
  12. 3
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/IAlertMessageService.java
  13. 3
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/AlertMessageServiceImpl.java
  14. 6
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/EquBaseServiceImpl.java
  15. 87
      ALOps_sys_backend/alops-system/src/main/java/com/alops/system/service/impl/ScheduleRulesServiceImpl.java
  16. 23
      ALOps_sys_backend/alops-system/src/main/resources/mapper/system/AlertMessageMapper.xml
  17. 2
      ALOps_sys_fe/alops-ui/.env.development
  18. 2
      ALOps_sys_fe/alops-ui/.env.production
  19. 2
      ALOps_sys_fe/alops-ui/package.json
  20. 3
      ALOps_sys_fe/alops-ui/public/index.html
  21. 2
      ALOps_sys_fe/alops-ui/src/layout/components/Sidebar/Logo.vue
  22. 2
      ALOps_sys_fe/alops-ui/src/utils/websocket.js
  23. 6
      ALOps_sys_fe/alops-ui/src/views/QandA/Aiassistant/index.vue
  24. 45
      ALOps_sys_fe/alops-ui/src/views/QandA/knowledge/index.vue
  25. 1
      ALOps_sys_fe/alops-ui/src/views/device/deviceInformation/index.vue
  26. 12
      ALOps_sys_fe/alops-ui/src/views/device/deviceManufacturer/index.vue
  27. 4
      ALOps_sys_fe/alops-ui/src/views/device/deviceType/index.vue
  28. 15
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/devicesState/cardItem.vue
  29. 778
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/devicesState/detail.vue
  30. 26
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/inMonitor/message/index.vue
  31. 6
      ALOps_sys_fe/alops-ui/src/views/inMonitoring/inMonitor/rules/index.vue
  32. 6
      ALOps_sys_fe/alops-ui/src/views/index.vue
  33. 4
      ALOps_sys_fe/alops-ui/src/views/message/index.vue

@ -50,6 +50,7 @@ public class EquBaseController extends BaseController
@PreAuthorize("@ss.hasPermi('device:files:list')")
@GetMapping("/list")
@ApiOperation("查询多个设备信息")
@Log(title = "设备档案查询", businessType = BusinessType.OTHER)
public TableDataInfo list( EquBaseQueryDto equBase)
{
startPage();
@ -152,6 +153,7 @@ public class EquBaseController extends BaseController
@PreAuthorize("@ss.hasPermi('device:files:query1')")
@GetMapping(value = "/{id}")
@ApiOperation("查询单个设备信息")
@Log(title = "单个设备查询", businessType = BusinessType.OTHER)
public AjaxResult getInfo(@PathVariable("id") String id)
{
return success(equBaseService.selectEquBaseById(id));
@ -226,7 +228,7 @@ public class EquBaseController extends BaseController
* MultipartFile file
*/
@PreAuthorize("@ss.hasPermi('device:files:add')")
@Log(title = "设备基本信息", businessType = BusinessType.INSERT)
@Log(title = "增加设备档案信息", businessType = BusinessType.INSERT)
@PostMapping(value = "/add")
@ApiOperation("增加设备档案信息")
public AjaxResult add(@RequestBody EquBase equBase) {
@ -249,7 +251,7 @@ public class EquBaseController extends BaseController
* 修改设备基本信息
*/
@PreAuthorize("@ss.hasPermi('device:files:edit')")
@Log(title = "设备基本信息", businessType = BusinessType.UPDATE)
@Log(title = "更新设备档案基本信息", businessType = BusinessType.UPDATE)
@PutMapping("/modify")
@ApiOperation("修改设备档案信息")
public AjaxResult edit(@RequestBody EquBase equBase){
@ -268,7 +270,7 @@ public class EquBaseController extends BaseController
* 删除设备基本信息
*/
@PreAuthorize("@ss.hasPermi('device:files:remove')")
@Log(title = "设备基本信息", businessType = BusinessType.DELETE)
@Log(title = "删除设备档案基本信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
@ApiOperation("删除设备档案信息")
public AjaxResult remove(@PathVariable String[] ids)

@ -61,7 +61,7 @@ public class ScheduleRulesController extends BaseController
@PreAuthorize("@ss.hasPermi('device:deviceInformation:protoCollect')")
@PostMapping("/addRule")
@ApiOperation("立即确定")
@Log(title = "设备信息采集", businessType = BusinessType.OTHER)
public AjaxResult run(@RequestBody ScheduleRules newRule) throws SchedulerException, ExecutionException, InterruptedException {
// 先检查是否有任务正在执行
@ -99,6 +99,7 @@ public class ScheduleRulesController extends BaseController
*/
@PreAuthorize("@ss.hasPermi('equipment:rules:list')")
@GetMapping("/list")
@Log(title = "设备信息采集基本信息", businessType = BusinessType.OTHER)
public TableDataInfo list(ScheduleRules scheduleRules)
{
startPage();

@ -10,6 +10,7 @@ import com.alops.common.core.page.TableDataInfo;
import com.alops.common.enums.BusinessType;
import com.alops.system.domain.AlertMessage;
import com.alops.system.domain.dto.AlertDto;
import com.alops.system.domain.vo.AlertMessages;
import com.alops.system.domain.vo.homeEquAlertMessageVo;
import com.alops.system.service.IAlertLaunchService;
import com.alops.system.domain.dto.AlertStatisticDto;
@ -79,7 +80,7 @@ public class AlertMessageController extends BaseController
@PreAuthorize("@ss.hasPermi('inMonitoring:alertMessage:statistic:list')")
@GetMapping ("/statistic/list")
@ApiOperation("首页查询预警信息")
@Log(title = "查询预警信息", businessType = BusinessType.SELECT)
@Log(title = "查询预警信息", businessType = BusinessType.OTHER)
public TableDataInfo homeList()
{
startPage();
@ -94,11 +95,11 @@ public class AlertMessageController extends BaseController
@PreAuthorize("@ss.hasPermi('inMonitoring:alertMessage:list')")
@GetMapping ("/list")
@ApiOperation("查询预警信息")
@Log(title = "查询预警信息", businessType = BusinessType.UPDATE)
@Log(title = "查询预警信息", businessType = BusinessType.OTHER)
public TableDataInfo list( AlertDto alertMessage)
{
startPage();
List<AlertMessage> list = alertMessageService.selectAlertMessageList(alertMessage);
List<AlertMessages> list = alertMessageService.selectAlertMessageList(alertMessage);
return getDataTable(list);
}
@ -107,7 +108,6 @@ public class AlertMessageController extends BaseController
* 查询界面预警信息+++
*/
@PreAuthorize("@ss.hasPermi('inMonitoring:alertMessage:listByEquId')")
@Log(title = "界面预警信息", businessType = BusinessType.SELECT)
@GetMapping("/{equId}")
@ApiOperation("获取界面ALlertMessage信息")
public TableDataInfo listByEquId(@PathVariable("equId")String equId)
@ -122,7 +122,6 @@ public class AlertMessageController extends BaseController
* 统计预警次数信息
*/
@PreAuthorize("@ss.hasPermi('inMonitoring:alertMessage:statistic')")
@Log(title = "界面预警信息", businessType = BusinessType.SELECT)
@GetMapping("/times")
@ApiOperation("统计预警次数信息")
public AjaxResult getAlertStatistic() {
@ -133,9 +132,9 @@ public class AlertMessageController extends BaseController
/**
* 删除预警信息
*/
@PreAuthorize("@ss.hasPermi('system:message:remove')")
@PreAuthorize("@ss.hasPermi('inMonitoring:alertMessage:remove')")
@ApiOperation("删除预警信息")
@Log(title = "删除预警信息", businessType = BusinessType.UPDATE)
@Log(title = "删除预警信息", businessType = BusinessType.DELETE)
@DeleteMapping("/{ids}")
public AjaxResult remove(@PathVariable Long[] ids)
{

@ -50,7 +50,7 @@ public class AlertRuleController extends BaseController {
*/
@PreAuthorize("@ss.hasPermi('inMonitoring:alertRule:add')")
@ApiOperation("增加预警规则")
@Log(title = "【增加预警规则】", businessType = BusinessType.DELETE)
@Log(title = "【增加预警规则】", businessType = BusinessType.INSERT)
@PostMapping("/addRule")
public AjaxResult addRule(@RequestBody AlertRuleAddDto dto) {
try {
@ -69,7 +69,7 @@ public class AlertRuleController extends BaseController {
*/
@PreAuthorize("@ss.hasPermi('inMonitoring:alertRule:edit')")
@ApiOperation("修改预警规则")
@Log(title = "【修改预警规则】", businessType = BusinessType.DELETE)
@Log(title = "【修改预警规则】", businessType = BusinessType.UPDATE)
@PutMapping("/editRule")
public AjaxResult editRule(@RequestBody AlertRuleAddDto dto) {
try {
@ -86,7 +86,7 @@ public class AlertRuleController extends BaseController {
*/
@PreAuthorize("@ss.hasPermi('inMonitoring:alertRule:list')")
@GetMapping("/list")
@Log(title = "【查询预警规则】", businessType = BusinessType.DELETE)
@Log(title = "【查询预警规则】", businessType = BusinessType.OTHER)
@ApiOperation("查询预警规则")
public TableDataInfo list(AlertRule alertRule) {
startPage();
@ -102,7 +102,7 @@ public class AlertRuleController extends BaseController {
@PreAuthorize("@ss.hasPermi('inMonitoring:alertRule:query')")
@GetMapping(value = "/{id}")
@ApiOperation("查询预警详细规则")
@Log(title = "【查询预警详细规则】", businessType = BusinessType.DELETE)
@Log(title = "【查询预警详细规则】", businessType = BusinessType.OTHER)
public AjaxResult getInfo(@PathVariable("id") Long id) {
return success(alertRuleService.selectDetailAlertRuleById(id));
}
@ -139,7 +139,7 @@ public class AlertRuleController extends BaseController {
@ApiOperation("获取预警参数")
@PreAuthorize("@ss.hasPermi('inMonitoring:alertRule:byTable')")
@GetMapping("/byTable")
@Log(title = "【获取预警参数】", businessType = BusinessType.EXPORT)
@Log(title = "【获取预警参数】", businessType = BusinessType.OTHER)
public AjaxResult getParamsByTableName() {
return AjaxResult.success("获取预警参数成功",dictAlarmParamService.getParamsByTableName());
}

@ -30,7 +30,7 @@ public class equAssessment extends BaseController {
@PreAuthorize("@ss.hasPermi('inMonitoring:equAssessment:list')")
@GetMapping("/list")
@ApiOperation("设备评估信息")
@Log(title = "设备评估与预测", businessType = BusinessType.UPDATE)
@Log(title = "设备评估与预测", businessType = BusinessType.OTHER)
public TableDataInfo homeList()
{
startPage();

@ -64,7 +64,7 @@ public class messageController extends BaseController {
@PreAuthorize("@ss.hasPermi('message:view')")
@ApiOperation("获取用户消息列表")
@Log(title = "消息列表")
@Log(title = "消息列表", businessType = BusinessType.OTHER)
@GetMapping("/list")
public TableDataInfo getMessageList() {
startPage();

@ -310,6 +310,32 @@ public class AItokenService {
return "AI模型正在加载中,请稍等2-3分钟后再试。如果问题持续存在,可能需要检查系统资源配置。";
}
}
// 处理"length"状态 - 表示响应因长度限制被截断
else if ("length".equals(doneReason)) {
System.out.println("第 " + attempt + " 次尝试:模型响应因长度限制被截断");
// 尝试获取已生成的内容
JsonNode messageNode = rootNode.get("message");
if (messageNode != null) {
JsonNode contentNode = messageNode.get("content");
if (contentNode != null) {
String content = contentNode.asText();
if (content != null && !content.isEmpty()) {
System.out.println("尽管响应被截断,但已获取到部分回答内容,长度: " + content.length());
return content;
}
}
}
// 如果没有获取到内容,继续重试
if (attempt < maxRetries) {
long waitTime = 3000 * attempt;
System.out.println("等待 " + (waitTime/1000) + " 秒后重试...");
Thread.sleep(waitTime);
continue;
} else {
return "AI模型响应过长被截断,请尝试提出更具体的问题。";
}
}
// 只有当done_reason为stop时才表示真正结束
else if (!"stop".equals(doneReason)) {
System.out.println("第 " + attempt + " 次尝试:模型返回未知的done_reason: " + doneReason);

@ -12,4 +12,6 @@ public class AlertDto {
private String status;
private String location;
private String inCharge;
private String name;
private String equNumber;
}

@ -0,0 +1,211 @@
package com.alops.system.domain.vo;
import java.util.Date;
import com.alops.common.annotation.Excel;
import com.alops.common.core.domain.BaseEntity;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.mail.Message;
/**
* 故障统计对象 alert_message
*
* @author ruoyi
* @date 2025-09-04
*/
@Data
public class AlertMessages
{
private static final long serialVersionUID = 1L;
/** ID */
private Long id;
/** 设备ID */
@Excel(name = "设备ID")
private String equId;
/** 预警类型(枚举表名) */
@Excel(name = "预警类型(枚举表名)")
private String type;
/** 设备类型 */
@Excel(name = "设备类型")
private String equType;
@Excel(name = "设备名称")
private String name;
@Excel(name = "设备编号")
private String equNumber;
/** 解决方案 */
@Excel(name = "解决方案")
private String solution;
/** 故障级别(1-5级) */
@Excel(name = "预警级别(1-5级)")
private Long level;
/** 发生时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "添加日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date occurTime;
/** 处理人ID */
@Excel(name = "处理人ID")
private String handler;
/** 解决时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@Excel(name = "添加日期", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")
private Date handleTime;
/** 处理状态(open、in_progress_closed) */
@Excel(name = "处理状态")
private String status;
@Excel(name = "信息批次")
private Integer batch;
@Excel(name ="预警信息")
private String message;
@JsonIgnore
@Excel(name ="预警规则id")
private Long alertRuleId;
public void setId(Long id)
{
this.id = id;
}
public Long getId()
{
return id;
}
public void setEquId(String equId)
{
this.equId = equId;
}
public String getEquId()
{
return equId;
}
public void setType(String type)
{
this.type = type;
}
public String getType()
{
return type;
}
public void setEquType(String equType)
{
this.equType = equType;
}
public String getEquType()
{
return equType;
}
public void setSolution(String solution)
{
this.solution = solution;
}
public String getSolution()
{
return solution;
}
public void setLevel(Long level)
{
this.level = level;
}
public Long getLevel()
{
return level;
}
public void setOccurTime(Date occurTime)
{
this.occurTime = occurTime;
}
public Date getOccurTime()
{
return occurTime;
}
public void setHandler(String handler)
{
this.handler = handler;
}
public String getHandler()
{
return handler;
}
public void setHandleTime(Date handleTime)
{
this.handleTime = handleTime;
}
public Date getHandleTime()
{
return handleTime;
}
public void setStatus(String status)
{
this.status = status;
}
public String getStatus()
{
return status;
}
@Override
public String toString() {
return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
.append("id", getId())
.append("equId", getEquId())
.append("type", getType())
.append("equType", getEquType())
.append("solution", getSolution())
.append("level", getLevel())
.append("occurTime", getOccurTime())
.append("handler", getHandler())
.append("handleTime", getHandleTime())
.append("status", getStatus())
.append("batch",getBatch())
.toString();
}
}

@ -43,6 +43,7 @@ public class GatherJob implements Job {
// 1.状态为5时取消调度任务
if (rule.getStatus() == 5) {
rule.setStatus(4);
scheduleRulesMapper.updateScheduleRules(rule);
Scheduler scheduler = context.getScheduler();
JobKey jobKey = context.getJobDetail().getKey();
// scheduler.deleteJob(jobKey);
@ -123,7 +124,7 @@ 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" );

@ -2,6 +2,7 @@ package com.alops.system.mapper;
import com.alops.system.domain.AlertMessage;
import com.alops.system.domain.dto.AlertDto;
import com.alops.system.domain.vo.AlertMessages;
import com.alops.system.domain.vo.homeEquAlertMessageVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@ -44,7 +45,7 @@ public interface AlertMessageMapper
* @param alertMessage 故障统计
* @return 故障统计集合
*/
public List<AlertMessage> selectAlertMessageList(AlertDto alertMessage);
public List<AlertMessages> selectAlertMessageList(AlertDto alertMessage);
public List<homeEquAlertMessageVo> selectStaticAlertMessageList();
public ArrayList<AlertMessage> selectAlertMessageByEquId(String Equid);

@ -3,6 +3,7 @@ package com.alops.system.service;
import com.alops.system.domain.AlertMessage;
import com.alops.system.domain.dto.AlertDto;
import com.alops.system.domain.dto.AlertStatisticDto;
import com.alops.system.domain.vo.AlertMessages;
import com.alops.system.domain.vo.homeEquAlertMessageVo;
import java.util.List;
@ -30,7 +31,7 @@ public interface IAlertMessageService
* @param alertMessage 故障统计
* @return 故障统计集合
*/
public List<AlertMessage> selectAlertMessageList(AlertDto alertMessage);
public List<AlertMessages> selectAlertMessageList(AlertDto alertMessage);
public List<homeEquAlertMessageVo> selectStaticAlertMessageList();
public List<AlertMessage> selectAlertMessageListByEquId(String equId);

@ -11,6 +11,7 @@ import com.alops.system.domain.dto.AlertDto;
import com.alops.system.domain.dto.AlertStatisticDto;
import com.alops.system.domain.dto.LevelCountDto;
import com.alops.system.domain.dto.TypeCountDto;
import com.alops.system.domain.vo.AlertMessages;
import com.alops.system.domain.vo.homeEquAlertMessageVo;
import com.alops.system.mapper.AlertMessageMapper;
import com.alops.system.service.IAlertMessageService;
@ -48,7 +49,7 @@ public class AlertMessageServiceImpl implements IAlertMessageService
* @return 故障统计
*/
@Override
public List<AlertMessage> selectAlertMessageList(AlertDto alertMessage)
public List<AlertMessages> selectAlertMessageList(AlertDto alertMessage)
{
return alertMessageMapper.selectAlertMessageList(alertMessage);
}

@ -87,9 +87,9 @@ public class EquBaseServiceImpl implements IEquBaseService
vo.setRiskGrade(switch (riskLevel) {
case 1 -> "无风险";
case 2 -> "低";
case 3 -> "中";
case 4 -> "高";
case 2 -> "低风险";
case 3 -> "中风险";
case 4 -> "高风险";
default -> "未知";
});

@ -205,7 +205,7 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
oidMap.put(dev.getEquManuId(), equOidMapper.findByManuIdAndStatus(dev.getEquManuId()));
}
// 并发采集
ExecutorService executor = Executors.newFixedThreadPool(10);
ExecutorService executor = Executors.newFixedThreadPool(12);
List<Future<GatherEntities>> futures = new ArrayList<>();
for (EquBase device : devices) {
futures.add(executor.submit(() -> {
@ -271,12 +271,12 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
gatherValidator.validateBeforeDbOperation(successList, failed, oidMap);
List<String> failedEquIds = validationResult.getFailed().stream()
.map(f -> f.get("equId"))
.collect(Collectors.toList());
// List<String> failedEquIds = validationResult.getFailed().stream()
// .map(f -> f.get("equId"))
// .collect(Collectors.toList());
// 事务内:删除成功设备依附表 + 插入成功设备数据 + 执行预警
deleteAllEquipmentData(failedEquIds); // 删除依附表,不含 equ_gather
deleteAllEquipmentData(validationResult.getSuccessList()); // 删除依附表,不含 equ_gather
// 插入成功设备数据(内部失败会抛异常,触发事务回滚)
persistAndUpdate(validationResult.getSuccessList());
@ -287,33 +287,64 @@ public class ScheduleRulesServiceImpl implements IScheduleRulesService {
return Code.SUCCESS;
}
@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.info("表 {} 删除 {} 条数据,保留失败设备采集ID: {}", table, deleted, excludeGatherIds);
}
// @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.info("表 {} 删除 {} 条数据,保留失败设备采集ID: {}", table, deleted, excludeGatherIds);
// }
//
// log.info("依附表数据删除完成,共保留 {} 个失败设备最新采集ID", excludeGatherIds.size());
// }
@Transactional(rollbackFor = Exception.class)
public void deleteAllEquipmentData(List<GatherEntities> successList) {
// 1 提取成功设备ID列表
List<String> successEquIds = successList.stream()
.map(g -> g.getEquGather().getEquId())
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
// 2 查询这些设备的最新采集ID(要保留的)
List<String> latestGatherIds = equGatherMapper.selectLatestGatherIdsByEquIds(successEquIds);
if (latestGatherIds == null || latestGatherIds.isEmpty()) {
log.warn("未查询到成功设备的最新采集ID,跳过删除操作");
return;
}
log.info("依附表数据删除完成,共保留 {} 个失败设备最新采集ID", excludeGatherIds.size());
// 3 依附表列表
String[] dependentTables = {"equ_interface", "equ_routing", "equ_veneer", "equ_vlan"};
// 4 拼 SQL: 删除所有 成功设备
String placeholders = latestGatherIds.stream().map(id -> "?").collect(Collectors.joining(","));
for (String table : dependentTables) {
String sql = String.format("DELETE FROM %s WHERE equ_gather_id IN (%s)", table, placeholders);
int deleted = jdbcTemplate.update(sql, latestGatherIds.toArray());
log.info("表 {} 删除旧数据 {} 条)", table, deleted);
}
log.info("✅ 成功清理依附表数据,");
}
/**
* 批量落库更新采集次数执行最新预警
* 事务保证如果任何步骤失败整个操作回滚

@ -45,17 +45,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="selectAlertMessageVo">
SELECT am.id,
am.equ_id,
am.equ_id AS equId ,
am.type,
am.equ_type,
am.equ_type AS equType,
am.solution,
am.level,
am.occur_time,
am.occur_time AS occurTime,
am.handler,
am.handle_time,
am.handle_time AS handleTime,
am.status,
am.batch,
am.message
am.message,
eb.name,
eb.equ_number AS equNumber
FROM alert_message am
LEFT JOIN equ_base eb ON am.equ_id = eb.id
</sql>
@ -64,7 +66,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select a.id, g.equ_id, type, equ_type, solution, level, occur_time, handler, handle_time, status,batch,message from alert_message a LEFT JOIN equ_gather g ON a.equ_id = g.equ_id
</sql>
<select id="selectAlertMessageList" parameterType="AlertDto" resultMap="AlertMessageResult">
<select id="selectAlertMessageList" parameterType="AlertDto" resultType="AlertMessages">
<include refid="selectAlertMessageVo"/>
<where>
<!-- 时间范围 -->
@ -82,6 +84,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<!-- 设备管理员精确匹配 -->
<if test="inCharge != null and inCharge != ''"> AND eb.in_charge = #{inCharge} </if>
<!-- ✅ 设备名称模糊匹配 -->
<if test="name != null and name != ''">
AND eb.name LIKE CONCAT('%', #{name}, '%')
</if>
<!-- ✅ 设备编号精准匹配 -->
<if test="equNumber != null and equNumber != ''">
AND eb.equ_number = #{equNumber}
</if>
</where>
<!-- 按 batch 从大到小排序 -->
ORDER BY am.batch DESC

@ -1,5 +1,5 @@
# 页面标题
VUE_APP_TITLE = 中国烟草
VUE_APP_TITLE = 唐山烟草网络设备智能监管平台
# 开发环境配置
ENV = 'development'

@ -1,5 +1,5 @@
# 页面标题
VUE_APP_TITLE = 中国烟草
VUE_APP_TITLE = 唐山烟草网络设备智能监管平台
# 生产环境配置
ENV = 'production'

@ -1,7 +1,7 @@
{
"name": "ruoyi",
"version": "3.9.0",
"description": "中国烟草",
"description": "唐山烟草网络设备智能监管平台",
"author": "若依",
"license": "MIT",
"scripts": {

@ -4,9 +4,10 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="renderer" content="webkit">
<link rel="icon" href="data:;base64,iVBORw0KGgo=">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<title><%= webpackConfig.name %></title>
<title>唐山烟草网络设备智能监管平台</title>
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
<style>
html,

@ -35,7 +35,7 @@ export default {
},
data() {
return {
title: process.env.VUE_APP_TITLE,
title: '中国烟草',
logo: logoImg
}
}

@ -194,7 +194,7 @@ class WebSocketClient {
title: message.title || '系统通知',
message: message.content,
type: 'info',
duration: 1000000, // 10秒后自动关闭
duration: 60000, // 60秒后自动关闭
showClose: true,
onClick: () => {
// 可以在这里添加点击通知后的处理逻辑

@ -79,21 +79,23 @@ export default {
methods: {
sendMessage() {
if (!this.newMessage.trim()) return;
console.log("%c 🍃: sendMessage -> this.newMessage ", "font-size:16px;background-color:#46f583;color:black;", this.newMessage)
//
this.messages.push({
text: this.newMessage,
sender: 'customer',
time: this.formatTime(new Date())
});
let newMessageContent = this.newMessage;
//
setTimeout(() => {
this.autoReply(this.newMessage);
this.autoReply(newMessageContent);
}, 1000);
this.newMessage = '';
},
async autoReply(userMessage) {
console.log("%c 🇱🇺: autoReply -> userMessage ", "font-size:16px;background-color:#c70b31;color:white;", userMessage)
let replyText = '';
let res = await knowledgeAsk({question: userMessage });
if (res.code == 200) {

@ -22,7 +22,7 @@
<el-table-column label="文件名称" align="center" key="source" prop="source" :show-overflow-tooltip="true" />
<el-table-column label="更新时间" align="center" prop="uploadTime" width="160">
<template slot-scope="scope">
<span>{{ parseTime(scope.row.uploadTime) }}</span>
<span>{{ formatISOTime(scope.row.uploadTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
@ -56,6 +56,8 @@
:data="{ source: form.source }"
:on-preview="handlePreview"
:on-remove="handleRemove"
:on-error="handleUploadError"
:before-upload="beforeUpload"
:file-list="fileList"
:headers="headers"
:auto-upload="false">
@ -298,6 +300,22 @@ export default {
this.$modal.msgSuccess("删除成功")
}).catch(() => {})
},
/** 格式化ISO时间字符串 */
formatISOTime(isoString) {
if (!isoString) return '';
const date = new Date(isoString);
if (isNaN(date.getTime())) return isoString;
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
const seconds = String(date.getSeconds()).padStart(2, '0');
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
},
/** 导出按钮操作 */
handleExport() {
this.download('system/user/export', {
@ -318,6 +336,31 @@ export default {
handleFileUploadProgress(event, file, fileList) {
this.upload.isUploading = true
},
//
beforeUpload(file) {
const isDocx = file.name.endsWith('.docx');
const isTxt = file.name.endsWith('.txt');
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isDocx && !isTxt) {
this.$message.error('只允许上传docx/txt格式的文件!');
return false;
}
if (!isLt5M) {
this.$message.error('文件大小不能超过5MB!');
return false;
}
return true;
},
//
handleUploadError(err, file, fileList) {
this.upload.isUploading = false;
//
this.$modal.msgError("上传失败,请重试");
console.error('上传失败:', err);
},
//
handleFileSuccess(response, file, fileList) {
this.upload.isUploading = false;

@ -21,6 +21,7 @@
<el-col :span="24">
<el-form-item label="采集间隔" prop="frequency">
<el-input-number v-model="queryParams.frequency" controls-position="right" :min="1"></el-input-number>
<span> 分钟</span>
</el-form-item>
</el-col>
<el-col :span="24">

@ -20,16 +20,16 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['device:deviceManufacturer:add']">新增</el-button>
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['device:deviceManu:add']">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['device:deviceManufacturer:remove']">删除</el-button>
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['device:deviceManu:remove']">删除</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['device:deviceManufacturer:import']">导入</el-button>
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['device:deviceManu:import']">导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['device:deviceManufacturer:export']">导出</el-button>
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport" v-hasPermi="['device:deviceManu:export']">导出</el-button>
</el-col> -->
</el-row>
@ -57,8 +57,8 @@
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['device:deviceManufacturer:edit']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['device:deviceManufacturer:remove']">删除</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['device:deviceManu:modify']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['device:deviceManu:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>

@ -47,8 +47,8 @@
</el-table-column>
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope" v-if="scope.row.id !== 1">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['device:deviceType:edit']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['device:deviceType:remove']">删除</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['device:deviceType:modify']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['device:deviceType:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>

@ -26,26 +26,26 @@
<span class="info-label">
<i class="fas fa-layer-group"></i> 设备级别
</span>
<span class="info-value">{{cardData.deviceLevel || '暂无'}}</span>
<span class="info-value" :title="cardData.deviceLevel || '暂无'">{{cardData.deviceLevel || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-map-marker-alt"></i> 安装位置
</span>
<span class="info-value">{{cardData.location || '暂无'}}</span>
<span class="info-value" :title="cardData.location || '暂无'">{{cardData.location || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-tag"></i> 品牌型号
</span>
<span class="info-value">{{cardData.brand || '暂无'}}</span>
<span class="info-value" :title="cardData.brand || '暂无'">{{cardData.brand || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-heartbeat"></i> 带外IP
<!-- <i class="fas fa-heartbeat"></i> 带内IP -->
</span>
<span class="info-value">{{cardData.ip || '暂无'}}</span>
<span class="info-value" :title="cardData.ip || '暂无'">{{cardData.ip || '暂无'}}</span>
</div>
</div>
</div>
@ -420,6 +420,7 @@ export default {
.info-label i {
font-size: 12px;
width: 8px;
color: #4299e1;
}
@ -427,6 +428,12 @@ export default {
font-size: 14px;
font-weight: 600;
color: #2d3748;
padding-left: 14px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: help;
width: 90px;
}
.chart-container {

@ -26,31 +26,31 @@
<span class="info-label">
<i class="fas fa-heartbeat"></i> 设备编号
</span>
<span class="info-value">{{details.equNumber || '暂无'}}</span>
<span class="info-value" :title="details.equNumber || '暂无'">{{details.equNumber || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-layer-group"></i> 固件版本
</span>
<span class="info-value">{{details.version || '暂无'}}</span>
<span class="info-value" :title="details.version || '暂无'">{{details.version || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-layer-group"></i> 设备级别
</span>
<span class="info-value">{{details.deviceLevel || '暂无'}}</span>
<span class="info-value" :title="(details.deviceLevel || '暂无') + '级'">{{details.deviceLevel || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-map-marker-alt"></i> 安装位置
</span>
<span class="info-value">{{details.location || '暂无'}}</span>
<span class="info-value" :title="details.location || '暂无'">{{details.location || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-tag"></i> 品牌型号
</span>
<span class="info-value">{{details.brand || '暂无'}}</span>
<span class="info-value" :title="details.brand || '暂无'">{{details.brand || '暂无'}}</span>
</div>
</div>
@ -62,33 +62,33 @@
<span class="info-label">
<i class="fas fa-layer-group"></i> 生产厂商名称
</span>
<span class="info-value">{{details.equManuName || '暂无'}}</span>
<span class="info-value" :title="details.equManuName || '暂无'">{{details.equManuName || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-map-marker-alt"></i> 供货商名称
</span>
<span class="info-value">{{details.supplierName || '暂无'}}</span>
<span class="info-value" :title="details.supplierName || '暂无'">{{details.supplierName || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-tag"></i> 供货商联系人
</span>
<span class="info-value">{{ details.contactBy || '暂无'}}</span>
<span class="info-value" :title="details.contactBy || '暂无'">{{ details.contactBy || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-heartbeat"></i> 供货商联系电话
</span>
<span class="info-value">{{ details.contactWay || '暂无'}}</span>
<span class="info-value" :title="details.contactWay || '暂无'">{{ details.contactWay || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-heartbeat"></i> 质保时间
</span>
<span class="info-value">{{details.warranty || '暂无'}}</span>
<span class="info-value" :title="details.warranty || '暂无'">{{details.warranty || '暂无'}}</span>
</div>
</div>
@ -100,7 +100,7 @@
<span class="info-label">
<i class="fas fa-layer-group"></i> 带外管理IP
</span>
<span class="info-value">{{details.ip || '暂无'}}</span>
<span class="info-value" :title="details.ip || '暂无'">{{details.ip || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
@ -112,13 +112,13 @@
<span class="info-label">
<i class="fas fa-layer-group"></i> 安装日期
</span>
<span class="info-value">{{details.installDate || '暂无'}}</span>
<span class="info-value" :title="details.installDate || '暂无'">{{details.installDate || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-layer-group"></i> 管理员
</span>
<span class="info-value">{{details.inCharge || '暂无'}}</span>
<span class="info-value" :title="details.inCharge || '暂无'">{{details.inCharge || '暂无'}}</span>
</div>
</div>
@ -129,19 +129,19 @@
<span class="info-label">
<i class="fas fa-layer-group"></i> 设备持续运行时长
</span>
<span class="info-value">{{formatDuration(details.contWorkPeriod) || '暂无'}}</span>
<span class="info-value" :title="formatDuration(details.contWorkPeriod) || '暂无'">{{formatDuration(details.contWorkPeriod) || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-layer-group"></i> 待处理预警数
</span>
<span class="info-value">{{details.unhandledAlertCount || '暂无'}}</span>
<span class="info-value" :title="details.unhandledAlertCount || '暂无'">{{details.unhandledAlertCount || '暂无'}}</span>
</div>
<div class="info-item">
<span class="info-label">
<i class="fas fa-layer-group"></i> 预警总数
</span>
<span class="info-value">{{details.alertCount || '暂无'}}</span>
<span class="info-value" :title="details.alertCount || '暂无'">{{details.alertCount || '暂无'}}</span>
</div>
</div>
</div>
@ -175,23 +175,102 @@
</el-col>
</el-col>
<el-col :span="24">
<el-col :span="12">
<div ref="usedmemory" style="height: 150px" />
</el-col>
<el-col :span="12">
<div ref="usedmemory2" style="height: 150px" />
</el-col>
<el-col :span="8">
<div ref="usedmemory3" style="height: 120px" />
</el-col>
<el-col :span="8">
<div ref="usedmemory4" style="height: 120px" />
</el-col>
<el-col :span="8">
<div ref="usedmemory5" style="height: 120px" />
</el-col>
<div class="health-indicators">
<el-row :gutter="16">
<!-- 温度 -->
<el-col :span="8">
<div class="health-card">
<div class="health-header">
<div class="health-icon" :class="getTemperatureClass(details.temperature)">
<i class="el-icon-odometer"></i>
</div>
<div class="health-label">当前温度</div>
</div>
<div class="health-circle" :class="getTemperatureClass(details.temperature)">
<div class="health-value" :class="getTemperatureClass(details.temperature)">{{details.temperature || '暂无'}}</div>
<div class="health-unit"></div>
</div>
</div>
</el-col>
<!-- CPU使用率 -->
<el-col :span="8">
<div class="health-card">
<div class="health-header">
<div class="health-icon" :class="getUtilizationClass(details.cpuUtilization)">
<i class="el-icon-cpu"></i>
</div>
<div class="health-label">CPU使用率</div>
</div>
<div class="health-circle" :class="getUtilizationClass(details.cpuUtilization)">
<div class="health-value" :class="getUtilizationClass(details.cpuUtilization)">{{details.cpuUtilization || '暂无'}}</div>
<div class="health-unit">%</div>
</div>
</div>
</el-col>
<!-- 内存使用率 -->
<el-col :span="8">
<div class="health-card">
<div class="health-header">
<div class="health-icon" :class="getUtilizationClass(details.memoryUtilization)">
<i class="el-icon-thumb"></i>
</div>
<div class="health-label">内存使用率</div>
</div>
<div class="health-circle" :class="getUtilizationClass(details.memoryUtilization)">
<div class="health-value" :class="getUtilizationClass(details.memoryUtilization)">{{details.memoryUtilization || '暂无'}}</div>
<div class="health-unit">%</div>
</div>
</div>
</el-col>
<!-- 健康度 -->
<el-col :span="8">
<div class="health-card">
<div class="health-header">
<div class="health-icon" :class="getHealthClass(Number(details.health))">
<i class="el-icon-orange"></i>
</div>
<div class="health-label">当前健康度</div>
</div>
<div class="health-circle" :class="getHealthClass(Number(details.health))">
<div class="health-value" :class="getHealthClass(Number(details.health))">{{ Number(details.health) == 1 ? '健康' : Number(details.health) == 2 ? '良好': Number(details.health) == 3 ? '一般': Number(details.health) == 4 ? '需维护' : '暂无' }}</div>
<div class="health-unit"></div>
</div>
</div>
</el-col>
<!-- volIn -->
<el-col :span="8">
<div class="health-card">
<div class="health-header">
<div class="health-icon system-color">
<i class="el-icon-s-data"></i>
</div>
<div class="health-label">当前流量输入值</div>
</div>
<div class="health-circle system-color">
<div class="health-value system-color">{{details.volIn ? parseInt(details.volIn) : '暂无'}}</div>
<div class="health-unit">L/min</div>
</div>
</div>
</el-col>
<!-- volOut -->
<el-col :span="8">
<div class="health-card">
<div class="health-header">
<div class="health-icon system-color">
<i class="el-icon-s-data"></i>
</div>
<div class="health-label">当前流量输出值</div>
</div>
<div class="health-circle system-color">
<div class="health-value system-color">{{details.volOut ? parseInt(details.volOut) : '暂无'}}</div>
<div class="health-unit">L/min</div>
</div>
</div>
</el-col>
</el-row>
</div>
</el-col>
<!-- 图表模块已删除 -->
</el-row>
</div>
@ -206,19 +285,19 @@
</div>
<div class="metrics-grid">
<div class="bottom-item scroll-height-500" v-if="tabIndex == 0">
<div class="bottom-item scroll-height-680" v-if="tabIndex == 0">
<History :dsId="dsId"/>
</div>
<div class="bottom-item scroll-height-500" v-if="tabIndex == 1">
<div class="bottom-item scroll-height-680" v-if="tabIndex == 1">
<Interface :dsId="dsId"/>
</div>
<div class="bottom-item scroll-height-500" v-if="tabIndex == 2">
<div class="bottom-item scroll-height-680" v-if="tabIndex == 2">
<Vlan :dsId="dsId"/>
</div>
<div class="bottom-item scroll-height-500" v-if="tabIndex == 3">
<div class="bottom-item scroll-height-680" v-if="tabIndex == 3">
<Routing :dsId="dsId"/>
</div>
<div class="bottom-item scroll-height-500" v-if="tabIndex == 4">
<div class="bottom-item scroll-height-680" v-if="tabIndex == 4">
<Veneer :dsId="dsId"/>
</div>
</div>
@ -247,7 +326,7 @@ export default {
components: { History, Interface, Vlan, Routing, LineChart, Veneer},
data() {
return {
DateValue: [dayjs().subtract(1, 'month').format('YYYY-MM-DD HH:mm:ss'),dayjs().format('YYYY-MM-DD HH:mm:ss')],
DateValue: [dayjs().subtract(1, 'week').format('YYYY-MM-DD HH:mm:ss'),dayjs().format('YYYY-MM-DD HH:mm:ss')],
selectedTab: 1,
tabs: [
{ name: '历史预警' },
@ -289,306 +368,7 @@ mounted() {
//
let details = this.historyQueryData;
const combinedPerformanceChart = echarts.init(document.getElementById('combinedPerformanceChart'));
const usedmemory = echarts.init(this.$refs.usedmemory, "macarons")
const usedmemory2 = echarts.init(this.$refs.usedmemory2, "macarons")
const usedmemory3 = echarts.init(this.$refs.usedmemory3, "macarons")
const usedmemory4 = echarts.init(this.$refs.usedmemory4, "macarons")
const usedmemory5 = echarts.init(this.$refs.usedmemory5, "macarons")
const chartData = { min: 0, max: 100, value: 70 };
usedmemory.setOption({
series: [
{
type: 'gauge',
center: ['50%', '60%'],
radius: '90%',
min: chartData.min,
max: chartData.max,
startAngle: 220,
endAngle: -40,
splitNumber: 8,
axisLine: {
lineStyle: {
color: [
[0.25, '#7afeaf'],
[0.5, '#58d9f9'],
[0.75, '#fbdd5e'],
[1, '#f03b44'],
],
width: 10,
},
},
splitLine: {
// 线
show: true,
length: 15,
distance: 10,
lineStyle: {
width: 2,
color: 'auto',
},
},
axisTick: {
// 线线
show: true,
lineStyle: {
width: 2,
color: 'auto',
},
length: 10,
distance: 10,
},
axisLabel: false,
detail: {
fontSize: '16',
formatter: '{value}℃',
color: 'auto',
offsetCenter: ['0', '0%'],
},
pointer: {
icon: 'triangle',
show: true,
offsetCenter: [0, '-70%'],
itemStyle: {
color: 'auto',
},
length: '10%',
width: '3%',
},
data: [
{
value: this.details.temperature,
name: '温度',
title: {
show: true,
color: '#606266',
offsetCenter: [0, '70%'],
fontSize: 18,
}
},
],
}
]
})
usedmemory2.setOption({
series: [
{
type: 'gauge',
center: ['50%', '60%'],
radius: '90%',
min: chartData.min,
max: chartData.max,
startAngle: 220,
endAngle: -40,
splitNumber: 8,
axisLine: {
lineStyle: {
color: [
[0.25, '#f03b44'],
[0.5, '#fbdd5e'],
[0.75, '#58d9f9'],
[1, '#7afeaf'],
],
width: 10,
},
},
splitLine: {
// 线
show: true,
length: 15,
distance: 10,
lineStyle: {
width: 2,
color: 'auto',
},
},
axisTick: {
// 线线
show: true,
lineStyle: {
width: 2,
color: 'auto',
},
length: 10,
distance: 10,
},
axisLabel: false,
detail: {
fontSize: '16',
formatter: '{value}',
color: 'auto',
offsetCenter: ['0', '0%'],
},
pointer: {
icon: 'triangle',
show: true,
offsetCenter: [0, '-70%'],
itemStyle: {
color: 'auto',
},
length: '10%',
width: '3%',
},
data: [
{
value: this.details.health == 1 ? '100' : this.details.health == 2 ? '75': this.details.health == 3 ? '50': this.details.health == 4 ? '25' : '0',
name: '健康度',
title: {
show: true,
color: '#606266',
offsetCenter: [0, '70%'],
fontSize: 18,
}
},
],
}
]
})
usedmemory3.setOption({
title: [
{
text: 'CPU占用',
left: '45%',
top: '55%',
textAlign: 'center',
textStyle: {
fontSize: '14',
fontWeight: '400',
color: '#6AA0E4',
textAlign: 'center',
textShadowColor: '#000',
},
},
],
series: {
type: 'pie',
radius: ['90%', "100%"],
hoverAnimation: false,
data: [
{
value: this.details.cpuUtilization,
label: {
normal: {
color: '#6AA0E4',
fontSize: 22,
fontWeight: 'bold',
formatter: function(params) {
return params.value + '%';
},
position: 'center',
show: true,
}
},
itemStyle: {
normal: {
color: '#6AA0E4'
}
}
},{
value: 100-this.details.cpuUtilization,
name: '',
labelLine: {
show: false
},
itemStyle: {
normal: {
color: '#D8D8D8'
},
emphasis:{
color:'#D8D8D8'
}
}
}]
}
})
usedmemory4.setOption({
title: [
{
text: '内存使用率',
left: '45%',
top: '55%',
textAlign: 'center',
textStyle: {
fontSize: '14',
fontWeight: '400',
color: '#6AA0E4',
textAlign: 'center',
textShadowColor: '#000',
},
},
],
series: {
type: 'pie',
radius: ['90%', "100%"],
hoverAnimation: false,
data: [
{
value: this.details.memoryUtilization,
label: {
normal: {
color: '#6AA0E4',
fontSize: 22,
fontWeight: 'bold',
formatter: function(params) {
return params.value + '%';
},
position: 'center',
show: true,
}
},
itemStyle: {
normal: {
color: '#6AA0E4'
}
}
},{
value: 100-this.details.memoryUtilization,
name: '',
labelLine: {
show: false
},
itemStyle: {
normal: {
color: '#D8D8D8'
},
emphasis:{
color:'#D8D8D8'
}
}
}]
}
})
usedmemory5.setOption({
series: [
{
type: 'pie',
radius: [0, '100%'],
center: ['50%', '50%'],
label: {
position: 'inside',
formatter: '{b}\n{c}',
color: '#30465D'
},
labelLine: {
length: 5,
length2: 5
},
data: [{
name: '输入',
value: this.details.volIn
},
{
name: '输出',
value: this.details.volOut
}
]
}
]
})
// usedmemory
combinedPerformanceChart.setOption({
tooltip: {
trigger: 'axis',
@ -602,42 +382,20 @@ mounted() {
params.forEach(function(item) {
let value = item.value;
let unit = '';
//
if (item.seriesName === '设备温度') {
unit = '℃';
} else if (item.seriesName === '健康度') {
// - 访
//
let originalValue;
switch(value) {
case 100: originalValue = 1; break;
case 75: originalValue = 2; break;
case 50: originalValue = 3; break;
case 25: originalValue = 4; break;
default: originalValue = 0;
}
let statusText = '';
switch(originalValue) {
case 1: statusText = '健康'; break;
case 2: statusText = '良好'; break;
case 3: statusText = '一般'; break;
case 4: statusText = '需维护'; break;
default: statusText = '未知';
}
result += item.marker + item.seriesName + ': ' + value + '% (' + statusText + ')<br/>';
return;
} else {
unit = '%';
}
//
if (item.seriesName === '设备温度') {
unit = '℃';
} else {
unit = '%';
}
result += item.marker + item.seriesName + ': ' + value + unit + '<br/>';
});
return result;
}
},
legend: {
data: ['设备温度', 'CPU使用率', '内存使用率', '健康度'],
data: ['设备温度', 'CPU使用率', '内存使用率'],
textStyle: {
color: '#718096'
}
@ -764,43 +522,6 @@ mounted() {
}]
}
}
},
{
name: '健康度',
type: 'bar',
// 1->100, 2->75, 3->50, 4->25
// 使
data: details.healthList.map(value => {
const valueMap = {1: 100, 2: 75, 3: 50, 4: 25};
// 使value
return {
value: valueMap[value] || 0,
originalValue: value
};
}),
itemStyle: {
color: function(params) {
//
const originalValue = params.data && params.data.originalValue !== undefined ?
params.data.originalValue : params.value;
// 使switchcasebreak
switch(originalValue) {
case 1: return '#48bb78'; // - 绿
case 2: return '#4299e1'; // -
case 3: return '#ed8936'; // -
case 4: return '#f56565'; // -
default: return '#a0aec0'; //
}
}
},
barWidth: '40%',
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
});
@ -809,8 +530,6 @@ mounted() {
//
window.addEventListener('resize', function() {
combinedPerformanceChart.resize();
usedmemory.resize();
usedmemory2.resize();
// trafficChart.resize();
});
},
@ -841,14 +560,46 @@ mounted() {
this.historyQueryList.minTime = date[0]
this.historyQueryList.maxTime = date[1]
this.gethistoryQuery(this.$route.params.id)
},
//
getTemperatureClass(temperature) {
if (!temperature || isNaN(temperature)) return 'temperature-normal';
const temp = Number(temperature);
if (temp < 20) return 'temperature-low';
if (temp >= 20 && temp <= 30) return 'temperature-normal';
if (temp > 30 && temp <= 40) return 'temperature-high';
return 'temperature-danger';
},
//
getHealthClass(health) {
switch(health) {
case 1:
return 'health-excellent';
case 2:
return 'health-good';
case 3:
return 'health-fair';
case 4:
return 'health-poor';
default:
return 'health-good';
}
},
// 使
getUtilizationClass(utilization) {
if (!utilization || isNaN(utilization)) return 'utilization-normal';
const value = Number(utilization);
if (value <= 50) return 'utilization-normal';
if (value > 50 && value <= 80) return 'utilization-warning';
return 'utilization-danger';
}
}
}
</script>
<style scoped>
.scroll-height-500 {
height: 500px;
.scroll-height-680 {
height: 680px;
overflow-y: auto;
overflow-x: hidden;
}
@ -1138,4 +889,211 @@ mounted() {
font-weight: 600;
border-bottom: 2px solid #2d3748;
}
</style>
.info-value {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: help;
}
</style>
<style scoped>
.health-indicators {
padding: 20px 20px 0;
background: #fff;
border-radius: 8px;
}
.section-title {
font-size: 16px;
font-weight: bold;
color: #333;
margin-bottom: 20px;
}
.health-card {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 180px;
padding: 10px 0;
box-sizing: border-box;
background: linear-gradient(135deg, #edf2f7, #e2e8f0);
border-radius: 12px;
border: 1px solid rgba(226, 232, 240, 0.8);
transition: all 0.3s ease;
margin-bottom: 16px;
}
.health-header {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 10px;
width: 100%;
padding-left: 16px;
}
.health-card:hover {
background: rgba(255, 255, 255, 0.9);
border-color: rgba(66, 153, 225, 0.3);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
}
.health-label {
font-size: 14px;
color: #2d3748;
margin-bottom: 0;
text-align: left;
font-weight: bold;
}
.health-icon {
position: relative;
width: 40px;
height: 40px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 18px;
margin-bottom: 0;
background: #e6f7ff;
color: #1890ff;
}
.health-circle {
width: 120px;
height: 120px;
border-radius: 50%;
display: flex;
/* flex-direction: column; */
align-items: center;
justify-content: center;
position: relative;
overflow: hidden;
}
/* 圆形背景颜色 */
/* 温度相关样式 */
.health-circle.temperature-low {
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
}
.health-circle.temperature-normal {
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
}
.health-circle.temperature-high {
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
}
.health-circle.temperature-danger {
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
}
.health-value.temperature-low {
color: #0ea5e9;
}
.health-value.temperature-normal {
color: #22c55e;
}
.health-value.temperature-high {
color: #f59e0b;
}
.health-value.temperature-danger {
color: #ef4444;
}
/* 健康度相关样式 */
.health-circle.health-excellent {
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
}
.health-circle.health-good {
background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%);
}
.health-circle.health-fair {
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
}
.health-circle.health-poor {
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
}
.health-value.health-excellent {
color: #22c55e;
}
.health-value.health-good {
color: #3b82f6;
}
.health-value.health-fair {
color: #f59e0b;
}
.health-value.health-poor {
color: #ef4444;
}
/* 使用率相关样式 */
.health-circle.utilization-normal {
background: linear-gradient(135deg, #f0fdf4 0%, #dcfce7 100%);
}
.health-circle.utilization-warning {
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
}
.health-circle.utilization-danger {
background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%);
}
.health-value.utilization-normal {
color: #22c55e;
}
.health-value.utilization-warning {
color: #f59e0b;
}
.health-value.utilization-danger {
color: #ef4444;
}
/* 系统色系样式 */
.health-circle.system-color {
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
}
.health-value.system-color {
color: #0ea5e9;
}
/* 图标颜色 */
.health-icon.blood-pressure,
.health-icon.heart-rate,
.health-icon.breath {
background: linear-gradient(135deg, #22c55e 0%, #4ade80 100%);
color: white;
}
.health-icon.cardio,
.health-icon.heart-rate-variability {
background: linear-gradient(135deg, #f97316 0%, #fb923c 100%);
color: white;
}
.health-icon.blood-oxygen {
background: linear-gradient(135deg, #ef4444 0%, #f87171 100%);
color: white;
}
.health-icon.system-color {
background: linear-gradient(135deg, #0ea5e9 0%, #38bdf8 100%);
color: white;
}
/* 数值样式 */
.health-value {
font-size: 28px;
font-weight: bold;
margin-bottom: 5px;
}
/* 单位样式 */
.health-unit {
font-size: 14px;
color: #666;
}
</style>

@ -9,6 +9,12 @@
<el-form-item label="预警时间">
<el-date-picker v-model="dateRange" style="width: 240px" value-format="yyyy-MM-dd hh:mm:ss" type="datetimerange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
</el-form-item>
<el-form-item label="设备编号" prop="equNumber">
<el-input v-model="queryParams.equNumber" placeholder="设备编号" clearable style="width: 240px" />
</el-form-item>
<el-form-item label="设备名称" prop="name">
<el-input v-model="queryParams.name" placeholder="设备名称" clearable style="width: 240px" />
</el-form-item>
<el-form-item label="预警等级" prop="level">
<el-select v-model="queryParams.level" placeholder="预警等级" clearable style="width: 240px">
<el-option v-for="dict in dict.type.warning_level" :key="dict.value" :label="dict.label" :value="dict.value" />
@ -27,26 +33,26 @@
<el-row :gutter="10" class="mb8">
<!-- <el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['inMonitoring:message:add']">新增</el-button>
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd" v-hasPermi="['inMonitoring:alertMessage:add']">新增</el-button>
</el-col> -->
<el-col :span="1.5">
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['inMonitoring:message:remove']">删除</el-button>
<el-button type="danger" plain icon="el-icon-delete" size="mini" :disabled="multiple" @click="handleDelete" v-hasPermi="['inMonitoring:alertMessage:remove']">删除</el-button>
</el-col>
<!-- <el-col :span="1.5">
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['inMonitoring:message:import']">导入</el-button>
<el-button type="info" plain icon="el-icon-upload2" size="mini" @click="handleImport" v-hasPermi="['inMonitoring:alertMessage:import']">导入</el-button>
</el-col> -->
</el-row>
<el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="50" align="center" />
<el-table-column
label="序号"
<el-table-column label="序号"
width="60"
type="index"
></el-table-column>
<el-table-column label="设备名称" align="center" key="name" prop="name" width="150" :show-overflow-tooltip="true" />
<el-table-column label="预警时间" align="center" key="occurTime" prop="occurTime" :show-overflow-tooltip="true" />
<el-table-column label="预警等级" align="center" key="level" prop="level" :show-overflow-tooltip="true" />
<el-table-column label="预警信息" align="center" key="message" prop="message" width="200" :show-overflow-tooltip="true">
<el-table-column label="预警信息" align="left" key="message" prop="message" width="200" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span @click="showContentDialog('预警信息', scope.row.message)" class="clickable-cell">{{ scope.row.message || '--' }}</span>
</template>
@ -66,7 +72,7 @@
<span>{{ parseTime(scope.row.handleTime) || '--' }}</span>
</template>
</el-table-column>
<el-table-column label="解决方案" align="center" key="solution" prop="solution" :show-overflow-tooltip="true" >
<el-table-column label="解决方案" align="left" key="solution" prop="solution" :show-overflow-tooltip="true" >
<template slot-scope="scope">
<span @click="showContentDialog('解决方案', scope.row.solution)" class="clickable-cell">{{ scope.row.solution || '--'}}</span>
</template>
@ -74,9 +80,9 @@
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" v-if="scope.row.status == 'UNHANDLED'" type="text" @click="handleUpdate(scope.row)" v-hasPermi="['inMonitoring:message:edit']">去处理</el-button>
<el-button size="mini" type="text" @click="handleUpdate(scope.row)" v-if="scope.row.status == 'HANDLED'" v-hasPermi="['inMonitoring:message:edit']">编辑</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['inMonitoring:message:remove']">删除</el-button>
<el-button size="mini" v-if="scope.row.status == 'UNHANDLED'" type="text" @click="handleUpdate(scope.row)" v-hasPermi="['inMonitoring:alertMessage:edit']">去处理</el-button>
<el-button size="mini" type="text" @click="handleUpdate(scope.row)" v-if="scope.row.status == 'HANDLED'" v-hasPermi="['inMonitoring:alertMessage:edit']">编辑</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)" v-hasPermi="['inMonitoring:alertMessage:remove']">删除</el-button>
</template>
</el-table-column>
</el-table>

@ -34,8 +34,8 @@
type="index"
></el-table-column>
<el-table-column label="名称" align="center" key="ruleName" prop="ruleName" width="120"/>
<el-table-column label="描述" align="center" key="description" prop="description" width="240" :show-overflow-tooltip="true" />
<el-table-column label="触发条件" align="center" key="alertTemplate" prop="alertTemplate" width="240" :show-overflow-tooltip="true" >
<el-table-column label="描述" align="left" key="description" prop="description" width="240" :show-overflow-tooltip="true" />
<el-table-column label="触发条件" align="left" key="alertTemplate" prop="alertTemplate" width="240" :show-overflow-tooltip="true" >
<template slot-scope="scope">
<span v-if="scope.row.paramName">{{ findParamName(scope.row.paramName) }} {{ scope.row.operator }} {{ scope.row.compareValue }}</span>
<span v-else></span>
@ -43,7 +43,7 @@
</el-table-column>
<el-table-column label="预警级别" align="center" key="severity" prop="severity" width="120" />
<el-table-column label="预警方式" align="center" key="alertWay" prop="alertWay" width="120" :show-overflow-tooltip="true" />
<el-table-column label="预警邮箱" align="center" key="alertStation" prop="alertStation" width="200" :show-overflow-tooltip="true" />
<el-table-column label="预警邮箱" align="left" key="alertStation" prop="alertStation" width="200" :show-overflow-tooltip="true" />
<el-table-column label="操作" align="center" width="160" class-name="small-padding fixed-width">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)" v-hasPermi="['inMonitoring:alertRule:edit']">修改</el-button>

@ -378,7 +378,7 @@ export default {
//
setInterval(() => {
this.refreshDeviceStatus();
}, 60000);
}, 600000);
},
activated() {
//
@ -926,9 +926,11 @@ export default {
},
//
jumpToDeviceFiles(floor) {
// ''
const floorNumber = Number(String(floor).replace('层', ''));
this.$router.push({
path: '/device/files',
query: { floor: floor }
query: { floor: floorNumber }
});
},

@ -14,7 +14,7 @@
></el-table-column>
<el-table-column label="消息ID" align="center" key="id" prop="id" width="80" />
<el-table-column label="用户ID" align="center" key="userId" prop="userId" width="80" />
<el-table-column label="消息内容" align="center" key="content" prop="content" :show-overflow-tooltip="true">
<el-table-column label="消息内容" align="left" key="content" prop="content" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{ scope.row.content || '--' }}</span>
</template>
@ -26,7 +26,7 @@
</el-tag>
</template>
</el-table-column>
<el-table-column label="标题" align="center" key="title" prop="title" :show-overflow-tooltip="true">
<el-table-column label="标题" align="left" key="title" prop="title" :show-overflow-tooltip="true">
<template slot-scope="scope">
<span>{{ scope.row.title || '--' }}</span>
</template>

Loading…
Cancel
Save