commit 0c2fe393df95851767502b93be73ca474aa2e5fd Author: admin <605696661@qq.com> Date: Thu Jul 9 09:54:43 2026 +0800 init 全新仓库,清除全部历史 diff --git a/alert_dashbaord.gz b/alert_dashbaord.gz new file mode 100644 index 0000000..22bd836 Binary files /dev/null and b/alert_dashbaord.gz differ diff --git a/alert_dashboard.tar.gz b/alert_dashboard.tar.gz new file mode 100644 index 0000000..c83009f Binary files /dev/null and b/alert_dashboard.tar.gz differ diff --git a/alert_dashboard/alert_mail_stat_back_20260702_140523.sql b/alert_dashboard/alert_mail_stat_back_20260702_140523.sql new file mode 100644 index 0000000..e69de29 diff --git a/alert_dashboard/alert_mail_stat_back_20260702_140530.sql b/alert_dashboard/alert_mail_stat_back_20260702_140530.sql new file mode 100644 index 0000000..e69de29 diff --git a/alert_dashboard/alert_mail_stat_back_20260702_140540.sql b/alert_dashboard/alert_mail_stat_back_20260702_140540.sql new file mode 100644 index 0000000..e69de29 diff --git a/alert_dashboard/alert_mail_stat_back_20260702_140601.sql b/alert_dashboard/alert_mail_stat_back_20260702_140601.sql new file mode 100644 index 0000000..45a9f8c --- /dev/null +++ b/alert_dashboard/alert_mail_stat_back_20260702_140601.sql @@ -0,0 +1,91 @@ +-- MySQL dump 10.14 Distrib 5.5.68-MariaDB, for Linux (x86_64) +-- +-- Host: localhost Database: alert_mail_stat +-- ------------------------------------------------------ +-- Server version 5.5.68-MariaDB + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!40101 SET NAMES utf8 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `alert_log` +-- + +DROP TABLE IF EXISTS `alert_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `alert_log` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '自增ID', + `mail_uid` varchar(100) NOT NULL COMMENT '邮件唯一ID,去重防止重复采集', + `alert_type` tinyint(4) NOT NULL COMMENT '1触发firing 2恢复resolved', + `alert_name` varchar(200) NOT NULL COMMENT '告警名称(Port_Down/NetworkInHigh)', + `instance` varchar(200) NOT NULL COMMENT '实例地址IP:端口', + `severity` varchar(50) NOT NULL COMMENT '级别critical/warning', + `starts_at` datetime DEFAULT NULL COMMENT '告警开始UTC转北京时间', + `ends_at` datetime DEFAULT NULL COMMENT '告警恢复时间', + `content` text COMMENT '邮件完整正文', + `receive_time` datetime NOT NULL COMMENT '邮件接收入库时间', + PRIMARY KEY (`id`), + KEY `idx_day` (`receive_time`), + KEY `idx_alert_name` (`alert_name`(191)), + KEY `idx_instance` (`instance`(191)) +) ENGINE=InnoDB AUTO_INCREMENT=185 DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `alert_log` +-- + +LOCK TABLES `alert_log` WRITE; +/*!40000 ALTER TABLE `alert_log` DISABLE KEYS */; +INSERT INTO `alert_log` VALUES (1,'test_demo_001',1,'389 Port Down','10.150.10.82:389','critical','2026-06-29 13:23:23',NULL,'测试端口告警内容','2026-06-29 14:00:00'),(2,'test_demo_002',1,'389 Port Down','10.150.10.82:389','critical','2026-06-30 09:40:33',NULL,'测试端口告警内容','2026-06-30 09:40:33'),(4,'28152faa1162fe7c',2,'389 Port Down','10.150.10.87:389','critical','2026-06-30 03:36:38','2026-06-30 06:43:08','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.87:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-30T06:35:38.187Z\', \'endsAt\': \'2026-06-30T06:43:08.187Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'28152faa1162fe7c\'}','2026-06-30 11:36:48'),(5,'e44392bc8df9dd53',2,'Port_Down','10.150.10.87:389','critical','2026-06-30 04:21:11','2026-06-30 06:43:11','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'Port_Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'level\': \'Down\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'业务:\\n监控任务:blackbox-freeipa-ldap\\n故障地址:10.150.10.87:389\\n异常端口:389\\n\', \'summary\': \'端口 389 通信异常 10.150.10.87:389\'}, \'startsAt\': \'2026-06-30T06:36:26.879Z\', \'endsAt\': \'2026-06-30T06:43:11.879Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%21%3D%22%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'e44392bc8df9dd53\'}','2026-06-30 12:21:21'),(6,'8a949c270be3a2bf',1,'389 Port Down','10.150.10.82:389','critical','2026-06-29 05:23:23','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.82:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.82:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-29T05:23:23.187Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'8a949c270be3a2bf\'}','2026-06-30 13:18:35'),(7,'bc172bafef3d821e',1,'Port_Down','10.150.10.82:389','critical','2026-06-29 05:24:11','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'Port_Down\', \'instance\': \'10.150.10.82:389\', \'job\': \'blackbox-freeipa-ldap\', \'level\': \'Down\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'业务:\\n监控任务:blackbox-freeipa-ldap\\n故障地址:10.150.10.82:389\\n异常端口:389\\n\', \'summary\': \'端口 389 通信异常 10.150.10.82:389\'}, \'startsAt\': \'2026-06-29T05:24:11.879Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%21%3D%22%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'bc172bafef3d821e\'}','2026-06-30 13:18:36'),(8,'test_resolve_00001',2,'测试恢复告警','127.0.0.1:9100','warning','2026-06-30 10:00:00','2026-06-30 10:10:00','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'测试恢复告警\', \'instance\': \'127.0.0.1:9100\', \'severity\': \'warning\'}, \'startsAt\': \'2026-06-30 10:00:00\', \'endsAt\': \'2026-06-30 10:10:00\', \'fingerprint\': \'test_resolve_00001\'}','2026-06-30 14:03:36'),(9,'ecf53c73edc2d5e1',1,'NetworkInHigh','10.150.111.240:9100','warning','2026-06-30 06:08:33','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:389.4799811045329 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-06-30T06:08:33.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-06-30 14:08:43'),(10,'c4722f8bb16c1c50',1,'ServerhomeDiskLess100G','10.150.111.240:9100','critical','2026-06-30 06:09:56','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'ServerhomeDiskLess100G\', \'device\': \'/dev/mapper/vg_adc-lv_adc\', \'fstype\': \'ext4\', \'instance\': \'10.150.111.240:9100\', \'job\': \'nsschina2\', \'mountpoint\': \'/serverhome\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'节点 10.150.111.240:9100 挂载点 /serverhome 剩余:8.0360579072e+10 字节\', \'summary\': \'/serverhome 分区剩余空间不足100GB\'}, \'startsAt\': \'2026-06-30T06:09:56.205Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=node_filesystem_avail_bytes%7Bmountpoint%3D%22%2Fserverhome%22%7D+%3C+100+%2A+1024+%2A+1024+%2A+1024&g0.tab=1\', \'fingerprint\': \'c4722f8bb16c1c50\'}','2026-06-30 14:10:06'),(112,'28152faa1162fe7c',2,'389 Port Down','10.150.10.87:389','critical','2026-06-30 06:48:08','2026-06-30 06:50:08','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.87:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-30T06:48:08.187Z\', \'endsAt\': \'2026-06-30T06:50:08.187Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'28152faa1162fe7c\'}','2026-06-30 14:48:18'),(113,'e44392bc8df9dd53',2,'Port_Down','10.150.10.87:389','critical','2026-06-30 06:48:56','2026-06-30 06:50:11','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'Port_Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'level\': \'Down\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'业务:\\n监控任务:blackbox-freeipa-ldap\\n故障地址:10.150.10.87:389\\n异常端口:389\\n\', \'summary\': \'端口 389 通信异常 10.150.10.87:389\'}, \'startsAt\': \'2026-06-30T06:48:56.879Z\', \'endsAt\': \'2026-06-30T06:50:11.879Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%21%3D%22%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'e44392bc8df9dd53\'}','2026-06-30 14:49:06'),(114,'28152faa1162fe7c',1,'389 Port Down','10.150.10.87:389','critical','2026-06-30 07:06:23','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.87:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-30T07:06:23.187Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'28152faa1162fe7c\'}','2026-06-30 15:06:33'),(115,'28152faa1162fe7c',2,'389 Port Down','10.150.10.87:389','critical','2026-06-30 07:06:23','2026-06-30 07:07:08','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.87:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-30T07:06:23.187Z\', \'endsAt\': \'2026-06-30T07:07:08.187Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'28152faa1162fe7c\'}','2026-06-30 15:07:13'),(116,'c4722f8bb16c1c50',1,'ServerhomeDiskLess100G','10.150.111.240:9100','critical','2026-06-30 08:01:26','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'ServerhomeDiskLess100G\', \'device\': \'/dev/mapper/vg_adc-lv_adc\', \'fstype\': \'ext4\', \'instance\': \'10.150.111.240:9100\', \'job\': \'nsschina2\', \'mountpoint\': \'/serverhome\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'节点 10.150.111.240:9100 挂载点 /serverhome 剩余:1.06818179072e+11 字节\', \'summary\': \'/serverhome 分区剩余空间不足100GB\'}, \'startsAt\': \'2026-06-30T08:01:26.205Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=node_filesystem_avail_bytes%7Bmountpoint%3D%22%2Fserverhome%22%7D+%3C+100+%2A+1024+%2A+1024+%2A+1024&g0.tab=1\', \'fingerprint\': \'c4722f8bb16c1c50\'}','2026-06-30 16:01:36'),(117,'28152faa1162fe7c',1,'389 Port Down','10.150.10.87:389','critical','2026-06-30 08:20:38','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.87:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-30T08:20:38.187Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'28152faa1162fe7c\'}','2026-06-30 16:20:48'),(118,'e44392bc8df9dd53',1,'Port_Down','10.150.10.87:389','critical','2026-06-30 08:21:26','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'Port_Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'level\': \'Down\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'业务:\\n监控任务:blackbox-freeipa-ldap\\n故障地址:10.150.10.87:389\\n异常端口:389\\n\', \'summary\': \'端口 389 通信异常 10.150.10.87:389\'}, \'startsAt\': \'2026-06-30T08:21:26.879Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%21%3D%22%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'e44392bc8df9dd53\'}','2026-06-30 16:21:36'),(119,'28152faa1162fe7c',2,'389 Port Down','10.150.10.87:389','critical','2026-06-30 08:20:38','2026-06-30 08:23:38','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.87:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-30T08:20:38.187Z\', \'endsAt\': \'2026-06-30T08:23:38.187Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'28152faa1162fe7c\'}','2026-06-30 16:23:38'),(120,'e44392bc8df9dd53',2,'Port_Down','10.150.10.87:389','critical','2026-06-30 08:21:26','2026-06-30 08:23:41','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'Port_Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'level\': \'Down\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'业务:\\n监控任务:blackbox-freeipa-ldap\\n故障地址:10.150.10.87:389\\n异常端口:389\\n\', \'summary\': \'端口 389 通信异常 10.150.10.87:389\'}, \'startsAt\': \'2026-06-30T08:21:26.879Z\', \'endsAt\': \'2026-06-30T08:23:41.879Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%21%3D%22%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'e44392bc8df9dd53\'}','2026-06-30 16:23:41'),(121,'28152faa1162fe7c',1,'389 Port Down','10.150.10.87:389','critical','2026-06-30 08:50:23','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.87:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-30T08:50:23.187Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'28152faa1162fe7c\'}','2026-06-30 16:54:08'),(122,'28152faa1162fe7c',2,'389 Port Down','10.150.10.87:389','critical','2026-06-30 08:50:23','2026-06-30 08:54:38','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.87:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.87:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-30T08:50:23.187Z\', \'endsAt\': \'2026-06-30T08:54:38.187Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'28152faa1162fe7c\'}','2026-06-30 16:54:38'),(123,'c4722f8bb16c1c50',2,'ServerhomeDiskLess100G','10.150.111.240:9100','critical','2026-06-30 08:01:26','2026-06-30 09:47:26','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'ServerhomeDiskLess100G\', \'device\': \'/dev/mapper/vg_adc-lv_adc\', \'fstype\': \'ext4\', \'instance\': \'10.150.111.240:9100\', \'job\': \'nsschina2\', \'mountpoint\': \'/serverhome\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'节点 10.150.111.240:9100 挂载点 /serverhome 剩余:9.3562322944e+10 字节\', \'summary\': \'/serverhome 分区剩余空间不足100GB\'}, \'startsAt\': \'2026-06-30T08:01:26.205Z\', \'endsAt\': \'2026-06-30T09:47:26.205Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=node_filesystem_avail_bytes%7Bmountpoint%3D%22%2Fserverhome%22%7D+%3C+100+%2A+1024+%2A+1024+%2A+1024&g0.tab=1\', \'fingerprint\': \'c4722f8bb16c1c50\'}','2026-06-30 17:47:27'),(124,'c4722f8bb16c1c50',1,'ServerhomeDiskLess100G','10.150.111.240:9100','critical','2026-06-30 09:48:41','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'ServerhomeDiskLess100G\', \'device\': \'/dev/mapper/vg_adc-lv_adc\', \'fstype\': \'ext4\', \'instance\': \'10.150.111.240:9100\', \'job\': \'nsschina2\', \'mountpoint\': \'/serverhome\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'节点 10.150.111.240:9100 挂载点 /serverhome 剩余:9.3549539328e+10 字节\', \'summary\': \'/serverhome 分区剩余空间不足100GB\'}, \'startsAt\': \'2026-06-30T09:48:41.205Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=node_filesystem_avail_bytes%7Bmountpoint%3D%22%2Fserverhome%22%7D+%3C+100+%2A+1024+%2A+1024+%2A+1024&g0.tab=1\', \'fingerprint\': \'c4722f8bb16c1c50\'}','2026-06-30 17:48:51'),(125,'c586bc91c11fbb65',1,'NIS Service Down','10.150.10.81:111','critical','2026-06-30 09:51:08','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NIS Service Down\', \'instance\': \'10.150.10.81:111\', \'job\': \'tcp_port_111_2049_check\', \'port\': \'111\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'111(rpcbind/NIS)端口断开,NIS/NFS依赖异常,实例:10.150.10.81:111,端口:111\', \'summary\': \'主机 10.150.10.81:111 RPC 111端口不可达\'}, \'startsAt\': \'2026-06-30T09:51:08.187Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22111%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'c586bc91c11fbb65\'}','2026-06-30 17:51:18'),(126,'8dd0f02ec854fb6a',1,'389 Port Down','10.150.10.81:389','critical','2026-06-30 09:51:08','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.81:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.81:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-30T09:51:08.187Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'8dd0f02ec854fb6a\'}','2026-06-30 17:51:18'),(127,'8dd0f02ec854fb6a',2,'389 Port Down','10.150.10.81:389','critical','2026-06-30 09:51:08','2026-06-30 09:51:23','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'389 Port Down\', \'instance\': \'10.150.10.81:389\', \'job\': \'blackbox-freeipa-ldap\', \'port\': \'389\', \'severity\': \'critical\'}, \'annotations\': {\'summary\': \'主机 10.150.10.81:389 认证 389端口不可达\'}, \'startsAt\': \'2026-06-30T09:51:08.187Z\', \'endsAt\': \'2026-06-30T09:51:23.187Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22389%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'8dd0f02ec854fb6a\'}','2026-06-30 17:51:28'),(128,'c586bc91c11fbb65',2,'NIS Service Down','10.150.10.81:111','critical','2026-06-30 09:51:08','2026-06-30 09:51:23','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NIS Service Down\', \'instance\': \'10.150.10.81:111\', \'job\': \'tcp_port_111_2049_check\', \'port\': \'111\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'111(rpcbind/NIS)端口断开,NIS/NFS依赖异常,实例:10.150.10.81:111,端口:111\', \'summary\': \'主机 10.150.10.81:111 RPC 111端口不可达\'}, \'startsAt\': \'2026-06-30T09:51:08.187Z\', \'endsAt\': \'2026-06-30T09:51:23.187Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bport%3D%22111%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'c586bc91c11fbb65\'}','2026-06-30 17:51:28'),(129,'bb9b8ceb657ed81c',1,'NetworkOutHigh','10.151.10.32:9100','warning','2026-06-30 09:56:33','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.151.10.32:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:86.81705233256022 MB/s\', \'summary\': \'服务器 10.151.10.32:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-06-30T09:56:33.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'bb9b8ceb657ed81c\'}','2026-06-30 17:56:43'),(130,'bb9b8ceb657ed81c',2,'NetworkOutHigh','10.151.10.32:9100','warning','2026-06-30 09:56:33','2026-06-30 09:57:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.151.10.32:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:86.22444572448731 MB/s\', \'summary\': \'服务器 10.151.10.32:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-06-30T09:56:33.466Z\', \'endsAt\': \'2026-06-30T09:57:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'bb9b8ceb657ed81c\'}','2026-06-30 17:57:23'),(131,'bb9b8ceb657ed81c',1,'NetworkOutHigh','10.151.10.32:9100','warning','2026-06-30 10:30:33','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.151.10.32:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:89.15950870513916 MB/s\', \'summary\': \'服务器 10.151.10.32:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-06-30T10:30:33.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'bb9b8ceb657ed81c\'}','2026-06-30 18:30:43'),(132,'bb9b8ceb657ed81c',2,'NetworkOutHigh','10.151.10.32:9100','warning','2026-06-30 10:30:33','2026-06-30 10:31:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.151.10.32:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:103.52136014302572 MB/s\', \'summary\': \'服务器 10.151.10.32:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-06-30T10:30:33.466Z\', \'endsAt\': \'2026-06-30T10:31:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'bb9b8ceb657ed81c\'}','2026-06-30 18:31:23'),(133,'bb9b8ceb657ed81c',1,'NetworkOutHigh','10.151.10.32:9100','warning','2026-06-30 11:06:18','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.151.10.32:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:87.14301408131918 MB/s\', \'summary\': \'服务器 10.151.10.32:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-06-30T11:06:18.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'bb9b8ceb657ed81c\'}','2026-06-30 19:06:28'),(134,'bb9b8ceb657ed81c',2,'NetworkOutHigh','10.151.10.32:9100','warning','2026-06-30 11:06:18','2026-06-30 11:06:48','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.151.10.32:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:87.14738356272379 MB/s\', \'summary\': \'服务器 10.151.10.32:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-06-30T11:06:18.466Z\', \'endsAt\': \'2026-06-30T11:06:48.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'bb9b8ceb657ed81c\'}','2026-06-30 19:06:48'),(135,'bb9b8ceb657ed81c',1,'NetworkOutHigh','10.151.10.32:9100','warning','2026-06-30 11:42:18','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.151.10.32:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:87.45762557983399 MB/s\', \'summary\': \'服务器 10.151.10.32:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-06-30T11:42:18.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'bb9b8ceb657ed81c\'}','2026-06-30 19:42:28'),(136,'bb9b8ceb657ed81c',2,'NetworkOutHigh','10.151.10.32:9100','warning','2026-06-30 11:42:18','2026-06-30 11:43:03','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.151.10.32:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:88.14833755493164 MB/s\', \'summary\': \'服务器 10.151.10.32:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-06-30T11:42:18.466Z\', \'endsAt\': \'2026-06-30T11:43:03.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'bb9b8ceb657ed81c\'}','2026-06-30 19:43:08'),(137,'bb9b8ceb657ed81c',1,'NetworkOutHigh','10.151.10.32:9100','warning','2026-06-30 12:16:18','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.151.10.32:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:88.15669511159261 MB/s\', \'summary\': \'服务器 10.151.10.32:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-06-30T12:16:18.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'bb9b8ceb657ed81c\'}','2026-06-30 20:16:28'),(138,'bb9b8ceb657ed81c',2,'NetworkOutHigh','10.151.10.32:9100','warning','2026-06-30 12:16:18','2026-06-30 12:17:03','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.151.10.32:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:81.74637203216552 MB/s\', \'summary\': \'服务器 10.151.10.32:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-06-30T12:16:18.466Z\', \'endsAt\': \'2026-06-30T12:17:03.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'bb9b8ceb657ed81c\'}','2026-06-30 20:17:08'),(139,'c4722f8bb16c1c50',2,'ServerhomeDiskLess100G','10.150.111.240:9100','critical','2026-06-30 09:48:41','2026-06-30 12:36:11','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'ServerhomeDiskLess100G\', \'device\': \'/dev/mapper/vg_adc-lv_adc\', \'fstype\': \'ext4\', \'instance\': \'10.150.111.240:9100\', \'job\': \'nsschina2\', \'mountpoint\': \'/serverhome\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'节点 10.150.111.240:9100 挂载点 /serverhome 剩余:8.6382112768e+10 字节\', \'summary\': \'/serverhome 分区剩余空间不足100GB\'}, \'startsAt\': \'2026-06-30T09:48:41.205Z\', \'endsAt\': \'2026-06-30T12:36:11.205Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=node_filesystem_avail_bytes%7Bmountpoint%3D%22%2Fserverhome%22%7D+%3C+100+%2A+1024+%2A+1024+%2A+1024&g0.tab=1\', \'fingerprint\': \'c4722f8bb16c1c50\'}','2026-06-30 20:36:12'),(140,'c4722f8bb16c1c50',1,'ServerhomeDiskLess100G','10.150.111.240:9100','critical','2026-06-30 12:37:26','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'ServerhomeDiskLess100G\', \'device\': \'/dev/mapper/vg_adc-lv_adc\', \'fstype\': \'ext4\', \'instance\': \'10.150.111.240:9100\', \'job\': \'nsschina2\', \'mountpoint\': \'/serverhome\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'节点 10.150.111.240:9100 挂载点 /serverhome 剩余:8.6383693824e+10 字节\', \'summary\': \'/serverhome 分区剩余空间不足100GB\'}, \'startsAt\': \'2026-06-30T12:37:26.205Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=node_filesystem_avail_bytes%7Bmountpoint%3D%22%2Fserverhome%22%7D+%3C+100+%2A+1024+%2A+1024+%2A+1024&g0.tab=1\', \'fingerprint\': \'c4722f8bb16c1c50\'}','2026-06-30 20:37:36'),(141,'ecf53c73edc2d5e1',1,'NetworkInHigh','10.150.111.240:9100','warning','2026-06-30 15:33:33','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:211.9940071105957 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-06-30T15:33:33.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-06-30 23:33:43'),(142,'ecf53c73edc2d5e1',2,'NetworkInHigh','10.150.111.240:9100','warning','2026-06-30 15:33:33','2026-06-30 15:37:33','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:139.73818600972493 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-06-30T15:33:33.466Z\', \'endsAt\': \'2026-06-30T15:37:33.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-06-30 23:37:33'),(143,'ecf53c73edc2d5e1',1,'NetworkInHigh','10.150.111.240:9100','warning','2026-06-30 15:46:18','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:184.75856119791666 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-06-30T15:46:18.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-06-30 23:46:28'),(144,'ecf53c73edc2d5e1',2,'NetworkInHigh','10.150.111.240:9100','warning','2026-06-30 15:46:18','2026-06-30 15:46:48','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:184.69718653361002 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-06-30T15:46:18.466Z\', \'endsAt\': \'2026-06-30T15:46:48.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-06-30 23:46:48'),(145,'ecf53c73edc2d5e1',1,'NetworkInHigh','10.150.111.240:9100','warning','2026-06-30 15:58:18','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:348.48570238749187 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-06-30T15:58:18.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-06-30 23:58:28'),(146,'ecf53c73edc2d5e1',2,'NetworkInHigh','10.150.111.240:9100','warning','2026-06-30 15:58:18','2026-06-30 15:59:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:313.6650074005127 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-06-30T15:58:18.466Z\', \'endsAt\': \'2026-06-30T15:59:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-06-30 23:59:18'),(147,'3665b99716e5611c',1,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:07:33','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'bond0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:113.78571872711181 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 bond0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:07:33.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'3665b99716e5611c\'}','2026-07-01 16:07:43'),(148,'cbc9b6006e9043ca',1,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:07:33','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'eth0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:113.78554503122966 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 eth0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:07:33.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'cbc9b6006e9043ca\'}','2026-07-01 16:07:43'),(149,'3665b99716e5611c',2,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:07:33','2026-07-01 08:07:48','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'bond0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:113.78571872711181 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 bond0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:07:33.466Z\', \'endsAt\': \'2026-07-01T08:07:48.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'3665b99716e5611c\'}','2026-07-01 16:07:53'),(150,'cbc9b6006e9043ca',2,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:07:33','2026-07-01 08:07:48','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'eth0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:113.78554503122966 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 eth0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:07:33.466Z\', \'endsAt\': \'2026-07-01T08:07:48.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'cbc9b6006e9043ca\'}','2026-07-01 16:07:53'),(151,'3665b99716e5611c',1,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:15:18','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'bond0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:115.62577927907309 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 bond0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:15:18.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'3665b99716e5611c\'}','2026-07-01 16:15:28'),(152,'cbc9b6006e9043ca',1,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:15:18','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'eth0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:115.62566878000895 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 eth0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:15:18.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'cbc9b6006e9043ca\'}','2026-07-01 16:15:28'),(153,'3665b99716e5611c',2,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:15:18','2026-07-01 08:15:33','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'bond0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:115.62577927907309 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 bond0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:15:18.466Z\', \'endsAt\': \'2026-07-01T08:15:33.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'3665b99716e5611c\'}','2026-07-01 16:15:38'),(154,'cbc9b6006e9043ca',2,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:15:18','2026-07-01 08:15:33','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'eth0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:115.62566878000895 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 eth0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:15:18.466Z\', \'endsAt\': \'2026-07-01T08:15:33.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'cbc9b6006e9043ca\'}','2026-07-01 16:15:38'),(155,'3665b99716e5611c',1,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:49:48','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'bond0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:112.30544484456381 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 bond0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:49:48.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'3665b99716e5611c\'}','2026-07-01 16:49:58'),(156,'cbc9b6006e9043ca',1,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:49:48','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'eth0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:112.30539436340332 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 eth0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:49:48.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'cbc9b6006e9043ca\'}','2026-07-01 16:49:58'),(157,'3665b99716e5611c',2,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:49:48','2026-07-01 08:50:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'bond0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:84.46075611114502 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 bond0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:49:48.466Z\', \'endsAt\': \'2026-07-01T08:50:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'3665b99716e5611c\'}','2026-07-01 16:50:18'),(158,'cbc9b6006e9043ca',2,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:49:48','2026-07-01 08:50:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'eth0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:84.46059767405193 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 eth0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:49:48.466Z\', \'endsAt\': \'2026-07-01T08:50:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'cbc9b6006e9043ca\'}','2026-07-01 16:50:18'),(159,'3665b99716e5611c',1,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:51:48','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'bond0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:96.6174389521281 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 bond0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:51:48.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'3665b99716e5611c\'}','2026-07-01 16:51:58'),(160,'cbc9b6006e9043ca',1,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:51:48','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'eth0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:96.61736125946045 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 eth0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:51:48.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'cbc9b6006e9043ca\'}','2026-07-01 16:51:58'),(161,'3665b99716e5611c',2,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:51:48','2026-07-01 08:52:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'bond0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:83.55216751098632 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 bond0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:51:48.466Z\', \'endsAt\': \'2026-07-01T08:52:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'3665b99716e5611c\'}','2026-07-01 16:52:18'),(162,'cbc9b6006e9043ca',2,'NetworkInHigh','10.108.10.14:9100','warning','2026-07-01 08:51:48','2026-07-01 08:52:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'eth0\', \'instance\': \'10.108.10.14:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:83.55205764770508 MB/s\', \'summary\': \'服务器 10.108.10.14:9100 网卡 eth0 入流量过高\'}, \'startsAt\': \'2026-07-01T08:51:48.466Z\', \'endsAt\': \'2026-07-01T08:52:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'cbc9b6006e9043ca\'}','2026-07-01 16:52:18'),(163,'f7dc897706e78082',1,'NetworkOutHigh','10.150.111.240:9100','warning','2026-07-01 09:09:03','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:106.44059569040934 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-07-01T09:09:03.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'f7dc897706e78082\'}','2026-07-01 17:09:13'),(164,'f7dc897706e78082',2,'NetworkOutHigh','10.150.111.240:9100','warning','2026-07-01 09:09:03','2026-07-01 09:09:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:106.44059569040934 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-07-01T09:09:03.466Z\', \'endsAt\': \'2026-07-01T09:09:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'f7dc897706e78082\'}','2026-07-01 17:09:23'),(165,'f7dc897706e78082',1,'NetworkOutHigh','10.150.111.240:9100','warning','2026-07-01 21:19:03','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:113.88906548817953 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-07-01T21:19:03.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'f7dc897706e78082\'}','2026-07-02 05:19:14'),(166,'f7dc897706e78082',2,'NetworkOutHigh','10.150.111.240:9100','warning','2026-07-01 21:19:03','2026-07-01 21:19:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:113.88906548817953 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-07-01T21:19:03.466Z\', \'endsAt\': \'2026-07-01T21:19:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'f7dc897706e78082\'}','2026-07-02 05:19:23'),(167,'ecf53c73edc2d5e1',1,'NetworkInHigh','10.150.111.240:9100','warning','2026-07-02 01:52:18','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:261.5427293141683 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-07-02T01:52:18.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-07-02 09:52:28'),(168,'ecf53c73edc2d5e1',2,'NetworkInHigh','10.150.111.240:9100','warning','2026-07-02 01:52:18','2026-07-02 01:52:48','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:295.6116184234619 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-07-02T01:52:18.466Z\', \'endsAt\': \'2026-07-02T01:52:48.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-07-02 09:52:48'),(169,'ecf53c73edc2d5e1',1,'NetworkInHigh','10.150.111.240:9100','warning','2026-07-02 01:54:33','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:410.69542032877604 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-07-02T01:54:33.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-07-02 09:54:43'),(170,'ecf53c73edc2d5e1',2,'NetworkInHigh','10.150.111.240:9100','warning','2026-07-02 01:54:33','2026-07-02 01:55:33','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:379.1396380742391 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-07-02T01:54:33.466Z\', \'endsAt\': \'2026-07-02T01:55:33.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-07-02 09:55:33'),(171,'ecf53c73edc2d5e1',1,'NetworkInHigh','10.150.111.240:9100','warning','2026-07-02 01:56:48','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:201.59052505493165 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-07-02T01:56:48.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-07-02 09:56:58'),(172,'f7dc897706e78082',1,'NetworkOutHigh','10.150.111.240:9100','warning','2026-07-02 01:57:33','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:81.94307200113933 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-07-02T01:57:33.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'f7dc897706e78082\'}','2026-07-02 09:57:43'),(173,'f7dc897706e78082',2,'NetworkOutHigh','10.150.111.240:9100','warning','2026-07-02 01:57:33','2026-07-02 01:58:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:127.83006394704184 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-07-02T01:57:33.466Z\', \'endsAt\': \'2026-07-02T01:58:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'f7dc897706e78082\'}','2026-07-02 09:58:23'),(174,'ecf53c73edc2d5e1',2,'NetworkInHigh','10.150.111.240:9100','warning','2026-07-02 01:56:48','2026-07-02 01:59:03','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkInHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前入流量:91.95962390899658 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 入流量过高\'}, \'startsAt\': \'2026-07-02T01:56:48.466Z\', \'endsAt\': \'2026-07-02T01:59:03.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_receive_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'ecf53c73edc2d5e1\'}','2026-07-02 09:59:08'),(175,'f7dc897706e78082',1,'NetworkOutHigh','10.150.111.240:9100','warning','2026-07-02 04:48:48','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:95.15920327504476 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-07-02T04:48:48.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'f7dc897706e78082\'}','2026-07-02 12:48:59'),(176,'f7dc897706e78082',2,'NetworkOutHigh','10.150.111.240:9100','warning','2026-07-02 04:48:48','2026-07-02 04:49:03','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:95.15920327504476 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-07-02T04:48:48.466Z\', \'endsAt\': \'2026-07-02T04:49:03.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'f7dc897706e78082\'}','2026-07-02 12:49:08'),(177,'f7dc897706e78082',1,'NetworkOutHigh','10.150.111.240:9100','warning','2026-07-02 05:26:48','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:89.8120033899943 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-07-02T05:26:48.466Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'f7dc897706e78082\'}','2026-07-02 13:26:58'),(178,'f7dc897706e78082',2,'NetworkOutHigh','10.150.111.240:9100','warning','2026-07-02 05:26:48','2026-07-02 05:27:18','{\'status\': \'resolved\', \'labels\': {\'alertname\': \'NetworkOutHigh\', \'device\': \'ens192\', \'instance\': \'10.150.111.240:9100\', \'severity\': \'warning\'}, \'annotations\': {\'description\': \'当前出流量:87.17511711120605 MB/s\', \'summary\': \'服务器 10.150.111.240:9100 网卡 ens192 出流量过高\'}, \'startsAt\': \'2026-07-02T05:26:48.466Z\', \'endsAt\': \'2026-07-02T05:27:18.466Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=avg+by%28instance%2C+device%29+%28irate%28node_network_transmit_bytes_total%7Bdevice%21~%22lo%22%7D%5B1m%5D%29%29+%2F+1024+%2F+1024+%3E+80&g0.tab=1\', \'fingerprint\': \'f7dc897706e78082\'}','2026-07-02 13:27:18'),(179,'1a407ec049587595',1,'Host_Ping_Offline','10.150.10.111','critical','2026-07-02 05:48:41','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'Host_Ping_Offline\', \'instance\': \'10.150.10.111\', \'job\': \'blackbox_icmp_ping\', \'level\': \'Down\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'主机网络不通,ICMP探测失败,大概率整机宕机或网段隔离\', \'summary\': \'主机 Ping 失联 10.150.10.111\'}, \'startsAt\': \'2026-07-02T05:48:41.879Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bjob%3D~%22blackbox_icmp.%2A%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'1a407ec049587595\'}','2026-07-02 13:48:51'),(180,'9028b64ead67feea',1,'Host_Ping_Offline','10.150.10.116','critical','2026-07-02 05:48:41','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'Host_Ping_Offline\', \'instance\': \'10.150.10.116\', \'job\': \'blackbox_icmp_ping\', \'level\': \'Down\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'主机网络不通,ICMP探测失败,大概率整机宕机或网段隔离\', \'summary\': \'主机 Ping 失联 10.150.10.116\'}, \'startsAt\': \'2026-07-02T05:48:41.879Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bjob%3D~%22blackbox_icmp.%2A%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'9028b64ead67feea\'}','2026-07-02 13:48:51'),(181,'b737cd002529a3d8',1,'Host_Ping_Offline','10.150.10.114','critical','2026-07-02 05:48:56','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'Host_Ping_Offline\', \'instance\': \'10.150.10.114\', \'job\': \'blackbox_icmp_ping\', \'level\': \'Down\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'主机网络不通,ICMP探测失败,大概率整机宕机或网段隔离\', \'summary\': \'主机 Ping 失联 10.150.10.114\'}, \'startsAt\': \'2026-07-02T05:48:56.879Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bjob%3D~%22blackbox_icmp.%2A%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'b737cd002529a3d8\'}','2026-07-02 13:49:06'),(182,'a50e6b0c27d2fedc',1,'Host_Ping_Offline','10.150.10.118','critical','2026-07-02 05:48:56','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'Host_Ping_Offline\', \'instance\': \'10.150.10.118\', \'job\': \'blackbox_icmp_ping\', \'level\': \'Down\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'主机网络不通,ICMP探测失败,大概率整机宕机或网段隔离\', \'summary\': \'主机 Ping 失联 10.150.10.118\'}, \'startsAt\': \'2026-07-02T05:48:56.879Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bjob%3D~%22blackbox_icmp.%2A%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'a50e6b0c27d2fedc\'}','2026-07-02 13:49:06'),(183,'a3aa61c157fcd557',1,'Host_Ping_Offline','10.150.10.113','critical','2026-07-02 05:48:56','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'Host_Ping_Offline\', \'instance\': \'10.150.10.113\', \'job\': \'blackbox_icmp_ping\', \'level\': \'Down\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'主机网络不通,ICMP探测失败,大概率整机宕机或网段隔离\', \'summary\': \'主机 Ping 失联 10.150.10.113\'}, \'startsAt\': \'2026-07-02T05:48:56.879Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bjob%3D~%22blackbox_icmp.%2A%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'a3aa61c157fcd557\'}','2026-07-02 13:49:06'),(184,'4c4e26bba8f2101b',1,'Host_Ping_Offline','10.150.10.117','critical','2026-07-02 05:48:56','0001-01-01 00:00:00','{\'status\': \'firing\', \'labels\': {\'alertname\': \'Host_Ping_Offline\', \'instance\': \'10.150.10.117\', \'job\': \'blackbox_icmp_ping\', \'level\': \'Down\', \'severity\': \'critical\'}, \'annotations\': {\'description\': \'主机网络不通,ICMP探测失败,大概率整机宕机或网段隔离\', \'summary\': \'主机 Ping 失联 10.150.10.117\'}, \'startsAt\': \'2026-07-02T05:48:56.879Z\', \'endsAt\': \'0001-01-01T00:00:00Z\', \'generatorURL\': \'http://13a25c21fcc9:9090/graph?g0.expr=probe_success%7Bjob%3D~%22blackbox_icmp.%2A%22%7D+%3D%3D+0&g0.tab=1\', \'fingerprint\': \'4c4e26bba8f2101b\'}','2026-07-02 13:49:06'); +/*!40000 ALTER TABLE `alert_log` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sys_user` +-- + +DROP TABLE IF EXISTS `sys_user`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `sys_user` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `username` varchar(32) NOT NULL COMMENT '登录账号', + `password` varchar(72) NOT NULL COMMENT 'bcrypt加密密码', + `role` enum('admin','read') NOT NULL DEFAULT 'read' COMMENT '权限:admin管理员/read只读', + `last_login` datetime DEFAULT NULL, + `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + UNIQUE KEY `username` (`username`) +) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_user` +-- + +LOCK TABLES `sys_user` WRITE; +/*!40000 ALTER TABLE `sys_user` DISABLE KEYS */; +INSERT INTO `sys_user` VALUES (1,'admin','$2y$10$0/620vN3sCKVUfejEmZj3O/gS11T2QKCea3d07RkTpojKom9K8zye','admin','2026-07-02 13:58:42','2026-07-02 01:37:55'); +/*!40000 ALTER TABLE `sys_user` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +-- Dump completed on 2026-07-02 14:06:01 diff --git a/alert_dashboard/docker-compose.yml b/alert_dashboard/docker-compose.yml new file mode 100644 index 0000000..6024926 --- /dev/null +++ b/alert_dashboard/docker-compose.yml @@ -0,0 +1,20 @@ +services: + php: + image: php:7.4-fpm-alpine + volumes: + - ./www:/usr/share/nginx/html + - ./www/php-fpm/www.conf:/usr/local/etc/php-fpm.d/www.conf + command: > + sh -c "apk add --no-cache $PHPIZE_DEPS && docker-php-ext-install mysqli pdo_mysql && apk del $PHPIZE_DEPS && php-fpm" + + nginx: + image: nginx:stable-alpine + ports: + - "8080:80" + volumes: + - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro + - ./www:/usr/share/nginx/html + depends_on: + - php + # 使用nc探测tcp端口,不再用wget(wget不兼容fastcgi) + command: ["/bin/sh", "-c", "until nc -z php 9000; do echo '等待php-fpm端口就绪...'; sleep 1; done; nginx -g 'daemon off;'"] diff --git a/alert_dashboard/docker-compose.yml.3 b/alert_dashboard/docker-compose.yml.3 new file mode 100644 index 0000000..b69c2ae --- /dev/null +++ b/alert_dashboard/docker-compose.yml.3 @@ -0,0 +1,23 @@ +services: + php: + image: php:7.4-fpm-alpine + user: "101:101" + volumes: + - ./www:/usr/share/nginx/html + # 关键:覆盖fpm监听配置 + - ./www/php-fpm/www.conf:/usr/local/etc/php-fpm.d/www.conf + # 直接apk安装mysqli,不编译,避免gcc全家桶 + command: > + sh -c "apk add --no-cache php7.4-mysqli && php-fpm" + + nginx: + image: nginx:stable-alpine + ports: + - "8080:80" + volumes: + - ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro + - ./www:/usr/share/nginx/html + depends_on: + - php + # alpine自带wget等待php端口就绪 + command: ["/bin/sh", "-c", "until wget -q -T1 php:9000; do echo '等待php-fpm就绪...'; sleep 1; done; nginx -g 'daemon off;'"] diff --git a/alert_dashboard/docker-compose.yml.bak b/alert_dashboard/docker-compose.yml.bak new file mode 100644 index 0000000..34a22c7 --- /dev/null +++ b/alert_dashboard/docker-compose.yml.bak @@ -0,0 +1,17 @@ +services: + php: + image: php:7.4-fpm-alpine + volumes: + - ./www:/usr/share/nginx/html + # 新增安装mysqli扩展 + command: > + sh -c "docker-php-ext-install mysqli && docker-php-ext-enable mysqli && php-fpm" + nginx: + image: nginx:stable-alpine + ports: + - "8080:80" + volumes: + - ./nginx/default.conf:/etc/nginx/conf.d/default.conf + - ./www:/usr/share/nginx/html + depends_on: + - php diff --git a/alert_dashboard/nginx/default.conf b/alert_dashboard/nginx/default.conf new file mode 100644 index 0000000..24e2556 --- /dev/null +++ b/alert_dashboard/nginx/default.conf @@ -0,0 +1,54 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html index.htm index.php; + + # 开启Gzip全局压缩(文本类压缩60%-80%) + gzip on; + gzip_min_length 1k; + gzip_buffers 4 16k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml; + gzip_vary on; + + # PHP统一解析 + 优化参数 + location ~ \.php$ { + fastcgi_pass php:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + + # PHP-FPM优化:减少等待超时 + fastcgi_connect_timeout 60s; + fastcgi_send_timeout 60s; + fastcgi_read_timeout 60s; + fastcgi_buffer_size 128k; + fastcgi_buffers 4 128k; + fastcgi_busy_buffers_size 256k; + fastcgi_temp_file_write_size 256k; + } + + # 首页通用路由 + location / { + try_files $uri $uri/; + } + + # api目录转发 + location /api/ { + try_files $uri $uri/; + } + + # 静态资源统一长期缓存(图片、样式、脚本、字体合并规则) + location ~* \.(jpg|jpeg|png|gif|webp|ico|css|js|woff|woff2|ttf)$ { + expires 7d; + add_header Cache-Control "public"; + access_log off; + } + + # 禁止外部直接访问cache缓存目录,返回403 + location ^~ /cache/ { + deny all; + return 403; + } +} diff --git a/alert_dashboard/nginx/default.conf.1 b/alert_dashboard/nginx/default.conf.1 new file mode 100644 index 0000000..a716978 --- /dev/null +++ b/alert_dashboard/nginx/default.conf.1 @@ -0,0 +1,25 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html index.htm index.php; + + # 统一PHP解析规则(所有目录下.php文件都交给php-fpm解析) + location ~ \.php$ { + fastcgi_pass php:9000; + fastcgi_index index.php; + # 自动获取当前请求php文件真实路径,支持任意php文件 + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + + # 前端静态页面、目录访问 + location / { + try_files $uri $uri/; + } + + # /api 仅路由,不用重复配置php,上面正则会自动处理 + location /api/ { + try_files $uri $uri/; + } +} diff --git a/alert_dashboard/nginx/default.conf.2 b/alert_dashboard/nginx/default.conf.2 new file mode 100644 index 0000000..4a8eab3 --- /dev/null +++ b/alert_dashboard/nginx/default.conf.2 @@ -0,0 +1,53 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html index.htm index.php; + + # 开启Gzip全局压缩(文本类压缩60%-80%) + gzip on; + gzip_min_length 1k; + gzip_buffers 4 16k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml; + gzip_vary on; + + # PHP统一解析 + 优化参数 + location ~ \.php$ { + fastcgi_pass php:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + + # PHP-FPM优化:减少等待超时 + fastcgi_connect_timeout 60s; + fastcgi_send_timeout 60s; + fastcgi_read_timeout 60s; + fastcgi_buffer_size 128k; + fastcgi_buffers 4 128k; + fastcgi_busy_buffers_size 256k; + fastcgi_temp_file_write_size 256k; + } + + # 首页通用路由 + location / { + try_files $uri $uri/; + } + + # api目录转发 + location /api/ { + try_files $uri $uri/; + } + + # 静态资源专项缓存(头像、js、css、字体) + location ~* \.(jpg|jpeg|png|gif|webp|ico)$ { + expires 7d; + add_header Cache-Control "public, max-age=604800"; + access_log off; + } + location ~* \.(js|css|woff|woff2|ttf)$ { + expires 3d; + add_header Cache-Control "public, max-age=259200"; + access_log off; + } +} diff --git a/alert_dashboard/nginx/default.conf.3 b/alert_dashboard/nginx/default.conf.3 new file mode 100644 index 0000000..4fed3c4 --- /dev/null +++ b/alert_dashboard/nginx/default.conf.3 @@ -0,0 +1,45 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html index.htm index.php; + + gzip on; + gzip_min_length 1k; + gzip_buffers 4 16k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml; + gzip_vary on; + + location ~ \.php$ { + fastcgi_pass php:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + fastcgi_connect_timeout 60s; + fastcgi_send_timeout 60s; + fastcgi_read_timeout 60s; + fastcgi_buffer_size 128k; + fastcgi_buffers 4 128k; + } + + location / { + try_files $uri $uri/; + } + + location /api/ { + try_files $uri $uri/; + } + + # 静态资源长期缓存 + location ~* \.(jpg|jpeg|png|gif|webp|ico)$ { + expires 7d; + add_header Cache-Control "public, max-age=604800"; + access_log off; + } + location ~* \.(js|css|woff|woff2|ttf)$ { + expires 3d; + add_header Cache-Control "public, max-age=259200"; + access_log off; + } +} diff --git a/alert_dashboard/nginx/default.conf.4 b/alert_dashboard/nginx/default.conf.4 new file mode 100644 index 0000000..87becbd --- /dev/null +++ b/alert_dashboard/nginx/default.conf.4 @@ -0,0 +1,59 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html index.htm index.php; + + # 开启Gzip压缩,提升页面加载速度 + gzip on; + gzip_min_length 1k; + gzip_buffers 4 16k; + gzip_http_version 1.1; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml; + gzip_vary on; + + # PHP解析配置 + location ~ \.php$ { + fastcgi_pass php:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + + # 优化PHP连接超时、缓冲区 + fastcgi_connect_timeout 60s; + fastcgi_send_timeout 60s; + fastcgi_read_timeout 60s; + fastcgi_buffer_size 128k; + fastcgi_buffers 4 128k; + fastcgi_busy_buffers_size 256k; + fastcgi_temp_file_write_size 256k; + } + + # 首页通用路由 + location / { + try_files $uri $uri/; + } + + # API目录 + location /api/ { + try_files $uri $uri/; + } + + # 静态资源长期缓存(本地static目录js/css/图片) + location ~* \.(jpg|jpeg|png|gif|webp|ico)$ { + expires 7d; + add_header Cache-Control "public, max-age=604800"; + access_log off; + } + location ~* \.(js|css|woff|woff2|ttf)$ { + expires 3d; + add_header Cache-Control "public, max-age=259200"; + access_log off; + } + + # 禁止访问缓存目录cache,防止外部直接下载缓存文件 + location ^~ /cache/ { + deny all; + return 403; + } +} diff --git a/alert_dashboard/nginx/default.conf.bak b/alert_dashboard/nginx/default.conf.bak new file mode 100644 index 0000000..1ad7c02 --- /dev/null +++ b/alert_dashboard/nginx/default.conf.bak @@ -0,0 +1,19 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html index.htm index.php; + + # 前端静态页面 + location / { + try_files $uri $uri/; + } + + # PHP告警接口 + location /api { + fastcgi_pass php:9000; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html/api/index.php; + include fastcgi_params; + } +} diff --git a/alert_dashboard/python_crawler/mail.py b/alert_dashboard/python_crawler/mail.py new file mode 100644 index 0000000..0bbb2f8 --- /dev/null +++ b/alert_dashboard/python_crawler/mail.py @@ -0,0 +1,150 @@ +import poplib +import ssl +import email +from email.header import decode_header +import time +import pymysql + +# 关闭SSL校验,解决老Python握手重置 +#ssl._create_default_https_context = ssl._create_unverified_context + +# ========== 邮箱配置 自行修改 ========== +MAIL_HOST = "pop.163.com" +MAIL_PORT = 995 +MAIL_USER = "lyudream@163.com" +MAIL_PWD = "DVZxyF5NKCkR6fL5" + +# ========== 本地MariaDB配置 ========== +DB_HOST = "127.0.0.1" +DB_USER = "root" +DB_PWD = "hp93000" +DB_NAME = "alert_mail_stat" + +# 解码邮件标题、正文 +def decode_str(s): + value, charset = decode_header(s)[0] + if charset: + value = value.decode(charset) + return value + +# 获取邮件正文 +def get_email_content(msg): + content = "" + if msg.is_multipart(): + for part in msg.walk(): + content_type = part.get_content_type() + content_disposition = str(part.get("Content-Disposition")) + if content_type == 'text/plain' and 'attachment' not in content_disposition: + payload = part.get_payload(decode=True) + charset = part.get_charset() + if charset is None: + charset = 'utf-8' + content = payload.decode(charset, errors='ignore') + break + else: + payload = msg.get_payload(decode=True) + charset = msg.get_charset() + if charset is None: + charset = 'utf-8' + content = payload.decode(charset, errors='ignore') + return content + +# 简单解析告警关键字(适配Alertmanager中文邮件模板:告警名称、实例、级别) +def parse_alert_info(text): + alert_name = "" + instance = "" + severity = "unknown" + lines = text.splitlines() + for line in lines: + if "告警名称" in line: + alert_name = line.split(":")[-1].strip() + if "实例" in line: + instance = line.split(":")[-1].strip() + if "级别" in line: + s_val = line.split(":")[-1].strip() + if s_val.lower() in ["critical", "严重"]: + severity = "critical" + elif s_val.lower() in ["warning", "警告"]: + severity = "warning" + return alert_name, instance, severity + +# 主抓取逻辑 +def crawl_mail(): + # 连接数据库 + db = pymysql.connect(host=DB_HOST, user=DB_USER, password=DB_PWD, database=DB_NAME, charset='utf8mb4') + cursor = db.cursor() + + # 连接POP3邮箱 + pop_conn = poplib.POP3_SSL(MAIL_HOST, MAIL_PORT) + pop_conn.user(MAIL_USER) + pop_conn.pass_(MAIL_PWD) + total_num, total_size = pop_conn.stat() + print(f"当前邮箱总邮件数量: {total_num}") + + if total_num == 0: + print("暂无新邮件") + pop_conn.quit() + db.close() + return + + # 从最新一封开始遍历 + for idx in range(total_num, 0, -1): + # 获取邮件原始内容 + resp, raw_lines, octet = pop_conn.retr(idx) + raw_msg = b'\r\n'.join(raw_lines).decode('utf-8', errors='ignore') + msg = email.message_from_string(raw_msg) + + # 生成唯一标识去重 + mail_uid = msg.get("Message-ID", str(time.time())) + + # 查重,避免重复入库 + cursor.execute("SELECT id FROM alert_log WHERE mail_uid=%s", (mail_uid,)) + if cursor.fetchone(): + print(f"邮件{idx}已存在数据库,跳过") + continue + + # 解析标题、发件人 + subject = decode_str(msg.get("Subject", "")) + from_addr = msg.get("From", "") + + # ===================== 临时关闭发件人过滤,全部邮件放行测试 ===================== + # if "prometheusalert" not in from_addr.lower(): + # print(f"邮件{idx}发件人不匹配,跳过,发件人:{from_addr}") + # continue + + # 调试打印所有邮件基础信息 + print("\n========================================") + print(f"【调试】正在处理第 {idx} 封邮件") + print(f"发件人:{from_addr}") + print(f"邮件标题:{subject}") + print("========================================") + + # 正文解析告警字段 + body = get_email_content(msg) + alert_name, instance, severity = parse_alert_info(body) + if not alert_name: + print(f"邮件{idx}未识别到【告警名称】关键字,跳过") + continue + + # 区分触发/恢复告警 + alert_type = 1 + if "恢复" in subject or "resolved" in subject.lower(): + alert_type = 2 + + now = time.strftime("%Y-%m-%d %H:%M:%S") + sql = """ + INSERT INTO alert_log + (mail_uid, alert_type, alert_name, instance, severity, starts_at, ends_at, content, receive_time) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) + """ + cursor.execute(sql, (mail_uid, alert_type, alert_name, instance, severity, now, None, body, now)) + db.commit() + print(f"入库成功 | 告警:{alert_name} 实例:{instance}") + + pop_conn.quit() + cursor.close() + db.close() + print("\n本次抓取全部完成") + +if __name__ == "__main__": + crawl_mail() diff --git a/alert_dashboard/python_crawler/mail_crawler.py b/alert_dashboard/python_crawler/mail_crawler.py new file mode 100644 index 0000000..e8be1e7 --- /dev/null +++ b/alert_dashboard/python_crawler/mail_crawler.py @@ -0,0 +1,140 @@ +import poplib +import ssl +import email +from email.header import decode_header +import time +import pymysql + +# 关闭SSL校验,解决老Python握手重置 +ssl._create_default_https_context = ssl._create_unverified_context + +# ========== 邮箱配置 自行修改 ========== +MAIL_HOST = "pop.163.com" +MAIL_PORT = 995 +MAIL_USER = "lyudream@163.com" +MAIL_PWD = "DVZxyF5NKCkR6fL5" + +# ========== 本地MariaDB配置 ========== +DB_HOST = "127.0.0.1" +DB_USER = "root" +DB_PWD = "hp93000" +DB_NAME = "alert_mail_stat" + +# 解码邮件标题、正文 +def decode_str(s): + value, charset = decode_header(s)[0] + if charset: + value = value.decode(charset) + return value + +# 获取邮件正文 +def get_email_content(msg): + content = "" + if msg.is_multipart(): + for part in msg.walk(): + content_type = part.get_content_type() + content_disposition = str(part.get("Content-Disposition")) + if content_type == 'text/plain' and 'attachment' not in content_disposition: + payload = part.get_payload(decode=True) + charset = part.get_charset() + if charset is None: + charset = 'utf-8' + content = payload.decode(charset, errors='ignore') + break + else: + payload = msg.get_payload(decode=True) + charset = msg.get_charset() + if charset is None: + charset = 'utf-8' + content = payload.decode(charset, errors='ignore') + return content + +# 简单解析告警关键字(适配Alertmanager中文邮件模板:告警名称、实例、级别) +def parse_alert_info(text): + alert_name = "" + instance = "" + severity = "unknown" + lines = text.splitlines() + for line in lines: + if "告警名称" in line: + alert_name = line.split(":")[-1].strip() + if "实例" in line: + instance = line.split(":")[-1].strip() + if "级别" in line: + s_val = line.split(":")[-1].strip() + if s_val.lower() in ["critical", "严重"]: + severity = "critical" + elif s_val.lower() in ["warning", "警告"]: + severity = "warning" + return alert_name, instance, severity + +# 主抓取逻辑 +def crawl_mail(): + # 连接数据库 + db = pymysql.connect(host=DB_HOST, user=DB_USER, password=DB_PWD, database=DB_NAME, charset='utf8mb4') + cursor = db.cursor() + + # 连接POP3邮箱 + pop_conn = poplib.POP3_SSL(MAIL_HOST, MAIL_PORT) + pop_conn.user(MAIL_USER) + pop_conn.pass_(MAIL_PWD) + total_num, total_size = pop_conn.stat() + print(f"当前邮箱总邮件数量: {total_num}") + + if total_num == 0: + print("暂无新邮件") + pop_conn.quit() + db.close() + return + + # 从最新一封开始遍历 + for idx in range(total_num, 0, -1): + # 获取邮件原始内容 + resp, raw_lines, octet = pop_conn.retr(idx) + raw_msg = b'\r\n'.join(raw_lines).decode('utf-8', errors='ignore') + msg = email.message_from_string(raw_msg) + + # 生成唯一标识去重 + mail_uid = msg.get("Message-ID", str(time.time())) + + # 查重,避免重复入库 + cursor.execute("SELECT id FROM alert_log WHERE mail_uid=%s", (mail_uid,)) + if cursor.fetchone(): + continue + + # 解析标题、发件人 + subject = decode_str(msg.get("Subject", "")) + from_addr = msg.get("From", "") + # 过滤非告警发件人(自行修改匹配你的Alertmanager发件地址) + if "prometheusalert" not in from_addr.lower(): + continue + + # 正文解析告警字段 + body = get_email_content(msg) + alert_name, instance, severity = parse_alert_info(body) + if not alert_name: + print(f"邮件{idx}未识别到告警,跳过") + continue + + # 区分触发/恢复告警 + alert_type = 1 + if "恢复" in subject or "resolved" in subject.lower(): + alert_type = 2 + + now = time.strftime("%Y-%m-%d %H:%M:%S") + sql = """ + INSERT INTO alert_log + (mail_uid, alert_type, alert_name, instance, severity, starts_at, ends_at, content, receive_time) + VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s) + """ + cursor.execute(sql, (mail_uid, alert_type, alert_name, instance, severity, now, None, body, now)) + db.commit() + print(f"入库成功 | 告警:{alert_name} 实例:{instance}") + + pop_conn.quit() + cursor.close() + db.close() + print("本次抓取完成\n") + +if __name__ == "__main__": + crawl_mail() diff --git a/alert_dashboard/python_crawler/test.py b/alert_dashboard/python_crawler/test.py new file mode 100644 index 0000000..a47370d --- /dev/null +++ b/alert_dashboard/python_crawler/test.py @@ -0,0 +1,77 @@ +import poplib +import ssl +import email +from email.header import decode_header + +# SSL兼容修复 +try: + ctx = ssl._create_unverified_context +except: + ctx = None +if ctx: + ssl._create_default_https_context = ctx + +# ========= 修改这里你的邮箱信息 ========= +MAIL_HOST = "pop.163.com" +MAIL_PORT = 995 +MAIL_USER = "lyudream@163.com" +MAIL_PWD = "DVZxyF5NKCkR6fL5" + +def decode_text(s): + value, charset = decode_header(s)[0] + if charset: + value = value.decode(charset) + return value + +if __name__ == "__main__": + print("=== 开始连接pop.163.com:995 ===") + try: + pop = poplib.POP3_SSL(MAIL_HOST, MAIL_PORT) + print("连接服务器成功,正在登录...") + pop.user(MAIL_USER) + pop.pass_(MAIL_PWD) + print("登录邮箱成功!") + + total, _ = pop.stat() + print(f"邮箱共有邮件:{total} 封\n") + if total == 0: + print("邮箱无邮件") + pop.quit() + exit() + + # 从最新邮件遍历 + for i in range(total, 0, -1): + resp, lines, octet = pop.retr(i) + msg_raw = b"\r\n".join(lines).decode("utf-8", errors="ignore") + msg = email.message_from_string(msg_raw) + + subject = decode_text(msg.get("Subject", "")) + from_addr = msg.get("From", "") + + # 取正文 + body = "" + if msg.is_multipart(): + for part in msg.walk(): + t = part.get_content_type() + disp = str(part.get("Content-Disposition")) + if t == "text/plain" and "attachment" not in disp: + payload = part.get_payload(decode=True) + body = payload.decode("utf-8", errors="ignore") + break + else: + payload = msg.get_payload(decode=True) + body = payload.decode("utf-8", errors="ignore") + + print("========================================") + print(f"第{i}封邮件") + print(f"发件人:{from_addr}") + print(f"标题:{subject}") + print(f"正文内容:\n{body}") + print("========================================\n") + + pop.quit() + print("全部读取完成") + + except Exception as e: + print("连接/登录失败,错误信息:") + print(str(e)) diff --git a/alert_dashboard/webhook.log b/alert_dashboard/webhook.log new file mode 100644 index 0000000..36c4f15 --- /dev/null +++ b/alert_dashboard/webhook.log @@ -0,0 +1,205 @@ +nohup: ignoring input + * Serving Flask app "webhook" (lazy loading) + * Environment: production + WARNING: This is a development server. Do not use it in a production deployment. + Use a production WSGI server instead. + * Debug mode: off + * Running on http://0.0.0.0:909/ (Press CTRL+C to quit) +10.150.117.190 - - [02/Jul/2026 14:34:17] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:34:17] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:34:17] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:34:17] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:35:51] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:35:51] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:35:51] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:35:51] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:42:38] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:42:41] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:42:41] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:42:41] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:42:41] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:42:42] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:42:42] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:42:48] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:41] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:41] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:41] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:41] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:41] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:41] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:43:56] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:11] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:11] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:11] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:44:18] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:45:06] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:45:06] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:45:06] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:45:06] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:45:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:45:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:45:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:56:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:56:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:56:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:56:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:56:36] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:56:46] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:58:57] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:59:12] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:59:12] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 14:59:12] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 17:28:04] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 19:23:43] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [02/Jul/2026 19:24:03] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [03/Jul/2026 12:30:28] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [03/Jul/2026 12:30:38] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [03/Jul/2026 15:19:43] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [03/Jul/2026 15:19:53] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [04/Jul/2026 13:44:13] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [04/Jul/2026 13:50:23] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [04/Jul/2026 13:56:43] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [04/Jul/2026 13:57:03] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [04/Jul/2026 14:26:13] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [04/Jul/2026 14:28:53] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [04/Jul/2026 14:33:28] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [04/Jul/2026 14:33:38] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.44.16 - - [06/Jul/2026 09:36:01] "[33mGET / HTTP/1.1[0m" 404 - +10.150.44.16 - - [06/Jul/2026 09:36:02] "[33mGET /favicon.ico HTTP/1.1[0m" 404 - +10.150.117.190 - - [06/Jul/2026 11:21:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 11:21:31] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 11:25:06] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 11:28:03] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 11:28:51] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 11:30:23] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 11:30:31] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 11:31:46] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 12:21:51] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 13:54:28] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 13:55:18] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:44:18] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:45:06] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:45:26] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:45:28] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:50:58] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:50:58] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:52:38] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:52:38] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:54:13] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:54:13] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:54:23] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 14:54:23] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 15:13:13] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 15:13:33] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 15:16:28] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 15:16:28] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 15:18:48] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 15:18:48] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 15:49:28] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 15:50:48] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 16:00:58] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 16:01:08] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [06/Jul/2026 17:22:05] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 09:42:58] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 09:43:38] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 09:46:13] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 09:46:23] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 10:11:28] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 10:11:28] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 10:13:08] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 10:13:08] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 10:23:58] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 10:24:43] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 10:25:18] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 10:29:23] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 10:50:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 11:21:01] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 11:24:13] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 11:24:23] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 11:37:21] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 11:37:31] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 11:43:43] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 11:44:06] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 11:44:43] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 11:45:03] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 11:46:03] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 12:12:58] "[37mPOST /alert HTTP/1.1[0m" 200 - +10.150.117.190 - - [07/Jul/2026 12:13:48] "[37mPOST /alert HTTP/1.1[0m" 200 - diff --git a/alert_dashboard/webhook.py b/alert_dashboard/webhook.py new file mode 100644 index 0000000..bd4549a --- /dev/null +++ b/alert_dashboard/webhook.py @@ -0,0 +1,65 @@ +from flask import Flask, request, jsonify +import pymysql + +app = Flask(__name__) + +def get_db(): + return pymysql.connect( + host="127.0.0.1", + user="root", + password="hp93000", + database="alert_mail_stat", + charset="utf8mb4" + ) + +@app.route("/alert", methods=["POST"]) +def alert_receive(): + try: + data = request.get_json() + alerts = data.get("alerts", []) + insert_num = 0 + update_num = 0 + db = get_db() + cur = db.cursor() + + for alert in alerts: + fp = alert["fingerprint"] + alert_name = alert["labels"].get("alertname", "") + instance = alert["labels"].get("instance", "") + severity = alert["labels"].get("severity", "warning") + status = alert["status"] + start_at = alert.get("startsAt", "") + end_at = alert.get("endsAt") + alert_type = 2 if status == "resolved" else 1 + content_str = str(alert) + + # 1. 原有告警入库逻辑不变 + sql = """ + INSERT INTO alert_log(mail_uid,alert_type,alert_name,instance,severity,starts_at,ends_at,content,receive_time) + VALUES(%s,%s,%s,%s,%s,%s,%s,%s,NOW()) + """ + cur.execute(sql, (fp, alert_type, alert_name, instance, severity, start_at, end_at, content_str)) + insert_num += 1 + + # 2. 新增:同步监控规则,存在则置为启用1 + if alert_name: + sync_rule_sql = """ + INSERT INTO monitor_rule (rule_name, status) + VALUES (%s, 1) + ON DUPLICATE KEY UPDATE status = 1 + """ + cur.execute(sync_rule_sql, [alert_name]) + + db.commit() + cur.close() + db.close() + return jsonify({"code": 200, "insert": insert_num, "update": update_num}) + + except Exception as e: + if "db" in locals(): + db.rollback() + db.close() + return jsonify({"code": 500, "msg": str(e)}), 200 + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=909, debug=False) diff --git a/alert_dashboard/webhook.py.bak b/alert_dashboard/webhook.py.bak new file mode 100644 index 0000000..dfcd13c --- /dev/null +++ b/alert_dashboard/webhook.py.bak @@ -0,0 +1,47 @@ +from flask import Flask, request, jsonify +import pymysql + +app = Flask(__name__) + +def get_db(): + return pymysql.connect( + host="127.0.0.1", + user="root", + password="hp93000", + database="alert_mail_stat", + charset="utf8mb4" + ) + +@app.route("/alert", methods=["POST"]) +def alert_in(): + raw = request.get_json() + alerts = raw.get("alerts", []) + add_cnt = 0 + db = get_db() + cur = db.cursor() + for alert in alerts: + fp = alert["fingerprint"] + alert_name = alert["labels"].get("alertname", "") + instance = alert["labels"].get("instance", "") + severity = alert["labels"].get("severity", "warning") + status = alert["status"] + start = alert.get("startsAt", "") + end = alert.get("endsAt") + type_val = 2 if status == "resolved" else 1 + + cur.execute("SELECT id FROM alert_log WHERE mail_uid=%s", (fp,)) + if cur.fetchone(): + continue + sql = """ + INSERT INTO alert_log(mail_uid,alert_type,alert_name,instance,severity,starts_at,ends_at,content,receive_time) + VALUES(%s,%s,%s,%s,%s,%s,%s,%s,NOW()) + """ + cur.execute(sql, (fp, type_val, alert_name, instance, severity, start, end, str(alert))) + add_cnt += 1 + db.commit() + cur.close() + db.close() + return jsonify({"code": 200, "insert_count": add_cnt}) + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=909, debug=False) diff --git a/alert_dashboard/webhook.py1 b/alert_dashboard/webhook.py1 new file mode 100644 index 0000000..fe3ec99 --- /dev/null +++ b/alert_dashboard/webhook.py1 @@ -0,0 +1,47 @@ +from flask import Flask, request, jsonify +import pymysql + +app = Flask(__name__) + +def get_db(): + return pymysql.connect( + host="127.0.0.1", + user="root", + password="hp93000", + database="alert_mail_stat", + charset="utf8mb4" + ) + +@app.route("/alert", methods=["POST"]) +def alert_receive(): + data = request.get_json() + alerts = data.get("alerts", []) + insert_num = 0 + db = get_db() + cur = db.cursor() + for alert in alerts: + fp = alert["fingerprint"] + alert_name = alert["labels"].get("alertname", "") + instance = alert["labels"].get("instance", "") + severity = alert["labels"].get("severity", "warning") + status = alert["status"] + start_at = alert.get("startsAt", "") + end_at = alert.get("endsAt") + alert_type = 2 if status == "resolved" else 1 + + cur.execute("SELECT id FROM alert_log WHERE mail_uid=%s", (fp,)) + if cur.fetchone(): + continue + sql = """ + INSERT INTO alert_log(mail_uid,alert_type,alert_name,instance,severity,starts_at,ends_at,content,receive_time) + VALUES(%s,%s,%s,%s,%s,%s,%s,%s,NOW()) + """ + cur.execute(sql, (fp, alert_type, alert_name, instance, severity, start_at, end_at, str(alert))) + insert_num += 1 + db.commit() + cur.close() + db.close() + return jsonify({"code": 200, "insert": insert_num}) + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=909, debug=False) diff --git a/alert_dashboard/webhook.py2 b/alert_dashboard/webhook.py2 new file mode 100644 index 0000000..ef29e76 --- /dev/null +++ b/alert_dashboard/webhook.py2 @@ -0,0 +1,72 @@ +from flask import Flask, request, jsonify +import pymysql + +app = Flask(__name__) + +def get_db(): + return pymysql.connect( + host="127.0.0.1", + user="root", + password="hp93000", + database="alert_mail_stat", + charset="utf8mb4" + ) + +@app.route("/alert", methods=["POST"]) +def alert_receive(): + try: + data = request.get_json() + alerts = data.get("alerts", []) + insert_num = 0 + update_num = 0 + db = get_db() + cur = db.cursor() + + for alert in alerts: + fp = alert["fingerprint"] + alert_name = alert["labels"].get("alertname", "") + instance = alert["labels"].get("instance", "") + severity = alert["labels"].get("severity", "warning") + status = alert["status"] + start_at = alert.get("startsAt", "") + end_at = alert.get("endsAt") + alert_type = 2 if status == "resolved" else 1 + content_str = str(alert) + + cur.execute(""" + SELECT id FROM alert_log + WHERE mail_uid=%s AND DATE(receive_time) = CURDATE() + ORDER BY id DESC LIMIT 1 + """, (fp,)) + row = cur.fetchone() + + if status == "resolved": + if row: + cur.execute(""" + UPDATE alert_log + SET alert_type=%s, ends_at=%s, content=%s + WHERE mail_uid=%s AND id=%s + """, (alert_type, end_at, content_str, fp, row[0])) + update_num += 1 + else: + # 删掉唯一索引后,每次故障直接插入,不再判断是否存在 + sql = """ + INSERT INTO alert_log(mail_uid,alert_type,alert_name,instance,severity,starts_at,ends_at,content,receive_time) + VALUES(%s,%s,%s,%s,%s,%s,%s,%s,NOW()) + """ + cur.execute(sql, (fp, alert_type, alert_name, instance, severity, start_at, end_at, content_str)) + insert_num += 1 + + db.commit() + cur.close() + db.close() + return jsonify({"code": 200, "insert": insert_num, "update": update_num}) + + except Exception as e: + if "db" in locals(): + db.rollback() + db.close() + return jsonify({"code": 500, "msg": str(e)}), 200 + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=909, debug=False) diff --git a/alert_dashboard/webhook.py3 b/alert_dashboard/webhook.py3 new file mode 100644 index 0000000..781258b --- /dev/null +++ b/alert_dashboard/webhook.py3 @@ -0,0 +1,55 @@ +from flask import Flask, request, jsonify +import pymysql + +app = Flask(__name__) + +def get_db(): + return pymysql.connect( + host="127.0.0.1", + user="root", + password="hp93000", + database="alert_mail_stat", + charset="utf8mb4" + ) + +@app.route("/alert", methods=["POST"]) +def alert_receive(): + try: + data = request.get_json() + alerts = data.get("alerts", []) + insert_num = 0 + update_num = 0 + db = get_db() + cur = db.cursor() + + for alert in alerts: + fp = alert["fingerprint"] + alert_name = alert["labels"].get("alertname", "") + instance = alert["labels"].get("instance", "") + severity = alert["labels"].get("severity", "warning") + status = alert["status"] + start_at = alert.get("startsAt", "") + end_at = alert.get("endsAt") + alert_type = 2 if status == "resolved" else 1 + content_str = str(alert) + + sql = """ + INSERT INTO alert_log(mail_uid,alert_type,alert_name,instance,severity,starts_at,ends_at,content,receive_time) + VALUES(%s,%s,%s,%s,%s,%s,%s,%s,NOW()) + """ + cur.execute(sql, (fp, alert_type, alert_name, instance, severity, start_at, end_at, content_str)) + insert_num += 1 + + db.commit() + cur.close() + db.close() + return jsonify({"code": 200, "insert": insert_num, "update": update_num}) + + except Exception as e: + if "db" in locals(): + db.rollback() + db.close() + return jsonify({"code": 500, "msg": str(e)}), 200 + +if __name__ == "__main__": + app.run(host="0.0.0.0", port=909, debug=False) diff --git a/alert_dashboard/www/admin.php b/alert_dashboard/www/admin.php new file mode 100644 index 0000000..d63b483 --- /dev/null +++ b/alert_dashboard/www/admin.php @@ -0,0 +1,619 @@ + $expire)) { + session_destroy(); + header("Location: login.php"); + exit; +} +$_SESSION['login_time'] = time(); +$userName = htmlspecialchars($_SESSION['username']); +$userRole = $_SESSION['role']; +$roleText = $userRole === 'admin' ? '管理员' : '只读用户'; + +// 数据库读取现有配置,页面打开自动回填 +$dbHost = "10.150.117.190"; +$dbUser = "root"; +$dbPwd = "hp93000"; +$dbName = "alert_mail_stat"; +$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName); +mysqli_set_charset($conn, "utf8mb4"); +$res = mysqli_query($conn, "SELECT k,v FROM sys_config"); +$config = []; +while ($row = mysqli_fetch_assoc($res)) { + $config[$row['k']] = $row['v']; +} +mysqli_close($conn); + +// 赋值表单默认值 +$pageTitle = $config['page_title'] ?? "告警监控系统"; +$themeColor = $config['theme_color'] ?? "#409eff"; +$pageSize = $config['page_size'] ?? "20"; +$footerText = $config['footer_text'] ?? ""; +$sidebarLinks = $config['sidebar_links'] ?? ""; +?> + + +
+ + +| 数据加载中... |
| 数据加载中... |
| 数据加载中... |
| Loading... |
|
+
+ Loading...
+ |
+
关联告警ID:$relateId
+标题:$title
+$content"; + // 此处接入PHPMailer发送逻辑 +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/alert_dashboard/www/api/work_order_api.php.1 b/alert_dashboard/www/api/work_order_api.php.1 new file mode 100644 index 0000000..62697b4 --- /dev/null +++ b/alert_dashboard/www/api/work_order_api.php.1 @@ -0,0 +1,117 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 数据库本地连接,避免远程拦截 +$dbHost = '10.150.117.190'; +$dbUser = 'root'; +$dbPwd = 'hp93000'; +$dbName = 'monitor'; +$dbPort = 3306; +$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName, $dbPort); +if (!$conn) { + echo json_encode([ + 'code' => 500, + 'msg' => '数据库连接失败:' . mysqli_connect_error() + ]); + exit; +} +mysqli_set_charset($conn, 'utf8mb4'); + +// 获取当前用户是否管理员 +$isAdmin = 0; +$roleSql = "SELECT r.is_admin FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($conn, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRow = mysqli_fetch_assoc(mysqli_stmt_get_result($stmtRole)); +if ($roleRow) $isAdmin = intval($roleRow['is_admin']); + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// 1、新建工单(修复第50行引用报错) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $status = 1; // 单独定义变量,不能直接写1传入bind_param + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,status,create_time,update_time) VALUES (?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($conn, $sql); + // 全部使用变量传参,禁止直接写数字/字符串 + mysqli_stmt_bind_param($stmt, 'ssssi', $title, $content, $loginUid, $assign, $status); + $ok = mysqli_stmt_execute($stmt); + if ($ok) { + echo json_encode(['code' => 0, 'msg' => '工单提交成功']); + } else { + echo json_encode(['code' => 500, 'msg' => '提交失败:' . mysqli_error($conn)]); + } + exit; +} + +// 2、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + // 普通用户权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($conn, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 3、管理员分发工单 +if ($action === "assign_order") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 无效操作 +echo json_encode(['code' => 400, 'msg' => '无效操作']); +mysqli_close($conn); +exit; +?> diff --git a/alert_dashboard/www/api/work_order_api.php.2.0 b/alert_dashboard/www/api/work_order_api.php.2.0 new file mode 100644 index 0000000..c80f790 --- /dev/null +++ b/alert_dashboard/www/api/work_order_api.php.2.0 @@ -0,0 +1,144 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 数据库本地连接 +$dbHost = '10.150.117.190'; +$dbUser = 'root'; +$dbPwd = 'hp93000'; +$dbName = 'monitor'; +$dbPort = 3306; +$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName, $dbPort); +if (!$conn) { + echo json_encode([ + 'code' => 500, + 'msg' => '数据库连接失败:' . mysqli_connect_error() + ]); + exit; +} +mysqli_set_charset($conn, 'utf8mb4'); + +// 获取当前用户是否管理员 +$isAdmin = 0; +$roleSql = "SELECT r.is_admin FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($conn, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRow = mysqli_fetch_assoc(mysqli_stmt_get_result($stmtRole)); +if ($roleRow) $isAdmin = intval($roleRow['is_admin']); + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// 1、新建工单 +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $status = 1; + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,status,create_time,update_time) VALUES (?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'ssssi', $title, $content, $loginUid, $assign, $status); + $ok = mysqli_stmt_execute($stmt); + if ($ok) { + echo json_encode(['code' => 0, 'msg' => '工单提交成功']); + } else { + echo json_encode(['code' => 500, 'msg' => '提交失败:' . mysqli_error($conn)]); + } + exit; +} + +// 2、编辑工单(新增,支持修改标题、内容备注、分配人) +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($conn, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'sssi', $title, $content, $assign, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单修改备注保存成功']); + exit; +} + +// 3、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($conn, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 4、管理员分发工单 +if ($action === "assign_order") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +echo json_encode(['code' => 400, 'msg' => '无效操作']); +mysqli_close($conn); +exit; +?> diff --git a/alert_dashboard/www/api/work_order_api.php.3.0 b/alert_dashboard/www/api/work_order_api.php.3.0 new file mode 100644 index 0000000..9230ec7 --- /dev/null +++ b/alert_dashboard/www/api/work_order_api.php.3.0 @@ -0,0 +1,146 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单数据库本地连接 +$dbHost = '10.150.117.190'; +$dbUser = 'root'; +$dbPwd = 'hp93000'; +$dbName = 'monitor'; +$dbPort = 3306; +$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName, $dbPort); +if (!$conn) { + echo json_encode([ + 'code' => 500, + 'msg' => '数据库连接失败:' . mysqli_connect_error() + ]); + exit; +} +mysqli_set_charset($conn, 'utf8mb4'); + +// 判断管理员 +$isAdmin = 0; +$roleSql = "SELECT r.is_admin FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($conn, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRow = mysqli_fetch_assoc(mysqli_stmt_get_result($stmtRole)); +if ($roleRow) $isAdmin = intval($roleRow['is_admin']); + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// 1、新建工单(新增relate_alert_id字段) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $status = 1; + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,status,create_time,update_time) VALUES (?,?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'sssssi', $title, $content, $loginUid, $assign, $relateAlertId, $status); + $ok = mysqli_stmt_execute($stmt); + if ($ok) { + echo json_encode(['code' => 0, 'msg' => '工单提交成功']); + } else { + echo json_encode(['code' => 500, 'msg' => '提交失败:' . mysqli_error($conn)]); + } + exit; +} + +// 2、编辑工单(不修改关联告警ID) +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($conn, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'sssi', $title, $content, $assign, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单修改备注保存成功']); + exit; +} + +// 3、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($conn, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 4、管理员分发工单 +if ($action === "assign_order") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 无匹配操作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($conn); +exit; +?> diff --git a/alert_dashboard/www/api/work_order_api.php.4.0 b/alert_dashboard/www/api/work_order_api.php.4.0 new file mode 100644 index 0000000..cc5d80f --- /dev/null +++ b/alert_dashboard/www/api/work_order_api.php.4.0 @@ -0,0 +1,247 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单业务库 monitor +$dbWorkHost = 'localhost'; +$dbWorkUser = 'root'; +$dbWorkPwd = 'hp93000'; +$dbWorkName = 'monitor'; +$connWork = mysqli_connect($dbWorkHost, $dbWorkUser, $dbWorkPwd, $dbWorkName); +if (!$connWork) { + echo json_encode(['code' => 500, 'msg' => '工单数据库连接失败:' . mysqli_connect_error()]); + exit; +} +mysqli_set_charset($connWork, 'utf8mb4'); + +// 配置库 alert_mail_stat +$dbConfHost = "10.150.117.190"; +$dbConfUser = "root"; +$dbConfPwd = "hp93000"; +$dbConfName = "alert_mail_stat"; +$connConf = mysqli_connect($dbConfHost, $dbConfUser, $dbConfPwd, $dbConfName); +mysqli_set_charset($connConf, "utf8mb4"); + +// 获取当前用户角色权限 +$isAdmin = 0; +$permList = ""; +$roleSql = "SELECT r.is_admin, r.perm_list FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($connWork, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRow = mysqli_fetch_assoc(mysqli_stmt_get_result($stmtRole)); +if ($roleRow) { + $isAdmin = intval($roleRow['is_admin']); + $permList = $roleRow['perm_list']; +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// 1、获取当前活跃告警(解决前端下拉空白) +if ($action === "get_firing_alert") { + $alertSql = "SELECT alert_id,alert_name,instance FROM alert_firing ORDER BY receive_time DESC LIMIT 100"; + $res = mysqli_query($connConf, $alertSql); + $list = []; + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// 2、新建工单(新增通知用户、通知渠道、关联告警) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + $status = 1; + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,notify_user,notify_channel,status,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssssi', $title, $content, $loginUid, $assign, $relateAlertId, $notifyUser, $notifyChannel, $status); + $ok = mysqli_stmt_execute($stmt); + if (!$ok) { + echo json_encode(['code' => 500, 'msg' => '创建失败:' . mysqli_error($connWork)]); + exit; + } + $orderId = mysqli_insert_id($connWork); + // 发送通知 + sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单提交成功,通知已下发']); + exit; +} + +// 3、编辑工单(更新通知、不修改关联告警) +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,notify_user=?,notify_channel=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssi', $title, $content, $assign, $notifyUser, $notifyChannel, $id); + mysqli_stmt_execute($stmt); + sendWorkOrderNotify($connConf, $connWork, $id, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单保存成功,通知已更新下发']); + exit; +} + +// 4、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 5、管理员分发工单 +if ($action === "assign_order") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 通知发送统一函数 +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 获取SMTP配置 + $smtpRow = mysqli_fetch_assoc(mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1")); + $smtpConfig = $smtpRow ?? []; + + // 获取工单详情 + $orderRow = mysqli_fetch_assoc(mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId")); + $title = $orderRow['title']; + $content = $orderRow['content']; + + // 遍历渠道发送 + foreach ($channelArr as $ch) { + switch ($ch) { + case "mail": + sendMailNotify($smtpConfig, $userArr, $title, $content); + break; + case "dingtalk": + // 此处填入钉钉机器人发送逻辑,按需补充 + break; + case "wecom": + // 此处填入企业微信机器人发送逻辑,按需补充 + break; + } + } +} + +// 邮件发送函数 +function sendMailNotify($smtp, $uidList, $title, $content) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_user']) || empty($smtp['smtp_pass'])) return; + // 查询用户邮箱 + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($GLOBALS['connWork'], "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", $emailList); + $subject = "【工单通知】" . $title; + $body = "
".$content.""; + // PHPMailer 发送逻辑,环境需安装phpmailer + /* + require_once 'PHPMailer.php'; + require_once 'SMTP.php'; + $mail = new PHPMailer\PHPMailer\PHPMailer(true); + try { + $mail->isSMTP(); + $mail->Host = $smtp['smtp_host']; + $mail->SMTPAuth = true; + $mail->Username = $smtp['smtp_user']; + $mail->Password = $smtp['smtp_pass']; + $mail->SMTPSecure = $smtp['smtp_ssl'] ? PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS : ''; + $mail->Port = $smtp['smtp_port']; + $mail->setFrom($smtp['send_from'], $smtp['send_from']); + $mail->addAddress($to); + $mail->isHTML(true); + $mail->Subject = $subject; + $mail->Body = $body; + $mail->send(); + } catch (Exception $e) {} + */ +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/alert_dashboard/www/api/work_order_api.php.5.0 b/alert_dashboard/www/api/work_order_api.php.5.0 new file mode 100644 index 0000000..c1f6af8 --- /dev/null +++ b/alert_dashboard/www/api/work_order_api.php.5.0 @@ -0,0 +1,267 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单业务库 monitor +$dbWorkHost = '10.150.117.190'; +$dbWorkUser = 'root'; +$dbWorkPwd = 'hp93000'; +$dbWorkName = 'monitor'; +$connWork = mysqli_connect($dbWorkHost, $dbWorkUser, $dbWorkPwd, $dbWorkName); +if (!$connWork) { + echo json_encode(['code' => 500, 'msg' => '工单数据库连接失败:' . mysqli_connect_error()]); + exit; +} +mysqli_set_charset($connWork, 'utf8mb4'); + +// 配置库 alert_mail_stat(告警、通知配置统一此处) +$dbConfHost = "10.150.117.190"; +$dbConfUser = "root"; +$dbConfPwd = "hp93000"; +$dbConfName = "alert_mail_stat"; +$connConf = mysqli_connect($dbConfHost, $dbConfUser, $dbConfPwd, $dbConfName); +if(!$connConf){ + echo json_encode(['code' => 500, 'msg' => '配置库连接失败']); + exit; +} +mysqli_set_charset($connConf, "utf8mb4"); + +// 获取当前用户角色权限 +$isAdmin = 0; +$permList = ""; +$roleSql = "SELECT r.is_admin, r.perm_list FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($connWork, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRes = mysqli_stmt_get_result($stmtRole); +$roleRow = $roleRes ? mysqli_fetch_assoc($roleRes) : []; +if ($roleRow) { + $isAdmin = intval($roleRow['is_admin']); + $permList = $roleRow['perm_list']; +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// 1、获取实时活跃告警(alert_mail_stat.alert_firing 数据源) +if ($action === "get_firing_alert") { + $alertSql = "SELECT alert_id,alert_name,instance FROM alert_firing WHERE status='firing' ORDER BY receive_time DESC LIMIT 100"; + $res = mysqli_query($connConf, $alertSql); + $list = []; + if($res instanceof mysqli_result){ + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// 2、新建工单(关联告警、多渠道通知) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + $status = 1; + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,notify_user,notify_channel,status,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssssi', $title, $content, $loginUid, $assign, $relateAlertId, $notifyUser, $notifyChannel, $status); + $ok = mysqli_stmt_execute($stmt); + if (!$ok) { + echo json_encode(['code' => 500, 'msg' => '创建失败:' . mysqli_error($connWork)]); + exit; + } + $orderId = mysqli_insert_id($connWork); + sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单提交成功,通知已下发']); + exit; +} + +// 3、编辑工单 +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 非管理员权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,notify_user=?,notify_channel=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssi', $title, $content, $assign, $notifyUser, $notifyChannel, $id); + mysqli_stmt_execute($stmt); + sendWorkOrderNotify($connConf, $connWork, $id, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单保存成功,通知已更新下发']); + exit; +} + +// 4、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 5、管理员分发工单(前端统一action=assign,废弃assign_order) +if ($action === "assign") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// ==================== 全局通知统一函数 ==================== +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 读取完整通知配置(邮箱+钉钉+企微) + $cfgRes = mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1"); + $notifyCfg = $cfgRes ? mysqli_fetch_assoc($cfgRes) : []; + if(empty($notifyCfg)) return; + + // 获取工单完整信息 + $orderRes = mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId"); + $order = $orderRes ? mysqli_fetch_assoc($orderRes) : []; + $title = $order['title']; + $content = $order['content']; + $relateId = $order['relate_alert_id'] ?: "无"; + + $msgText = "【运维工单通知】\n工单ID:$orderId\n关联告警:$relateId\n标题:$title\n详情:$content"; + + foreach ($channelArr as $ch) { + $ch = trim($ch); + switch ($ch) { + case "mail": + sendMailNotify($notifyCfg, $userArr, $title, $content, $relateId, $orderId); + break; + case "dingtalk": + sendDingNotify($notifyCfg, $msgText); + break; + case "wecom": + sendWecomNotify($notifyCfg, $msgText); + break; + } + } +} + +// 邮件发送 +function sendMailNotify($smtp, $uidList, $title, $content, $relateId, $orderId) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_account'])) return; + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($GLOBALS['connWork'], "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", array_unique($emailList)); + $subject = "【工单通知】#$orderId $title"; + $body = "
关联告警ID:$relateId
+标题:$title
+$content"; + // 此处接入PHPMailer发送逻辑 +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/alert_dashboard/www/api/work_order_api.php.6.0 b/alert_dashboard/www/api/work_order_api.php.6.0 new file mode 100644 index 0000000..7502531 --- /dev/null +++ b/alert_dashboard/www/api/work_order_api.php.6.0 @@ -0,0 +1,378 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单业务库 monitor +$dbWorkHost = '10.150.117.190'; +$dbWorkUser = 'root'; +$dbWorkPwd = 'hp93000'; +$dbWorkName = 'monitor'; +$connWork = mysqli_connect($dbWorkHost, $dbWorkUser, $dbWorkPwd, $dbWorkName); +if (!$connWork) { + echo json_encode(['code' => 500, 'msg' => '工单数据库连接失败:' . mysqli_connect_error()]); + exit; +} +mysqli_set_charset($connWork, 'utf8mb4'); + +// 配置库 alert_mail_stat(告警、通知配置统一此处) +$dbConfHost = "10.150.117.190"; +$dbConfUser = "root"; +$dbConfPwd = "hp93000"; +$dbConfName = "alert_mail_stat"; +$connConf = mysqli_connect($dbConfHost, $dbConfUser, $dbConfPwd, $dbConfName); +if(!$connConf){ + echo json_encode(['code' => 500, 'msg' => '配置库连接失败']); + exit; +} +mysqli_set_charset($connConf, "utf8mb4"); + +// 获取当前用户角色权限 +$isAdmin = 0; +$permList = ""; +$roleSql = "SELECT r.is_admin, r.perm_list FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($connWork, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRes = mysqli_stmt_get_result($stmtRole); +$roleRow = $roleRes ? mysqli_fetch_assoc($roleRes) : []; +if ($roleRow) { + $isAdmin = intval($roleRow['is_admin']); + $permList = $roleRow['perm_list']; +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// ===================== 新增1:根据告警ID查询关联工单 ===================== +if ($action === "get_workorder_by_alertid") { + $alertId = trim($_GET['alert_id'] ?? ''); + if (empty($alertId)) { + echo json_encode(['code' => 400, 'msg' => '缺少告警ID参数']); + exit; + } + $alertEsc = mysqli_real_escape_string($connWork, $alertId); + $sql = "SELECT id,title,status,assign_uid,create_time FROM work_order WHERE relate_alert_id = ? ORDER BY create_time DESC"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 's', $alertEsc); + mysqli_stmt_execute($stmt); + $res = mysqli_stmt_get_result($stmt); + $list = []; + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// ===================== 新增2:自动同步告警状态,批量更新工单为已完成 ===================== +if ($action === "auto_sync_workorder_status") { + // 1. 查询所有已恢复的告警(alert_type=2),去重获取告警ID + $resolveSql = "SELECT DISTINCT CONCAT(alert_name,'_',instance) as alert_id FROM alert_log WHERE alert_type=2"; + $resolveRes = mysqli_query($connConf, $resolveSql); + $resolveAlertIds = []; + while ($row = mysqli_fetch_assoc($resolveRes)) { + $resolveAlertIds[] = $row['alert_id']; + } + if (empty($resolveAlertIds)) { + echo json_encode(['code' => 0, 'msg' => '暂无已恢复告警,无需更新工单', 'update_count' => 0]); + exit; + } + + // 2. 拼接IN条件,查询未完成、关联该告警的工单 + $inStr = implode("','", array_map(function($v) use ($connWork) { + return mysqli_real_escape_string($connWork, $v); + }, $resolveAlertIds)); + // status=1待处理 / status=2处理中,统一改为3已完成 + $updateSql = "UPDATE work_order SET status=3,update_time=NOW() WHERE relate_alert_id IN ('$inStr') AND status IN (1,2)"; + mysqli_query($connWork, $updateSql); + $updateCnt = mysqli_affected_rows($connWork); + + echo json_encode([ + 'code' => 0, + 'msg' => '工单状态自动同步完成,已恢复告警绑定工单全部置为已完成', + 'update_count' => $updateCnt + ], JSON_UNESCAPED_UNICODE); + exit; +} + +// 1、获取实时活跃告警(alert_mail_stat.alert_firing 数据源) +if ($action === "get_firing_alert") { + $alertSql = "SELECT alert_id,alert_name,instance FROM alert_firing WHERE status='firing' ORDER BY receive_time DESC LIMIT 100"; + $res = mysqli_query($connConf, $alertSql); + $list = []; + if($res instanceof mysqli_result){ + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// 2、新建工单(关联告警、多渠道通知 + 自动判断告警是否已恢复) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + // 默认待处理1;若关联告警已恢复,直接置完成3 + $status = 1; + + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + + // 关联了告警,判断该告警是否存在恢复记录 + if (!empty($relateAlertId)) { + $alertEsc = mysqli_real_escape_string($connConf, $relateAlertId); + $checkResolveSql = "SELECT 1 FROM alert_log WHERE CONCAT(alert_name,'_',instance) = ? AND alert_type=2 LIMIT 1"; + $stmtCheck = mysqli_prepare($connConf, $checkResolveSql); + mysqli_stmt_bind_param($stmtCheck, 's', $alertEsc); + mysqli_stmt_execute($stmtCheck); + $checkRes = mysqli_stmt_get_result($stmtCheck); + if (mysqli_num_rows($checkRes) > 0) { + $status = 3; + } + } + + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,notify_user,notify_channel,status,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssssi', $title, $content, $loginUid, $assign, $relateAlertId, $notifyUser, $notifyChannel, $status); + $ok = mysqli_stmt_execute($stmt); + if (!$ok) { + echo json_encode(['code' => 500, 'msg' => '创建失败:' . mysqli_error($connWork)]); + exit; + } + $orderId = mysqli_insert_id($connWork); + sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单提交成功,通知已下发', 'init_status' => $status]); + exit; +} + +// 3、编辑工单 +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 非管理员权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,notify_user=?,notify_channel=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssi', $title, $content, $assign, $notifyUser, $notifyChannel, $id); + mysqli_stmt_execute($stmt); + sendWorkOrderNotify($connConf, $connWork, $id, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单保存成功,通知已更新下发']); + exit; +} + +// 4、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 5、管理员分发工单(前端统一action=assign,废弃assign_order) +if ($action === "assign") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 新增:同步Prometheus实时告警,自动清理恢复告警(完全独立,无需index.php) +if ($action === "sync_prom_alerts") { + header("Content-Type: application/json;charset=utf-8"); + $promUrl = "http://10.150.117.190:9090/api/v1/alerts"; + $resp = @file_get_contents($promUrl); + if($resp === false){ + echo json_encode(["code"=>500,"msg"=>"无法连接Prometheus接口"],JSON_UNESCAPED_UNICODE); + exit; + } + $promData = json_decode($resp,true); + if(!isset($promData['data']['alerts'])){ + echo json_encode(["code"=>500,"msg"=>"Prometheus返回数据异常"],JSON_UNESCAPED_UNICODE); + exit; + } + + // 1. 先将表内所有告警标记为已恢复 + mysqli_query($connConf,"UPDATE alert_firing SET status='resolved'"); + + // 2. 遍历当前Prometheus活跃告警,写入/更新 + $alerts = $promData['data']['alerts'] ?? []; + foreach($alerts as $item){ + $alertName = $item['labels']['alertname'] ?? "unknown"; + $instance = $item['labels']['instance'] ?? "unknown"; + $alertId = $alertName."_".$instance; + $sql = "INSERT INTO alert_firing(alert_id,alert_name,instance,status,receive_time) VALUES (?,?,?,'firing',NOW()) ON DUPLICATE KEY UPDATE status='firing',receive_time=NOW()"; + $stmt = mysqli_prepare($connConf,$sql); + mysqli_stmt_bind_param($stmt,"sss",$alertId,$alertName,$instance); + mysqli_stmt_execute($stmt); + } + $syncNum = count($alerts); + + // 同步完成后自动执行工单状态更新,无需额外调用 + $syncWorkUrl = apiOrder . "?action=auto_sync_workorder_status"; + @file_get_contents($syncWorkUrl); + + echo json_encode([ + "code"=>0, + "msg"=>"Prometheus告警同步完成,已自动同步工单状态", + "active_alert_total"=>$syncNum + ],JSON_UNESCAPED_UNICODE); + exit; +} + +// ==================== 全局通知统一函数 ==================== +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 读取完整通知配置(邮箱+钉钉+企微) + $cfgRes = mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1"); + $notifyCfg = $cfgRes ? mysqli_fetch_assoc($cfgRes) : []; + if(empty($notifyCfg)) return; + + // 获取工单完整信息 + $orderRes = mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId"); + $order = $orderRes ? mysqli_fetch_assoc($orderRes) : []; + $title = $order['title']; + $content = $order['content']; + $relateId = $order['relate_alert_id'] ?: "无"; + + $msgText = "【运维工单通知】\n工单ID:$orderId\n关联告警:$relateId\n标题:$title\n详情:$content"; + + foreach ($channelArr as $ch) { + $ch = trim($ch); + switch ($ch) { + case "mail": + sendMailNotify($notifyCfg, $userArr, $title, $content, $relateId, $orderId); + break; + case "dingtalk": + sendDingNotify($notifyCfg, $msgText); + break; + case "wecom": + sendWecomNotify($notifyCfg, $msgText); + break; + } + } +} + +// 邮件发送 +function sendMailNotify($smtp, $uidList, $title, $content, $relateId, $orderId) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_account'])) return; + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($GLOBALS['connWork'], "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", array_unique($emailList)); + $subject = "【工单通知】#$orderId $title"; + $body = "
关联告警ID:$relateId
+标题:$title
+$content"; + // 此处接入PHPMailer发送逻辑 +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/alert_dashboard/www/api/work_order_api.php.7.0 b/alert_dashboard/www/api/work_order_api.php.7.0 new file mode 100644 index 0000000..6a210d6 --- /dev/null +++ b/alert_dashboard/www/api/work_order_api.php.7.0 @@ -0,0 +1,407 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单业务库 monitor +$dbWorkHost = '10.150.117.190'; +$dbWorkUser = 'root'; +$dbWorkPwd = 'hp93000'; +$dbWorkName = 'monitor'; +$connWork = mysqli_connect($dbWorkHost, $dbWorkUser, $dbWorkPwd, $dbWorkName); +if (!$connWork) { + echo json_encode(['code' => 500, 'msg' => '工单数据库连接失败:' . mysqli_connect_error()]); + exit; +} +mysqli_set_charset($connWork, 'utf8mb4'); + +// 配置库 alert_mail_stat(告警、通知配置统一此处) +$dbConfHost = "10.150.117.190"; +$dbConfUser = "root"; +$dbConfPwd = "hp93000"; +$dbConfName = "alert_mail_stat"; +$connConf = mysqli_connect($dbConfHost, $dbConfUser, $dbConfPwd, $dbConfName); +if(!$connConf){ + echo json_encode(['code' => 500, 'msg' => '配置库连接失败']); + exit; +} +mysqli_set_charset($connConf, "utf8mb4"); + +// 获取当前用户角色权限 +$isAdmin = 0; +$permList = ""; +$roleSql = "SELECT r.is_admin, r.perm_list FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($connWork, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRes = mysqli_stmt_get_result($stmtRole); +$roleRow = $roleRes ? mysqli_fetch_assoc($roleRes) : []; +if ($roleRow) { + $isAdmin = intval($roleRow['is_admin']); + $permList = $roleRow['perm_list']; +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// ===================== 新增1:根据告警ID查询关联工单 ===================== +if ($action === "get_workorder_by_alertid") { + $alertId = trim($_GET['alert_id'] ?? ''); + if (empty($alertId)) { + echo json_encode(['code' => 400, 'msg' => '缺少告警ID参数']); + exit; + } + $alertEsc = mysqli_real_escape_string($connWork, $alertId); + $sql = "SELECT id,title,status,assign_uid,create_time FROM work_order WHERE relate_alert_id = ? ORDER BY create_time DESC"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 's', $alertEsc); + mysqli_stmt_execute($stmt); + $res = mysqli_stmt_get_result($stmt); + $list = []; + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// ===================== 新增2:自动同步告警状态,批量更新工单为已完成 ===================== +if ($action === "auto_sync_workorder_status") { + // 1. 查询所有已恢复的告警(alert_type=2),去重获取告警ID + $resolveSql = "SELECT DISTINCT CONCAT(alert_name,'_',instance) as alert_id FROM alert_log WHERE alert_type=2"; + $resolveRes = mysqli_query($connConf, $resolveSql); + $resolveAlertIds = []; + while ($row = mysqli_fetch_assoc($resolveRes)) { + $resolveAlertIds[] = $row['alert_id']; + } + if (empty($resolveAlertIds)) { + echo json_encode(['code' => 0, 'msg' => '暂无已恢复告警,无需更新工单', 'update_count' => 0]); + exit; + } + + // 2. 拼接IN条件,查询未完成、关联该告警的工单 + $inStr = implode("','", array_map(function($v) use ($connWork) { + return mysqli_real_escape_string($connWork, $v); + }, $resolveAlertIds)); + // status=1待处理 / status=2处理中,统一改为3已完成 + $updateSql = "UPDATE work_order SET status=3,update_time=NOW() WHERE relate_alert_id IN ('$inStr') AND status IN (1,2)"; + mysqli_query($connWork, $updateSql); + $updateCnt = mysqli_affected_rows($connWork); + + echo json_encode([ + 'code' => 0, + 'msg' => '工单状态自动同步完成,已恢复告警绑定工单全部置为已完成', + 'update_count' => $updateCnt + ], JSON_UNESCAPED_UNICODE); + exit; +} + +// 1、获取实时活跃告警(alert_mail_stat.alert_firing 数据源) +if ($action === "get_firing_alert") { + $alertSql = "SELECT alert_id,alert_name,instance FROM alert_firing WHERE status='firing' ORDER BY receive_time DESC LIMIT 100"; + $res = mysqli_query($connConf, $alertSql); + $list = []; + if($res instanceof mysqli_result){ + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// 2、新建工单(关联告警、多渠道通知 + 自动判断告警是否已恢复) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + // 默认待处理1;若关联告警已恢复,直接置完成3 + $status = 1; + + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + + // 关联了告警,判断该告警是否存在恢复记录 + if (!empty($relateAlertId)) { + $alertEsc = mysqli_real_escape_string($connConf, $relateAlertId); + $checkResolveSql = "SELECT 1 FROM alert_log WHERE CONCAT(alert_name,'_',instance) = ? AND alert_type=2 LIMIT 1"; + $stmtCheck = mysqli_prepare($connConf, $checkResolveSql); + mysqli_stmt_bind_param($stmtCheck, 's', $alertEsc); + mysqli_stmt_execute($stmtCheck); + $checkRes = mysqli_stmt_get_result($stmtCheck); + if (mysqli_num_rows($checkRes) > 0) { + $status = 3; + } + } + + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,notify_user,notify_channel,status,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssssi', $title, $content, $loginUid, $assign, $relateAlertId, $notifyUser, $notifyChannel, $status); + $ok = mysqli_stmt_execute($stmt); + if (!$ok) { + echo json_encode(['code' => 500, 'msg' => '创建失败:' . mysqli_error($connWork)]); + exit; + } + $orderId = mysqli_insert_id($connWork); + sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单提交成功,通知已下发', 'init_status' => $status]); + exit; +} + +// 3、编辑工单 +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 非管理员权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,notify_user=?,notify_channel=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssi', $title, $content, $assign, $notifyUser, $notifyChannel, $id); + mysqli_stmt_execute($stmt); + sendWorkOrderNotify($connConf, $connWork, $id, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单保存成功,通知已更新下发']); + exit; +} + +// 4、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 5、管理员分发工单(前端统一action=assign,废弃assign_order) +if ($action === "assign") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 新增:同步Prometheus实时告警,自动清理恢复告警(完全独立,无需index.php) +if ($action === "sync_prom_alerts") { + header("Content-Type: application/json;charset=utf-8"); + $promUrl = "http://10.150.117.190:9090/api/v1/alerts"; + $resp = @file_get_contents($promUrl); + if($resp === false){ + echo json_encode(["code"=>500,"msg"=>"无法连接Prometheus接口"],JSON_UNESCAPED_UNICODE); + exit; + } + $promData = json_decode($resp,true); + if(!isset($promData['data']['alerts'])){ + echo json_encode(["code"=>500,"msg"=>"Prometheus返回数据异常"],JSON_UNESCAPED_UNICODE); + exit; + } + + // 1. 先将表内所有告警标记为已恢复 + mysqli_query($connConf,"UPDATE alert_firing SET status='resolved'"); + + // 2. 遍历当前Prometheus活跃告警,写入/更新 + $alerts = $promData['data']['alerts'] ?? []; + foreach($alerts as $item){ + $alertName = $item['labels']['alertname'] ?? "unknown"; + $instance = $item['labels']['instance'] ?? "unknown"; + $alertId = $alertName."_".$instance; + $sql = "INSERT INTO alert_firing(alert_id,alert_name,instance,status,receive_time) VALUES (?,?,?,'firing',NOW()) ON DUPLICATE KEY UPDATE status='firing',receive_time=NOW()"; + $stmt = mysqli_prepare($connConf,$sql); + mysqli_stmt_bind_param($stmt,"sss",$alertId,$alertName,$instance); + mysqli_stmt_execute($stmt); + } + $syncNum = count($alerts); + + // 同步完成后自动执行工单状态更新,无需额外调用 + $syncWorkUrl = apiOrder . "?action=auto_sync_workorder_status"; + @file_get_contents($syncWorkUrl); + + echo json_encode([ + "code"=>0, + "msg"=>"Prometheus告警同步完成,已自动同步工单状态", + "active_alert_total"=>$syncNum + ],JSON_UNESCAPED_UNICODE); + exit; +} + +// 定时同步:告警恢复自动完工单 +if($_POST['action'] === "sync_alert_auto_close"){ + // 1、拉取仪表盘实时未恢复告警 + $alertApi = "./index.php?act=active_firing"; + $raw = file_get_contents($alertApi); + $activeList = json_decode($raw, true) ?: []; + $activeKeys = []; + foreach($activeList as $item){ + $activeKeys[] = $item['alert_name'] . "|" . $item['instance']; + } + // 2、查询所有未完成、绑定告警的工单 + $sql = "SELECT id, relate_alert_id FROM work_order WHERE status IN(1,2) AND relate_alert_id <> ''"; + $res = mysqli_query($conn, $sql); + while($row = mysqli_fetch_assoc($res)){ + // 工单关联告警不在当前活跃列表=已恢复,自动设为完成 + if(!in_array($row['relate_alert_id'], $activeKeys)){ + $updateSql = "UPDATE work_order SET status=3, update_time=NOW() WHERE id = ".(int)$row['id']; + mysqli_query($conn, $updateSql); + } + } + echo json_encode(['code'=>0, 'msg'=>'同步完成']); + exit; +} + +// 读取通知配置 +$notifyRow = mysqli_fetch_assoc(mysqli_query($conn, "SELECT * FROM sys_workorder_notify LIMIT 1")); +if(!$notifyRow || $notifyRow['notify_open'] != 1) return; +// 组装钉钉、企业微信请求,curl发送webhook + +// ==================== 全局通知统一函数 ==================== +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 读取完整通知配置(邮箱+钉钉+企微) + $cfgRes = mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1"); + $notifyCfg = $cfgRes ? mysqli_fetch_assoc($cfgRes) : []; + if(empty($notifyCfg)) return; + + // 获取工单完整信息 + $orderRes = mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId"); + $order = $orderRes ? mysqli_fetch_assoc($orderRes) : []; + $title = $order['title']; + $content = $order['content']; + $relateId = $order['relate_alert_id'] ?: "无"; + + $msgText = "【运维工单通知】\n工单ID:$orderId\n关联告警:$relateId\n标题:$title\n详情:$content"; + + foreach ($channelArr as $ch) { + $ch = trim($ch); + switch ($ch) { + case "mail": + sendMailNotify($notifyCfg, $userArr, $title, $content, $relateId, $orderId); + break; + case "dingtalk": + sendDingNotify($notifyCfg, $msgText); + break; + case "wecom": + sendWecomNotify($notifyCfg, $msgText); + break; + } + } +} + +// 邮件发送 +function sendMailNotify($smtp, $uidList, $title, $content, $relateId, $orderId) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_account'])) return; + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($GLOBALS['connWork'], "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", array_unique($emailList)); + $subject = "【工单通知】#$orderId $title"; + $body = "
关联告警ID:$relateId
+标题:$title
+$content"; + // 此处接入PHPMailer发送逻辑 +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/alert_dashboard/www/api/work_order_api.php.8.0 b/alert_dashboard/www/api/work_order_api.php.8.0 new file mode 100644 index 0000000..63e3a97 --- /dev/null +++ b/alert_dashboard/www/api/work_order_api.php.8.0 @@ -0,0 +1,427 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单业务库 monitor +$dbWorkHost = '10.150.117.190'; +$dbWorkUser = 'root'; +$dbWorkPwd = 'hp93000'; +$dbWorkName = 'monitor'; +$connWork = mysqli_connect($dbWorkHost, $dbWorkUser, $dbWorkPwd, $dbWorkName); +if (!$connWork) { + echo json_encode(['code' => 500, 'msg' => '工单数据库连接失败:' . mysqli_connect_error()]); + exit; +} +mysqli_set_charset($connWork, 'utf8mb4'); + +// 配置库 alert_mail_stat(告警、通知配置统一此处) +$dbConfHost = "10.150.117.190"; +$dbConfUser = "root"; +$dbConfPwd = "hp93000"; +$dbConfName = "alert_mail_stat"; +$connConf = mysqli_connect($dbConfHost, $dbConfUser, $dbConfPwd, $dbConfName); +if(!$connConf){ + echo json_encode(['code' => 500, 'msg' => '配置库连接失败']); + exit; +} +mysqli_set_charset($connConf, "utf8mb4"); + +// 获取当前用户角色权限 +$isAdmin = 0; +$permList = ""; +$roleSql = "SELECT r.is_admin, r.perm_list FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($connWork, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRes = mysqli_stmt_get_result($stmtRole); +$roleRow = $roleRes ? mysqli_fetch_assoc($roleRes) : []; +if ($roleRow) { + $isAdmin = intval($roleRow['is_admin']); + $permList = $roleRow['perm_list']; +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// ===================== 新增1:根据告警ID查询关联工单 ===================== +if ($action === "get_workorder_by_alertid") { + $alertId = trim($_GET['alert_id'] ?? ''); + if (empty($alertId)) { + echo json_encode(['code' => 400, 'msg' => '缺少告警ID参数']); + exit; + } + $alertEsc = mysqli_real_escape_string($connWork, $alertId); + $sql = "SELECT id,title,status,assign_uid,create_time FROM work_order WHERE relate_alert_id = ? ORDER BY create_time DESC"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 's', $alertEsc); + mysqli_stmt_execute($stmt); + $res = mysqli_stmt_get_result($stmt); + $list = []; + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// ===================== 新增2:自动同步告警状态,批量更新工单为已完成 ===================== +if ($action === "auto_sync_workorder_status") { + // 1. 查询所有已恢复的告警(alert_type=2),去重获取告警ID + $resolveSql = "SELECT DISTINCT CONCAT(alert_name,'_',instance) as alert_id FROM alert_log WHERE alert_type=2"; + $resolveRes = mysqli_query($connConf, $resolveSql); + $resolveAlertIds = []; + while ($row = mysqli_fetch_assoc($resolveRes)) { + $resolveAlertIds[] = $row['alert_id']; + } + if (empty($resolveAlertIds)) { + echo json_encode(['code' => 0, 'msg' => '暂无已恢复告警,无需更新工单', 'update_count' => 0]); + exit; + } + + // 2. 拼接IN条件,查询未完成、关联该告警的工单 + $inStr = implode("','", array_map(function($v) use ($connWork) { + return mysqli_real_escape_string($connWork, $v); + }, $resolveAlertIds)); + // status=1待处理 / status=2处理中,统一改为3已完成 + $updateSql = "UPDATE work_order SET status=3,update_time=NOW() WHERE relate_alert_id IN ('$inStr') AND status IN (1,2)"; + mysqli_query($connWork, $updateSql); + $updateCnt = mysqli_affected_rows($connWork); + + echo json_encode([ + 'code' => 0, + 'msg' => '工单状态自动同步完成,已恢复告警绑定工单全部置为已完成', + 'update_count' => $updateCnt + ], JSON_UNESCAPED_UNICODE); + exit; +} + +// 1、获取实时活跃告警(alert_mail_stat.alert_firing 数据源) +if ($action === "get_firing_alert") { + $alertSql = "SELECT alert_id,alert_name,instance FROM alert_firing WHERE status='firing' ORDER BY receive_time DESC LIMIT 100"; + $res = mysqli_query($connConf, $alertSql); + $list = []; + if($res instanceof mysqli_result){ + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// 2、新建工单(关联告警、多渠道通知 + 自动判断告警是否已恢复) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + // 默认待处理1;若关联告警已恢复,直接置完成3 + $status = 1; + + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + + // 关联了告警,判断该告警是否存在恢复记录 + if (!empty($relateAlertId)) { + $alertEsc = mysqli_real_escape_string($connConf, $relateAlertId); + $checkResolveSql = "SELECT 1 FROM alert_log WHERE CONCAT(alert_name,'_',instance) = ? AND alert_type=2 LIMIT 1"; + $stmtCheck = mysqli_prepare($connConf, $checkResolveSql); + mysqli_stmt_bind_param($stmtCheck, 's', $alertEsc); + mysqli_stmt_execute($stmtCheck); + $checkRes = mysqli_stmt_get_result($stmtCheck); + if (mysqli_num_rows($checkRes) > 0) { + $status = 3; + } + } + + // 新增工单默认未读 is_read=0 + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,notify_user,notify_channel,status,is_read,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,0,NOW(),NOW())"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ssssssss', $title, $content, $loginUid, $assign, $relateAlertId, $notifyUser, $notifyChannel, $status); + $ok = mysqli_stmt_execute($stmt); + if (!$ok) { + echo json_encode(['code' => 500, 'msg' => '创建失败:' . mysqli_error($connWork)]); + exit; + } + $orderId = mysqli_insert_id($connWork); + sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单提交成功,通知已下发', 'init_status' => $status]); + exit; +} + +// 3、编辑工单 +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 非管理员权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,relate_alert_id=?,notify_user=?,notify_channel=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ssssssi', $title, $content, $assign, $relateAlertId, $notifyUser, $notifyChannel, $id); + mysqli_stmt_execute($stmt); + sendWorkOrderNotify($connConf, $connWork, $id, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单保存成功,通知已更新下发']); + exit; +} + +// 4、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + // 修改状态自动标记已读 + $sql = "UPDATE work_order SET status=?,is_read=1,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 5、管理员分发工单(前端统一action=assign,废弃assign_order) +if ($action === "assign") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + // 修复BUG:仅按工单唯一ID更新,不再使用标题模糊匹配,选中哪一行只修改该行工单 + $sql = "UPDATE work_order SET assign_uid=?,is_read=0,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 一键标记当前用户全部待处理工单为已读 +if ($action === 'mark_all_read') { + $uid = $post['uid'] ?? ''; + $sql = "UPDATE work_order SET is_read=1 WHERE assign_uid=? AND status=1"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, "s", $uid); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '已全部标记为已读']); + exit; +} + +// 语言切换 +if ($action === 'save_lang') { + $lang = $post['lang'] ?? 'zh'; + $sql = "UPDATE sys_config SET v=? WHERE k='lang'"; + $stmt = mysqli_prepare($connConf, $sql); + mysqli_stmt_bind_param($stmt, "s", $lang); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '成功']); + exit; +} + +// 新增:同步Prometheus实时告警,自动清理恢复告警(完全独立,无需index.php) +if ($action === "sync_prom_alerts") { + $promUrl = "http://10.150.117.190:9090/api/v1/alerts"; + $resp = @file_get_contents($promUrl); + if($resp === false){ + echo json_encode(["code"=>500,"msg"=>"无法连接Prometheus接口"],JSON_UNESCAPED_UNICODE); + exit; + } + $promData = json_decode($resp,true); + if(!isset($promData['data']['alerts'])){ + echo json_encode(["code"=>500,"msg"=>"Prometheus返回数据异常"],JSON_UNESCAPED_UNICODE); + exit; + } + + // 1. 先将表内所有告警标记为已恢复 + mysqli_query($connConf,"UPDATE alert_firing SET status='resolved'"); + + // 2. 遍历当前Prometheus活跃告警,写入/更新 + $alerts = $promData['data']['alerts'] ?? []; + foreach($alerts as $item){ + $alertName = $item['labels']['alertname'] ?? "unknown"; + $instance = $item['labels']['instance'] ?? "unknown"; + $alertId = $alertName."_".$instance; + $sql = "INSERT INTO alert_firing(alert_id,alert_name,instance,status,receive_time) VALUES (?,?,?,'firing',NOW()) ON DUPLICATE KEY UPDATE status='firing',receive_time=NOW()"; + $stmt = mysqli_prepare($connConf,$sql); + mysqli_stmt_bind_param($stmt,"sss",$alertId,$alertName,$instance); + mysqli_stmt_execute($stmt); + } + $syncNum = count($alerts); + + // 同步完成后自动执行工单状态更新 + $syncWorkUrl = $_SERVER['REQUEST_SCHEME'] . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . "?action=auto_sync_workorder_status"; + @file_get_contents($syncWorkUrl); + + echo json_encode([ + "code"=>0, + "msg"=>"Prometheus告警同步完成,已自动同步工单状态", + "active_alert_total"=>$syncNum + ],JSON_UNESCAPED_UNICODE); + exit; +} + +// 定时同步:告警恢复自动完工单 +if($action === "sync_alert_auto_close"){ + // 1、拉取仪表盘实时未恢复告警 + $alertApi = "./index.php?act=active_firing"; + $raw = @file_get_contents($alertApi); + $activeList = json_decode($raw, true) ?: []; + $activeKeys = []; + foreach($activeList as $item){ + $activeKeys[] = $item['alert_name'] . "|" . $item['instance']; + } + // 2、查询所有未完成、绑定告警的工单 + $sql = "SELECT id, relate_alert_id FROM work_order WHERE status IN(1,2) AND relate_alert_id <> ''"; + $res = mysqli_query($connWork, $sql); + while($row = mysqli_fetch_assoc($res)){ + // 工单关联告警不在当前活跃列表=已恢复,自动设为完成 + if(!in_array($row['relate_alert_id'], $activeKeys)){ + $updateSql = "UPDATE work_order SET status=3, update_time=NOW() WHERE id = ".(int)$row['id']; + mysqli_query($connWork, $updateSql); + } + } + echo json_encode(['code'=>0, 'msg'=>'同步完成']); + exit; +} + +// ==================== 全局通知统一函数 ==================== +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 读取完整通知配置(邮箱+钉钉+企微) + $cfgRes = mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1"); + $notifyCfg = $cfgRes ? mysqli_fetch_assoc($cfgRes) : []; + if(empty($notifyCfg)) return; + + // 获取工单完整信息 + $orderRes = mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId"); + $order = $orderRes ? mysqli_fetch_assoc($orderRes) : []; + $title = $order['title']; + $content = $order['content']; + $relateId = $order['relate_alert_id'] ?: "无"; + + $msgText = "【运维工单通知】\n工单ID:$orderId\n关联告警:$relateId\n标题:$title\n详情:$content"; + + foreach ($channelArr as $ch) { + $ch = trim($ch); + switch ($ch) { + case "mail": + sendMailNotify($notifyCfg, $connWork, $userArr, $title, $content, $relateId, $orderId); + break; + case "dingtalk": + sendDingNotify($notifyCfg, $msgText); + break; + case "wecom": + sendWecomNotify($notifyCfg, $msgText); + break; + } + } +} + +// 邮件发送 +function sendMailNotify($smtp, $connWork, $uidList, $title, $content, $relateId, $orderId) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_account'])) return; + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($connWork, "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", array_unique($emailList)); + $subject = "【工单通知】#$orderId $title"; + $body = "
关联告警ID:$relateId
+标题:$title
+$content"; + // 此处接入PHPMailer发送逻辑 +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/alert_dashboard/www/api/work_order_api.php.bak b/alert_dashboard/www/api/work_order_api.php.bak new file mode 100644 index 0000000..ce696eb --- /dev/null +++ b/alert_dashboard/www/api/work_order_api.php.bak @@ -0,0 +1,104 @@ + 0])); +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 数据库配置,优先本地localhost +$dbHost = '10.150.117.190'; +$dbUser = 'root'; +$dbPwd = 'hp93000'; +$dbName = 'monitor'; +$dbPort = 3306; + +// 关键:连接后立刻判断是否失败 +$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName, $dbPort); +if (!$conn) { + echo json_encode([ + 'code' => 500, + 'msg' => '数据库连接失败:' . mysqli_connect_error() . ' 错误码:' . mysqli_connect_errno() + ]); + exit; +} +// 只有连接成功才执行字符集 +mysqli_set_charset($conn, 'utf8mb4'); + +// 下面原有业务代码不变 ... +// 获取角色 +$isAdmin = 0; +$r = mysqli_query($conn,"SELECT r.is_admin FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid='$loginUid'"); +if($rr = mysqli_fetch_assoc($r)) $isAdmin = intval($rr['is_admin']); +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"),true) ?: []; + +// 1、新建工单 +if($action === "create"){ + $title = $post['title'] ?? ''; + $content = $post['content'] ?? ''; + $assign = $post['assign_uid'] ?? ''; + if(!$title || !$content){ + echo json_encode(['code'=>400,'msg'=>'标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,status,create_time,update_time) VALUES (?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($conn,$sql); + mysqli_stmt_bind_param($stmt,'ssssi',$title,$content,$loginUid,$assign,1); + $ok = mysqli_stmt_execute($stmt); + if($ok) echo json_encode(['code'=>0,'msg'=>'工单提交成功']); + else echo json_encode(['code'=>500,'msg'=>'提交失败:'.mysqli_error($conn)]); + exit; +} + +// 2、修改工单状态 +if($action === "update_status"){ + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if($id <=0){ + echo json_encode(['code'=>400,'msg'=>'工单ID错误']); + exit; + } + // 权限校验:普通用户只能改自己创建/分配给自己的工单 + if($isAdmin !== 1){ + $chk = mysqli_query($conn,"SELECT id FROM work_order WHERE id=$id AND (create_uid='$loginUid' OR assign_uid='$loginUid')"); + if(mysqli_num_rows($chk) === 0){ + echo json_encode(['code'=>403,'msg'=>'无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn,$sql); + mysqli_stmt_bind_param($stmt,'ii',$status,$id); + mysqli_stmt_execute($stmt); + echo json_encode(['code'=>0,'msg'=>'状态更新成功']); + exit; +} + +// 3、管理员分发工单 +if($action === "assign_order"){ + if($isAdmin !== 1){ + echo json_encode(['code'=>403,'msg'=>'仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = $post['assign_uid'] ?? ''; + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn,$sql); + mysqli_stmt_bind_param($stmt,'si',$assignUid,$id); + mysqli_stmt_execute($stmt); + echo json_encode(['code'=>0,'msg'=>'工单分发成功']); + exit; +} + +echo json_encode(['code'=>400,'msg'=>'无效操作']); +mysqli_close($conn); +exit; +?> diff --git a/alert_dashboard/www/auth.php b/alert_dashboard/www/auth.php new file mode 100644 index 0000000..8db6b8f --- /dev/null +++ b/alert_dashboard/www/auth.php @@ -0,0 +1,145 @@ + $expireTime) { + session_destroy(); + header("Location: login.php"); + exit; +} +$_SESSION['login_time'] = time(); + +// 登录用户基础信息 +$loginUserId = $_SESSION['user_id']; +$userName = htmlspecialchars($_SESSION['username'] ?? ''); +$isAdmin = intval($_SESSION['is_admin'] ?? 0); +$userRoleId = intval($_SESSION['role_id'] ?? 0); + +// ====================== 双库固定连接(修复null根源) ====================== +$dbHost = "10.150.117.190"; +$dbUser = "root"; +$dbPass = "hp93000"; + +// 业务库 alert_mail_stat(工单、SMTP、大盘) +$dbAlertName = "alert_mail_stat"; +$connAlert = mysqli_connect($dbHost, $dbUser, $dbPass, $dbAlertName); +if (!$connAlert) { + die("业务库alert_mail_stat连接失败:" . mysqli_connect_error()); +} +mysqli_set_charset($connAlert, "utf8mb4"); + +// 权限库 monitor(用户、角色、权限、消息) +$dbMonitorName = "monitor"; +$connMonitor = mysqli_connect($dbHost, $dbUser, $dbPass, $dbMonitorName); +if (!$connMonitor) { + die("权限库monitor连接失败:" . mysqli_connect_error()); +} +mysqli_set_charset($connMonitor, "utf8mb4"); +// ======================================================================== + +// 获取当前访问页面文件名(去掉.php后缀) +$currentPage = basename($_SERVER['SCRIPT_NAME'], '.php'); + +// 非超级管理员校验页面权限 +if ($isAdmin !== 1) { + $permSql = "SELECT page_key FROM sys_role_permission WHERE role_id = $userRoleId"; + $permRes = mysqli_query($connMonitor, $permSql); + $allowPages = []; + if ($permRes) { + while ($row = mysqli_fetch_assoc($permRes)) { + $allowPages[] = $row['page_key']; + } + } + // 无权限拦截 + if (!in_array($currentPage, $allowPages)) { + echo ""; + exit; + } +} + +// 读取右上角未读消息数量(修复第79/84/86行报错) +$unreadMsgCount = 0; +$msgSql = "SELECT COUNT(id) cnt FROM sys_user_msg WHERE user_id = $loginUserId AND is_read = 0"; +$msgRes = mysqli_query($connMonitor, $msgSql); +if ($msgRes) { + $msgRow = mysqli_fetch_assoc($msgRes); + $unreadMsgCount = intval($msgRow['cnt']); +} + +// 读取系统基础配置 +$sysConfig = []; +$configSql = "SELECT k, v FROM sys_config"; +$configRes = mysqli_query($connAlert, $configSql); +if ($configRes) { + while ($row = mysqli_fetch_assoc($configRes)) { + $sysConfig[$row['k']] = $row['v']; + } +} +$currentLang = $sysConfig['lang'] ?? "zh"; +$sidebarRawLinks = $sysConfig['quick_link'] ?? ""; + +// 多语言字典 +$langMap = [ + 'zh' => [ + 'html_lang' => 'zh-CN', + 'page_title' => '运维监控平台', + 'monitor_panel' => '监控面板', + 'alert_overview' => '告警总览', + 'quick_link' => '快捷外链', + 'system_manage' => '系统管理', + 'notify_config' => '消息通知配置', + 'role_manage' => '角色管理', + 'user_manage' => '用户管理', + 'current_user' => '当前用户:', + 'sys_time' => '系统时间:', + 'refresh' => '刷新页面', + 'logout' => '退出登录', + 'unread_msg' => '未读消息' + ], + 'en' => [ + 'html_lang' => 'en', + 'page_title' => "Monitor Platform", + 'monitor_panel' => "Monitor Panel", + 'alert_overview' => "Alert Dashboard", + 'quick_link' => "Quick Link", + 'system_manage' => "System Manage", + 'notify_config' => "Notify Config", + 'role_manage' => "Role Manage", + 'user_manage' => "User Manage", + 'current_user' => "User: ", + 'sys_time' => "Time: ", + 'refresh' => "Refresh", + 'logout' => "Logout", + 'unread_msg' => "Unread Msg" + ] +]; +$t = $langMap[$currentLang]; + +// 解析侧边栏外链 +$sidebarLinkList = []; +if (!empty($sidebarRawLinks)) { + $lines = explode("\n", $sidebarRawLinks); + foreach ($lines as $line) { + $line = trim($line); + if (empty($line)) continue; + $item = explode("|", $line); + if (count($item) === 2) { + $sidebarLinkList[] = [ + 'name' => trim($item[0]), + 'url' => trim($item[1]) + ]; + } + } +} +?> diff --git a/alert_dashboard/www/dashbaord.en-zh.php b/alert_dashboard/www/dashbaord.en-zh.php new file mode 100644 index 0000000..82f6e8c --- /dev/null +++ b/alert_dashboard/www/dashbaord.en-zh.php @@ -0,0 +1,1094 @@ + $expire)) { + session_destroy(); + header("Location: login.php"); + exit; +} +// 刷新登录计时 +$_SESSION['login_time'] = time(); +$userName = htmlspecialchars($_SESSION['username']); +$userRole = $_SESSION['role']; +$roleText = $userRole === 'admin' ? '管理员' : '只读用户'; + +// 读取全局系统配置(admin后台保存后同步生效) +$dbHost = "10.150.117.190"; +$dbUser = "root"; +$dbPwd = "hp93000"; +$dbName = "alert_mail_stat"; +$connConfig = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName); +mysqli_set_charset($connConfig, "utf8mb4"); +$configSql = "SELECT k,v FROM sys_config"; +$configRes = mysqli_query($connConfig, $configSql); +$sysConfig = []; +while ($row = mysqli_fetch_assoc($configRes)) { + $sysConfig[$row['k']] = $row['v']; +} +mysqli_close($connConfig); + +// 赋值全局配置 +$pageTitle = htmlspecialchars($sysConfig['page_title'] ?? "告警监控系统"); +$themeColor = htmlspecialchars($sysConfig['theme_color'] ?? "#409eff"); +$pageSize = $sysConfig['page_size'] ?? "20"; +$footerText = htmlspecialchars($sysConfig['footer_text'] ?? ""); +$sidebarRawLinks = $sysConfig['sidebar_links'] ?? ""; +// 读取语言配置 zh=中文 en=英文 +$currentLang = $sysConfig['lang'] ?? "zh"; + +// 全局多语言词典 +$langMap = [ + 'zh' => [ + 'html_lang' => 'zh-CN', + 'page_subtitle' => '爱德万测试 · 运维告警监控平台', + 'monitor_panel' => '监控面板', + 'alert_overview' => '告警监控总览', + 'disk_capacity' => '服务器磁盘容量', + 'quick_link' => '快捷外链', + 'system_manage' => '系统管理', + 'system_config' => '后台配置管理', + 'current_user' => '当前用户:', + 'sys_time' => '系统时间:', + 'refresh_all' => '刷新全部数据', + 'export_csv' => '导出告警CSV', + 'logout' => '退出登录', + 'lang_text' => '语言:', + 'today_fire' => '今日触发告警', + 'unresolved_now' => '当前未恢复告警', + 'resolved_today' => '今日恢复告警', + 'instance_total' => '故障实例总数', + 'recovery_rate' => '告警恢复率', + 'today_fire_sub' => 'Total Firing Today', + 'unresolved_sub' => 'Unresolved Now', + 'resolved_sub' => 'Resolved Today', + 'instance_sub' => 'Affected Host Instance', + 'rate_sub' => 'Daily Recovery Rate', + 'top10_alert' => '今日告警频次 TOP 10', + 'active_alert' => '当前未恢复活跃告警', + 'alert_count' => '条未恢复故障', + 'all_alert_log' => '今日全量告警明细列表', + 'total_alert' => '条告警记录', + 'tag' => '告警状态', + 'alert_name' => '告警名称', + 'instance_addr' => '实例地址', + 'severity' => '告警级别', + 'occur_time' => '故障发生时间', + 'empty_normal' => '当前无未恢复告警,系统运行正常', + 'empty_no_data' => '暂无今日告警数据', + 'disk_stat' => '用户目录磁盘占用统计', + 'filter_ip' => '筛选实例IP:', + 'export_disk' => '导出当前表格CSV', + 'all_instance' => '全部实例', + 'disk_loading' => '磁盘数据加载中...', + 'no_disk_data' => '暂无磁盘指标数据', + 'no_ip_data' => '该实例下无目录数据', + 'csv_tip' => '正在生成CSV报表,请稍候...', + 'alert_axis' => '告警次数', + 'data_fail' => '数据加载失败,请重新登录', + 'server_fail' => '接口数据加载失败,请检查后端服务', + 'disk_api_fail' => '磁盘接口异常:', + 'disk_request_fail' => '请求df.php接口失败,请检查服务', + 'no_export_data' => '无数据可导出' + ], + 'en' => [ + 'html_lang' => 'en', + 'page_subtitle' => 'Advantest · O&M Alert Monitor Platform', + 'monitor_panel' => 'Monitor Panel', + 'alert_overview' => 'Alert Overview', + 'disk_capacity' => 'Server Disk Capacity', + 'quick_link' => 'Quick Links', + 'system_manage' => 'System Manage', + 'system_config' => 'System Config', + 'current_user' => 'User: ', + 'sys_time' => 'System Time: ', + 'refresh_all' => 'Refresh All Data', + 'export_csv' => 'Export Alert CSV', + 'logout' => 'Logout', + 'lang_text' => 'Lang: ', + 'today_fire' => 'Today Triggered Alerts', + 'unresolved_now' => 'Unresolved Alerts', + 'resolved_today' => 'Resolved Today', + 'instance_total' => 'Affected Instances', + 'recovery_rate' => 'Daily Recovery Rate', + 'today_fire_sub' => 'Total Firing Today', + 'unresolved_sub' => 'Unresolved Now', + 'resolved_sub' => 'Resolved Today', + 'instance_sub' => 'Affected Host Instance', + 'rate_sub' => 'Daily Recovery Rate', + 'top10_alert' => 'Top 10 Alert Frequency Today', + 'active_alert' => 'Active Unresolved Alerts', + 'alert_count' => ' unresolved alerts', + 'all_alert_log' => 'Today Full Alert List', + 'total_alert' => ' alert records', + 'tag' => 'Status', + 'alert_name' => 'Alert Name', + 'instance_addr' => 'Instance IP', + 'severity' => 'Severity', + 'occur_time' => 'Occur Time', + 'empty_normal' => 'No active alerts, system normal', + 'empty_no_data' => 'No alert data today', + 'disk_stat' => 'User Directory Disk Usage', + 'filter_ip' => 'Filter IP: ', + 'export_disk' => 'Export Table CSV', + 'all_instance' => 'All Instances', + 'disk_loading' => 'Loading disk data...', + 'no_disk_data' => 'No disk data available', + 'no_ip_data' => 'No directory data for this instance', + 'csv_tip' => 'Generating CSV file, please wait...', + 'alert_axis' => 'Alert Count', + 'data_fail' => 'Data load failed, please relogin', + 'server_fail' => 'API request failed, check backend service', + 'disk_api_fail' => 'Disk API Error: ', + 'disk_request_fail' => 'Request df.php failed, check service', + 'no_export_data' => 'No data to export' + ] +]; +$t = $langMap[$currentLang]; + +// 解析侧边外链列表 +$sidebarLinkList = []; +if (!empty($sidebarRawLinks)) { + $lines = explode("\n", $sidebarRawLinks); + foreach ($lines as $line) { + $line = trim($line); + if (empty($line)) continue; + $item = explode("|", $line, 2); + if (count($item) === 2) { + $sidebarLinkList[] = [ + "name" => trim($item[0]), + "url" => trim($item[1]) + ]; + } + } +} +?> + + + + + +
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| IP | +Username | +Path | +Size(GB) | +
|---|---|---|---|
| + + + | +|||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 状态 | +告警名称 | +实例地址 | +级别 | +故障开始时间 | +接收时间 | +
|---|---|---|---|---|---|
|
+
+
+
+ 数据加载中...
+ |
+ |||||
| 状态 | +告警名称 | +实例地址 | +级别 | +故障开始时间 | +恢复时间 | +接收时间 | +
|---|---|---|---|---|---|---|
|
+
+
+
+ 数据加载中...
+ |
+ ||||||
0
+0
+0
+| 类型 | +告警名称 | +实例 | +级别 | +故障开始 | +恢复时间 | +邮件接收时间 | +
|---|
| 状态 | +告警名称 | +实例地址 | +级别 | +故障开始时间 | +恢复时间 | +接收时间 | +
|---|---|---|---|---|---|---|
|
+
+
+
+ 数据加载中...
+ |
+ ||||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| IP | +Username | +Path | +Size(GB) | +
|---|---|---|---|
| + + + | +|||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| IP | +Username | +Path | +Size(GB) | +
|---|---|---|---|
| + + + | +|||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| IP | +Username | +Path | +Size(GB) | +
|---|---|---|---|
| + + + | +|||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
| 数据加载中... | +|||
=t.x&&e<=t.x+t.width&&n>=t.y&&n<=t.y+t.height},t.prototype.contain=function(e,n){return t.contain(this,e,n)},t.prototype.clone=function(){return new t(this.x,this.y,this.width,this.height)},t.prototype.copy=function(t){qe(this,t)},t.prototype.plain=function(){return{x:this.x,y:this.y,width:this.width,height:this.height}},t.prototype.isFinite=function(){return isFinite(this.x)&&isFinite(this.y)&&isFinite(this.width)&&isFinite(this.height)},t.prototype.isZero=function(){return 0===this.width||0===this.height},t.create=function(e){return new t(e?e.x:0,e?e.y:0,e?e.width:0,e?e.height:0)},t.copy=function(t,e){return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,t},t.applyTransform=function(t,e,n){if(n){if(n[1]<1e-5&&n[1]>-1e-5&&n[2]<1e-5&&n[2]>-1e-5){var i=n[0],r=n[3],o=n[4],a=n[5];return t.x=e.x*i+o,t.y=e.y*r+a,t.width=e.width*i,t.height=e.height*r,t.width<0&&(t.x+=t.width,t.width=-t.width),void(t.height<0&&(t.y+=t.height,t.height=-t.height))}Be.x=Ge.x=e.x,Be.y=Fe.y=e.y,Ve.x=Fe.x=e.x+e.width,Ve.y=Ge.y=e.y+e.height,Be.transform(n),Fe.transform(n),Ve.transform(n),Ge.transform(n),t.x=Oe(Be.x,Ve.x,Ge.x,Fe.x),t.y=Oe(Be.y,Ve.y,Ge.y,Fe.y);var s=Re(Be.x,Ve.x,Ge.x,Fe.x),l=Re(Be.y,Ve.y,Ge.y,Fe.y);t.width=s-t.x,t.height=l-t.y}else t!==e&&qe(t,e)},t.calculateTransform=function(t,e,n){var i=n.width/e.width,r=n.height/e.height;return Te(t=Se(t||[]),t,kt(en,-e.x,-e.y)),De(t,t,kt(en,i,r)),Te(t,t,kt(en,n.x,n.y)),t},t}(),Ze=Xe.create,je=Xe.set,qe=Xe.copy,Ke=Xe.calculateTransform,$e=Xe.applyTransform,Je=Xe.contain,Qe=new Xe(0,0,0,0),tn=new Xe(0,0,0,0),en=[];function nn(t,e,n,i,r,o,a,s){var l=Ne(e-n),u=Ne(i-t),c=Oe(l,u),h=Ee[r],d=Ee[1-r],p=ze[r];e jh[1]){if(r=!1,qh.negativeSize||n)return r;var s=Xh(jh[0]-Zh[1]),l=Xh(Zh[0]-jh[1]);Uh(s,l)>$h.len()&&(s 1&&i>0?s:a}};return o;function a(){return e=0;o--){var a=t[o],s=void 0;if(a!==r&&!a.ignore&&(s=dn(a,n,i))&&(!e.topTarget&&(e.topTarget=a),s!==on)){e.target=a;break}}}function fn(t,e,n){var i=t.painter;return e<0||e>i.getWidth()||n<0||n>i.getHeight()}B(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],function(t){hn.prototype[t]=function(e){var n,i,r=e.zrX,o=e.zrY,a=fn(this,r,o);if("mouseup"===t&&a||(i=(n=this.findHover(r,o)).target),"mousedown"===t)this._downEl=i,this._downPoint=[e.zrX,e.zrY],this._upEl=i;else if("mouseup"===t)this._upEl=i;else if("click"===t){if(this._downEl!==this._upEl||!this._downPoint||Ft(this._downPoint,[e.zrX,e.zrY])>4)return;this._downPoint=null}this.dispatchToElement(n,t,e)}});function gn(t,e,n,i){var r=e+1;if(r===n)return 1;if(i(t[r++],t[e])<0){for(;r0;)a=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),a+=r,l+=r}else{for(s=r+1;ls&&(l=s);var u=a;a=r-l,l=r-u}for(a++;as&&(l=s);var u=a;a=r-l,l=r-u}else{for(s=i-r;l=0;)a=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),a+=r,l+=r}for(a++;a=0;l--)t[p+l]=t[d+l];return void(t[h]=a[c])}var f=r;for(;;){var g=0,v=0,y=!1;do{if(e(a[c],t[u])<0){if(t[h--]=t[u--],g++,v=0,0===--i){y=!0;break}}else if(t[h--]=a[c--],v++,g=0,1===--s){y=!0;break}}while((g|v)1;){var t=o-2;if(t>=1&&i[t-1]<=i[t]+i[t+1]||t>=2&&i[t-2]<=i[t]+i[t-1])i[t-1]i[t+1])break;s(t)}},forceMergeRuns:function(){for(;o>1;){var t=o-2;t>0&&i[t-1]=32;)e|=1&t,t>>=1;return t+e}(r);do{if((o=gn(t,n,i,e))s&&(l=s),vn(t,n,n+l,n+o,e),o=l}a.pushRun(n,o),a.mergeRuns(),r-=o,n+=o}while(0!==r);a.forceMergeRuns()}}}var bn=!1;function wn(){bn||(bn=!0,console.warn("z / z2 / zlevel of displayable is invalid, which may cause unexpected errors"))}function Sn(t,e){return t.zlevel===e.zlevel?t.z===e.z?t.z2-e.z2:t.z-e.z:t.zlevel-e.zlevel}var Mn=function(){function t(){this._roots=[],this._displayList=[],this._displayListLen=0,this.displayableSortFunc=Sn}return t.prototype.traverse=function(t,e){for(var n=0;n0&&t.arc(u+Z.cx,c+Z.cy,Y,fh(Z.y0,Z.x0),fh(Z.y1,Z.x1),!h),t.arc(u,c,r,fh(Z.cy+Z.y1,Z.cx+Z.x1),fh(j.cy+j.y1,j.cx+j.x1),h),U>0&&t.arc(u+j.cx,c+j.cy,U,fh(j.y1,j.x1),fh(j.y0,j.x0),!h))}else t.lineTo(u+P,c+O),t.arc(u,c,r,l,s,h);else t.lineTo(u+P,c+O)}else t.moveTo(u,c);t.closePath()}}}var wh=function(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,this.endAngle=2*Math.PI,this.clockwise=!0,this.cornerRadius=0},Sh=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultShape=function(){return new wh},e.prototype.buildPath=function(t,e){bh(t,e)},e.prototype.isZeroArea=function(){return this.shape.startAngle===this.shape.endAngle||this.shape.r===this.shape.r0},e}(Kl);Sh.prototype.type="sector";var Mh=function(){this.cx=0,this.cy=0,this.r=0,this.r0=0},Ih=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultShape=function(){return new Mh},e.prototype.buildPath=function(t,e){var n=e.cx,i=e.cy,r=2*Math.PI;t.moveTo(n+e.r,i),t.arc(n,i,e.r,0,r,!1),t.moveTo(n+e.r0,i),t.arc(n,i,e.r0,0,r,!0)},e}(Kl);function Th(t,e,n){var i=e.smooth,r=e.points;if(r&&r.length>=2){if(i){var o=function(t,e,n,i){var r,o,a,s,l=[],u=[],c=[],h=[];if(i){a=[1/0,1/0],s=[-1/0,-1/0];for(var d=0,p=t.length;dg[1]&&(g[1]=f)}return this._rawCount=this._count=s,{start:a,end:s}},t.prototype._initDataFromProvider=function(t,e,n){for(var i=this._provider,r=this._chunks,o=this._dimensions,a=o.length,s=this._rawExtent,l=V(o,function(t){return t.property}),u=0;uv[1]&&(v[1]=g)}}!i.persistent&&i.clean&&i.clean(),this._rawCount=this._count=e,this._extent=[]},t.prototype.count=function(){return this._count},t.prototype.get=function(t,e){if(!(e>=0&&ec[1]&&(c[1]=v),h[d++]=y}return r._count=d,r._indices=h,r._updateGetRawIdx(),r},t.prototype.each=function(t,e){if(this._count)for(var n=t.length,i=this._chunks,r=0,o=this.count();r