init 全新仓库,清除全部历史
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
session_start();
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: *");
|
||||
if($_SERVER['REQUEST_METHOD'] === 'OPTIONS'){
|
||||
echo json_encode(array('code'=>0));
|
||||
exit;
|
||||
}
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
echo json_encode(array('code'=>401,'msg'=>'登录失效','list'=>array(),'total'=>0,'stat'=>array()));
|
||||
exit;
|
||||
}
|
||||
$_SESSION['login_time'] = time();
|
||||
|
||||
$promHost = "10.150.117.190";
|
||||
$promPort = "9090";
|
||||
$promApi = "http://".$promHost.":".$promPort."/api/v1";
|
||||
|
||||
// 修复低版本PHP不支持 ?? 运算符,改用三元判断
|
||||
$page = isset($_REQUEST['page']) ? (int)$_REQUEST['page'] : 1;
|
||||
$pageSize = isset($_REQUEST['page_size']) ? (int)$_REQUEST['page_size'] : 20;
|
||||
$startDate = isset($_REQUEST['start_date']) ? $_REQUEST['start_date'] : "";
|
||||
$endDate = isset($_REQUEST['end_date']) ? $_REQUEST['end_date'] : "";
|
||||
$filterMetric = isset($_REQUEST['metric']) ? trim($_REQUEST['metric']) : "";
|
||||
$filterInstance = isset($_REQUEST['instance']) ? trim($_REQUEST['instance']) : "";
|
||||
$filterGlobal = isset($_REQUEST['global']) ? trim($_REQUEST['global']) : "";
|
||||
$distinct = isset($_REQUEST['distinct']) ? (int)$_REQUEST['distinct'] : 1;
|
||||
$filterSeverity = isset($_REQUEST['severity']) ? $_REQUEST['severity'] : array();
|
||||
$filterStatus = isset($_REQUEST['status']) ? $_REQUEST['status'] : array();
|
||||
$isExport = (isset($_REQUEST['export']) && $_REQUEST['export'] == 1) ? 1 : 0;
|
||||
|
||||
$startTs = 0;
|
||||
$endTs = 0;
|
||||
if (!empty($startDate) && !empty($endDate)) {
|
||||
$startTs = strtotime($startDate);
|
||||
$endTs = strtotime($endDate . " 23:59:59");
|
||||
} else {
|
||||
$today = date("Y-m-d");
|
||||
$startTs = strtotime($today);
|
||||
$endTs = strtotime($today . " 23:59:59");
|
||||
}
|
||||
|
||||
$promQL = 'sort_desc(ALERTS)';
|
||||
$params = array('query' => $promQL);
|
||||
$params['start'] = $startTs;
|
||||
$params['end'] = $endTs;
|
||||
$params['step'] = 60;
|
||||
$queryUrl = $promApi . "/query_range";
|
||||
$url = $queryUrl . "?" . http_build_query($params);
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
$resp = curl_exec($ch);
|
||||
$err = curl_error($ch);
|
||||
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if (!empty($err) || empty($resp)) {
|
||||
echo json_encode(array(
|
||||
'code' => 500,
|
||||
'msg' => 'Prometheus连接失败:' . $err,
|
||||
'list' => array(),
|
||||
'total' => 0,
|
||||
'stat' => array()
|
||||
));
|
||||
exit;
|
||||
}
|
||||
$promData = json_decode($resp, true);
|
||||
if (!isset($promData['status']) || $promData['status'] !== 'success') {
|
||||
echo json_encode(array(
|
||||
'code' => 500,
|
||||
'msg' => 'Prometheus查询异常:'.$resp,
|
||||
'list' => array(),
|
||||
'total' => 0,
|
||||
'stat' => array()
|
||||
));
|
||||
exit;
|
||||
}
|
||||
|
||||
$rawAll = array();
|
||||
$hourCount = array();
|
||||
for ($h = 0; $h < 24; $h++){
|
||||
$hourCount[$h] = 0;
|
||||
}
|
||||
if (!empty($promData['data']['result'])) {
|
||||
foreach ($promData['data']['result'] as $item) {
|
||||
$metric = $item['metric'];
|
||||
$state = isset($metric['alertstate']) ? $metric['alertstate'] : "";
|
||||
$sev = isset($metric['severity']) ? $metric['severity'] : "info";
|
||||
$alertName = isset($metric['alertname']) ? $metric['alertname'] : "";
|
||||
$inst = isset($metric['instance']) ? $metric['instance'] : "";
|
||||
$values = isset($item['values']) ? $item['values'] : array();
|
||||
foreach ($values as $v) {
|
||||
$ts = (int)$v[0];
|
||||
$dt = date("Y-m-d H:i:s", $ts);
|
||||
$hour = date("G", $ts);
|
||||
$hourCount[$hour]++;
|
||||
$rawAll[] = array(
|
||||
'state' => $state,
|
||||
'severity' => $sev,
|
||||
'metric' => $alertName,
|
||||
'instance' => $inst,
|
||||
'time' => $dt,
|
||||
'timestamp' => $ts,
|
||||
'labels' => $metric
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$filterFunc = function ($row) use ($filterMetric, $filterInstance, $filterGlobal, $filterSeverity, $filterStatus) {
|
||||
if (!empty($filterGlobal)) {
|
||||
$g = strtolower($filterGlobal);
|
||||
$match = (stripos(strtolower($row['metric']), $g) !== false)
|
||||
|| (stripos(strtolower($row['instance']), $g) !== false)
|
||||
|| (stripos(strtolower($row['severity']), $g) !== false);
|
||||
if (!$match) return false;
|
||||
}
|
||||
if (!empty($filterMetric) && stripos($row['metric'], $filterMetric) === false) return false;
|
||||
if (!empty($filterInstance) && stripos($row['instance'], $filterInstance) === false) return false;
|
||||
if (!empty($filterSeverity) && !in_array($row['severity'], $filterSeverity)) return false;
|
||||
if (!empty($filterStatus) && !in_array($row['state'], $filterStatus)) return false;
|
||||
return true;
|
||||
};
|
||||
$rawAll = array_filter($rawAll, $filterFunc);
|
||||
usort($rawAll, function($a, $b){
|
||||
return $b['timestamp'] - $a['timestamp'];
|
||||
});
|
||||
|
||||
$listRaw = array();
|
||||
$stat = array(
|
||||
'total_all' => count($rawAll),
|
||||
'total_firing' => 0,
|
||||
'total_resolved' => 0,
|
||||
'total_critical' => 0,
|
||||
'unique_count' => 0,
|
||||
'hour_data' => $hourCount
|
||||
);
|
||||
if ($distinct !== 1) {
|
||||
$listRaw = $rawAll;
|
||||
foreach ($rawAll as $r) {
|
||||
if ($r['state'] == 'firing') $stat['total_firing']++;
|
||||
else $stat['total_resolved']++;
|
||||
if ($r['severity'] == 'critical') $stat['total_critical']++;
|
||||
}
|
||||
} else {
|
||||
$group = array();
|
||||
foreach ($rawAll as $row) {
|
||||
$key = md5($row['metric'] . "|" . $row['instance'] . "|" . $row['severity'] . "|" . $row['state']);
|
||||
if (!isset($group[$key])) {
|
||||
$group[$key] = array(
|
||||
'state' => $row['state'],
|
||||
'severity' => $row['severity'],
|
||||
'metric' => $row['metric'],
|
||||
'instance' => $row['instance'],
|
||||
'time' => $row['time'],
|
||||
'timestamp' => $row['timestamp'],
|
||||
'total_count' => 1,
|
||||
'labels' => $row['labels']
|
||||
);
|
||||
} else {
|
||||
$group[$key]['total_count']++;
|
||||
if ($row['timestamp'] > $group[$key]['timestamp']) {
|
||||
$group[$key]['time'] = $row['time'];
|
||||
$group[$key]['timestamp'] = $row['timestamp'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$listRaw = array_values($group);
|
||||
$stat['unique_count'] = count($listRaw);
|
||||
foreach ($listRaw as $r) {
|
||||
if ($r['state'] == 'firing') $stat['total_firing']++;
|
||||
else $stat['total_resolved']++;
|
||||
if ($r['severity'] == 'critical') $stat['total_critical']++;
|
||||
}
|
||||
}
|
||||
|
||||
$total = count($listRaw);
|
||||
if ($isExport) {
|
||||
$pageData = $listRaw;
|
||||
} else {
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$pageData = array_slice($listRaw, $offset, $pageSize);
|
||||
}
|
||||
|
||||
echo json_encode(array(
|
||||
'code' => 0,
|
||||
'msg' => 'ok',
|
||||
'total' => $total,
|
||||
'stat' => $stat,
|
||||
'list' => $pageData
|
||||
), JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
?>
|
||||
@@ -0,0 +1,143 @@
|
||||
<?php
|
||||
session_start();
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if($_SERVER['REQUEST_METHOD'] === 'OPTIONS') exit(json_encode(['code'=>0]));
|
||||
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
exit(json_encode(['code'=>401,'msg'=>'登录失效','list'=>[],'total'=>0]));
|
||||
}
|
||||
$_SESSION['login_time'] = time();
|
||||
|
||||
$promHost = "10.150.117.190";
|
||||
$promPort = "9090";
|
||||
$promApi = "http://{$promHost}:{$promPort}/api/v1";
|
||||
|
||||
$page = (int)($_REQUEST['page'] ?? 1);
|
||||
$pageSize = (int)($_REQUEST['page_size'] ?? 20);
|
||||
$filterDate = $_REQUEST['date'] ?? "";
|
||||
$filterMetric = trim($_REQUEST['metric'] ?? "");
|
||||
$filterSeverity = $_REQUEST['severity'] ?? "";
|
||||
$filterStatus = $_REQUEST['status'] ?? "";
|
||||
$distinct = (int)($_REQUEST['distinct'] ?? 1);
|
||||
|
||||
$promQL = 'sort_desc(ALERTS)';
|
||||
$params = ['query' => $promQL];
|
||||
if (!empty($filterDate)) {
|
||||
$end = strtotime($filterDate . " 23:59:59");
|
||||
$start = $end - 86400;
|
||||
$params['start'] = $start;
|
||||
$params['end'] = $end;
|
||||
$params['step'] = 60;
|
||||
$queryUrl = $promApi . "/query_range";
|
||||
} else {
|
||||
$queryUrl = $promApi . "/query";
|
||||
}
|
||||
|
||||
$url = $queryUrl . "?" . http_build_query($params);
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
$resp = curl_exec($ch);
|
||||
$err = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if (!empty($err) || empty($resp)) {
|
||||
exit(json_encode([
|
||||
'code' => 500,
|
||||
'msg' => 'Prometheus连接失败:'.$err,
|
||||
'list' => [],
|
||||
'total' => 0
|
||||
]));
|
||||
}
|
||||
$promData = json_decode($resp, true);
|
||||
if ($promData['status'] !== 'success') {
|
||||
exit(json_encode([
|
||||
'code' => 500,
|
||||
'msg' => 'Prometheus查询异常',
|
||||
'list' => [],
|
||||
'total' => 0
|
||||
]));
|
||||
}
|
||||
|
||||
$rawAll = [];
|
||||
if (!empty($promData['data']['result'])) {
|
||||
foreach ($promData['data']['result'] as $item) {
|
||||
$metric = $item['metric'];
|
||||
$state = $metric['alertstate'] ?? "";
|
||||
$sev = $metric['severity'] ?? "info";
|
||||
$alertName = $metric['alertname'] ?? "";
|
||||
$inst = $metric['instance'] ?? "";
|
||||
$values = $item['values'] ?? [];
|
||||
foreach ($values as $v) {
|
||||
$ts = (int)$v[0];
|
||||
$dt = date("Y-m-d H:i:s", $ts);
|
||||
$rawAll[] = [
|
||||
'state' => $state,
|
||||
'severity' => $sev,
|
||||
'metric' => $alertName,
|
||||
'instance' => $inst,
|
||||
'time' => $dt,
|
||||
'timestamp' => $ts
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==========修复过滤逻辑==========
|
||||
if (!empty($filterMetric)) {
|
||||
$rawAll = array_filter($rawAll, function($r) use ($filterMetric){
|
||||
return stripos($r['metric'], $filterMetric) !== false;
|
||||
});
|
||||
}
|
||||
if (!empty($filterSeverity)) {
|
||||
$rawAll = array_filter($rawAll, fn($r) => $r['severity'] === $filterSeverity);
|
||||
}
|
||||
if (!empty($filterStatus)) {
|
||||
$rawAll = array_filter($rawAll, fn($r) => $r['state'] === $filterStatus);
|
||||
}
|
||||
|
||||
usort($rawAll, fn($a,$b) => $b['timestamp'] - $a['timestamp']);
|
||||
|
||||
if ($distinct !== 1) {
|
||||
$list = $rawAll;
|
||||
} else {
|
||||
$group = [];
|
||||
foreach ($rawAll as $row) {
|
||||
$key = md5($row['metric'] . "|" . $row['instance'] . "|" . $row['severity'] . "|" . $row['state']);
|
||||
if (!isset($group[$key])) {
|
||||
$group[$key] = [
|
||||
'state' => $row['state'],
|
||||
'severity' => $row['severity'],
|
||||
'metric' => $row['metric'],
|
||||
'instance' => $row['instance'],
|
||||
'time' => $row['time'],
|
||||
'timestamp' => $row['timestamp'],
|
||||
'total_count' => 1
|
||||
];
|
||||
} else {
|
||||
$group[$key]['total_count']++;
|
||||
if ($row['timestamp'] > $group[$key]['timestamp']) {
|
||||
$group[$key]['time'] = $row['time'];
|
||||
$group[$key]['timestamp'] = $row['timestamp'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$list = array_values($group);
|
||||
}
|
||||
|
||||
$total = count($list);
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$pageData = array_slice($list, $offset, $pageSize);
|
||||
|
||||
echo json_encode([
|
||||
'code' => 0,
|
||||
'msg' => 'ok',
|
||||
'total' => $total,
|
||||
'list' => $pageData
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
?>
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
session_start();
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
echo json_encode(["code"=>401,"msg"=>"未登录"]);
|
||||
exit;
|
||||
}
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
$configFile = "./system_config.json";
|
||||
if(!file_exists($configFile)){
|
||||
echo json_encode(["code"=>500,"msg"=>"配置文件缺失"]);
|
||||
exit;
|
||||
}
|
||||
$config = json_decode(file_get_contents($configFile),true);
|
||||
$act = $_POST['act'] ?? '';
|
||||
|
||||
if($act == "save_style"){
|
||||
$config['theme_color'] = $_POST['theme_color'];
|
||||
$config['card_bg'] = $_POST['card_bg'];
|
||||
$config['body_bg'] = $_POST['body_bg'];
|
||||
$config['font_family'] = $_POST['font_family'];
|
||||
file_put_contents($configFile,json_encode($config,JSON_UNESCAPED_UNICODE));
|
||||
echo json_encode(["code"=>0,"msg"=>"样式保存成功,刷新页面生效"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if($act == "save_sidebar_link"){
|
||||
$list = json_decode($_POST['link_list'],true);
|
||||
$config['sidebar_links'] = $list;
|
||||
file_put_contents($configFile,json_encode($config,JSON_UNESCAPED_UNICODE));
|
||||
echo json_encode(["code"=>0,"msg"=>"侧边栏链接更新成功"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if($act == "get_config"){
|
||||
echo json_encode(["code"=>0,"data"=>$config]);
|
||||
exit;
|
||||
}
|
||||
echo json_encode(["code"=>400,"msg"=>"非法操作"]);
|
||||
?>
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
echo json_encode(["code"=>200,"msg"=>"PHP正常,mysqli已加载"]);
|
||||
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
session_start();
|
||||
if(empty($_SESSION['user_id'])){
|
||||
header("Location: ../login.php");
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>开发者模式 - 接口可视化调试面板</title>
|
||||
<style>
|
||||
*{box-sizing: border-box;margin:0;padding:0;font-family:Microsoft Yahei}
|
||||
body{background:#f5f7fa;padding:20px}
|
||||
.container{max-width:1400px;margin:0 auto}
|
||||
h1{color:#333;margin-bottom:24px;font-size:22px}
|
||||
.card{background:#fff;border-radius:8px;padding:16px;margin-bottom:16px;border:1px solid #e4e7ed}
|
||||
.card h3{margin-bottom:12px;color:#409eff;font-size:16px}
|
||||
.row{display:flex;gap:12px;align-items:center;margin-bottom:10px;flex-wrap:wrap}
|
||||
input{padding:8px 10px;border:1px solid #dcdfe6;border-radius:4px;width:260px}
|
||||
button{padding:8px 16px;border:none;border-radius:4px;cursor:pointer;color:#fff;background:#409eff}
|
||||
button.warn{background:#e6a23c}
|
||||
button.danger{background:#f56c6c}
|
||||
button.success{background:#67c23a}
|
||||
#result{margin-top:20px;padding:16px;background:#1e1e1e;color:#fff;border-radius:6px;white-space:pre-wrap;min-height:200px;font-family:Consolas}
|
||||
.tip{font-size:12px;color:#999}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>开发者调试面板 | 全部告警接口可视化调用</h1>
|
||||
<a href="../admin.php">← 返回系统后台管理页</a>
|
||||
|
||||
<!-- 1 临时清理单实例告警 clear_single_instance -->
|
||||
<div class="card">
|
||||
<h3>1. 临时隐藏单实例告警 clear_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="c_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="c_ins" placeholder="例 10.150.10.82:389">
|
||||
<button onclick="req('clear_single_instance','c_rule','c_ins')">执行清理</button>
|
||||
</div>
|
||||
<div class="tip">仅插入恢复日志,下次故障自动重新展示;冒号自动编码无需手动处理</div>
|
||||
</div>
|
||||
|
||||
<!-- 2 永久屏蔽单实例 offline_single_instance -->
|
||||
<div class="card">
|
||||
<h3>2. 永久屏蔽单实例 offline_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="off_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="off_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="danger" onclick="req('offline_single_instance','off_rule','off_ins')">永久屏蔽</button>
|
||||
</div>
|
||||
<div class="tip">修改monitor_rule状态0,不恢复永远不告警</div>
|
||||
</div>
|
||||
|
||||
<!-- 3 恢复单实例 restore_single_instance -->
|
||||
<div class="card">
|
||||
<h3>3. 恢复单实例 restore_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="res_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="res_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="success" onclick="req('restore_single_instance','res_rule','res_ins')">恢复实例告警</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 4 整条规则下线 clear_offline_rule -->
|
||||
<div class="card">
|
||||
<h3>4. 整条规则全部下线 clear_offline_rule</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="cr_rule" placeholder="例 Host_Ping_Offline">
|
||||
<button class="danger" onclick="reqSingle('clear_offline_rule','cr_rule')">下线整条规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5 恢复整条规则 restore_rule -->
|
||||
<div class="card">
|
||||
<h3>5. 恢复整条规则 restore_rule</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="rr_rule" placeholder="例 Host_Ping_Offline">
|
||||
<button class="success" onclick="reqSingle('restore_rule','rr_rule')">恢复整条规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6 删除单实例全部日志 delete_single_instance_log -->
|
||||
<div class="card">
|
||||
<h3>6. 删除单实例所有告警日志 delete_single_instance_log</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="del_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="del_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="warn" onclick="req('delete_single_instance_log','del_rule','del_ins')">清空日志</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 7 同步Prometheus实时告警 sync_prom_alerts -->
|
||||
<div class="card">
|
||||
<h3>7. 手动同步Prometheus告警(清除脏恢复记录)</h3>
|
||||
<div class="row">
|
||||
<button onclick="simpleReq('sync_prom_alerts')">立即同步校准数据库</button>
|
||||
</div>
|
||||
<div class="tip">自动删除故障中残留的手动恢复记录,修复前端不显示告警bug</div>
|
||||
</div>
|
||||
|
||||
<!-- 8 数据查询接口 -->
|
||||
<div class="card">
|
||||
<h3>8. 数据查询接口(查看面板原始数据)</h3>
|
||||
<div class="row">
|
||||
<button onclick="simpleReq('day_total')">今日统计 day_total</button>
|
||||
<button onclick="simpleReq('top_alert')">告警TOP10 top_alert</button>
|
||||
<button onclick="simpleReq('log_list')">今日明细 log_list</button>
|
||||
<button onclick="simpleReq('active_firing')">当前未恢复 active_firing</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 9 导出CSV报表 -->
|
||||
<div class="card">
|
||||
<h3>9. 导出今日告警CSV报表</h3>
|
||||
<div class="row">
|
||||
<button onclick="exportCsv()">下载报表 export_csv</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 结果输出区域 -->
|
||||
<div class="card">
|
||||
<h3>接口返回结果(JSON)</h3>
|
||||
<div id="result">等待操作...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 当前文件在/api/下,接口同目录index.php
|
||||
const apiUrl = "./index.php";
|
||||
const resultDom = document.getElementById("result");
|
||||
|
||||
// 双参数接口 act + rule_name + instance
|
||||
function req(act, ruleId, insId){
|
||||
const rule = document.getElementById(ruleId).value.trim();
|
||||
const ins = document.getElementById(insId).value.trim();
|
||||
if(!rule || !ins){
|
||||
resultDom.innerText = "错误:rule_name 和 instance 不能为空";
|
||||
return;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("act", act);
|
||||
params.append("rule_name", rule);
|
||||
params.append("instance", ins);
|
||||
fetch(`${apiUrl}?${params.toString()}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 单参数接口 act + rule_name
|
||||
function reqSingle(act, ruleId){
|
||||
const rule = document.getElementById(ruleId).value.trim();
|
||||
if(!rule){
|
||||
resultDom.innerText = "错误:rule_name 不能为空";
|
||||
return;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("act", act);
|
||||
params.append("rule_name", rule);
|
||||
fetch(`${apiUrl}?${params.toString()}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 无参数接口
|
||||
function simpleReq(act){
|
||||
fetch(`${apiUrl}?act=${act}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 导出CSV新窗口
|
||||
function exportCsv(){
|
||||
window.open(`${apiUrl}?act=export_csv`,"_blank");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
session_start();
|
||||
// 会话超时:30分钟无操作自动登出
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
session_destroy();
|
||||
header("Location: ../login.php");
|
||||
exit;
|
||||
}
|
||||
// 刷新登录时间
|
||||
$_SESSION['login_time'] = time();
|
||||
$userName = htmlspecialchars($_SESSION['username']);
|
||||
$userRole = $_SESSION['role'];
|
||||
$roleText = $userRole === 'admin' ? '管理员' : '只读用户';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>开发者调试面板 - Advantest 运维告警监控平台</title>
|
||||
<link rel="stylesheet" href="/static/css/font-awesome.min.css">
|
||||
<script src="/static/js/jquery.min.js"></script>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{
|
||||
background:#f9fafc;
|
||||
font-family:"Inter","PingFang SC","Microsoft YaHei";
|
||||
color:#1d2939;
|
||||
line-height:1.6;
|
||||
}
|
||||
/* 顶部导航 */
|
||||
.header{
|
||||
background:#ffffff;
|
||||
border-bottom:1px solid #eef2fb;
|
||||
color:#152c5b;
|
||||
padding:20px 40px;
|
||||
display:flex;
|
||||
justify-content:space-between;
|
||||
align-items:center;
|
||||
box-shadow:0 2px 12px rgba(21,44,91,0.05);
|
||||
}
|
||||
.header-left{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:14px;
|
||||
}
|
||||
.header-title{
|
||||
font-size:24px;
|
||||
font-weight:700;
|
||||
letter-spacing:1px;
|
||||
color:#152c5b;
|
||||
}
|
||||
.header-sub{
|
||||
font-size:14px;
|
||||
color:#64748b;
|
||||
}
|
||||
.header-right{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:18px;
|
||||
font-size:14px;
|
||||
flex-wrap:wrap;
|
||||
}
|
||||
.user-info{
|
||||
color:#475569;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:6px;
|
||||
}
|
||||
.btn-base{
|
||||
border:none;
|
||||
padding:9px 18px;
|
||||
border-radius:8px;
|
||||
cursor:pointer;
|
||||
font-size:14px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:6px;
|
||||
transition:all 0.2s ease;
|
||||
}
|
||||
.btn-outline{
|
||||
background:#ffffff;
|
||||
color:#152c5b;
|
||||
border:1px solid #cbd5e1;
|
||||
}
|
||||
.btn-outline:hover{
|
||||
background:#f7f8fc;
|
||||
border-color:#152c5b;
|
||||
}
|
||||
.btn-primary{
|
||||
background:#152c5b;
|
||||
color:#fff;
|
||||
}
|
||||
.btn-primary:hover{
|
||||
background:#0f1e42;
|
||||
}
|
||||
.btn-success{
|
||||
background:#10b981;
|
||||
color:#fff;
|
||||
}
|
||||
.btn-success:hover{
|
||||
background:#059669;
|
||||
}
|
||||
.btn-warning{
|
||||
background:#e6a23c;
|
||||
color:#fff;
|
||||
}
|
||||
.btn-danger{
|
||||
background:#f56c6c;
|
||||
color:#fff;
|
||||
}
|
||||
/* 主容器 */
|
||||
.container{
|
||||
padding:36px 40px;
|
||||
max-width:1600px;
|
||||
margin:0 auto;
|
||||
}
|
||||
h1{
|
||||
display:none;
|
||||
}
|
||||
/* 卡片样式 统一企业面板风格 */
|
||||
.card{
|
||||
background:#fff;
|
||||
border-radius:16px;
|
||||
padding:32px;
|
||||
margin-bottom:36px;
|
||||
box-shadow:0 3px 18px rgba(21,44,91,0.06);
|
||||
border:1px solid #eef2fb;
|
||||
}
|
||||
.card h3{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:10px;
|
||||
margin-bottom:26px;
|
||||
padding-bottom:16px;
|
||||
border-bottom:1px solid #eef2fb;
|
||||
font-size:19px;
|
||||
font-weight:600;
|
||||
color:#152c5b;
|
||||
}
|
||||
.card h3 i{
|
||||
color:#2563eb;
|
||||
font-size:20px;
|
||||
}
|
||||
.row{
|
||||
display:flex;
|
||||
gap:12px;
|
||||
align-items:center;
|
||||
margin-bottom:10px;
|
||||
flex-wrap:wrap;
|
||||
}
|
||||
label{
|
||||
font-size:14px;
|
||||
color:#475569;
|
||||
width:110px;
|
||||
}
|
||||
input{
|
||||
padding:8px 12px;
|
||||
border:1px solid #dcdfe6;
|
||||
border-radius:8px;
|
||||
width:280px;
|
||||
font-size:14px;
|
||||
}
|
||||
input[type="date"]{
|
||||
width:200px;
|
||||
}
|
||||
button{
|
||||
padding:9px 18px;
|
||||
border:none;
|
||||
border-radius:8px;
|
||||
cursor:pointer;
|
||||
font-size:14px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:6px;
|
||||
}
|
||||
button.normal{
|
||||
background:#409eff;
|
||||
color:#fff;
|
||||
}
|
||||
button.normal:hover{
|
||||
background:#2979e0;
|
||||
}
|
||||
.tip{
|
||||
font-size:13px;
|
||||
color:#94a3b8;
|
||||
margin-top:8px;
|
||||
padding-left:110px;
|
||||
}
|
||||
/* 返回结果区块 */
|
||||
#result{
|
||||
margin-top:20px;
|
||||
padding:20px;
|
||||
background:#1e1e1e;
|
||||
color:#fff;
|
||||
border-radius:12px;
|
||||
white-space:pre-wrap;
|
||||
min-height:260px;
|
||||
font-family:Consolas,monospace;
|
||||
font-size:13px;
|
||||
}
|
||||
/* 响应式适配 */
|
||||
@media (max-width:768px){
|
||||
.header{padding:16px 24px;flex-direction:column;gap:16px;align-items:flex-start}
|
||||
.container{padding:24px}
|
||||
.card{padding:24px}
|
||||
.row{flex-direction:column;align-items:flex-start}
|
||||
label{width:auto}
|
||||
input{width:100% !important;max-width:320px}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 顶部导航 -->
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<div>
|
||||
<div class="header-title"><i class="fa fa-code"></i> 开发者调试面板</div>
|
||||
<div class="header-sub">全部告警接口可视化调用 · 后端API调试工具</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span class="user-info"><i class="fa fa-user-circle"></i><?php echo $userName; ?>(<?php echo $roleText; ?>)</span>
|
||||
<button class="btn-base btn-outline" onclick="location.href='../admin.php'"><i class="fa fa-arrow-left"></i>返回系统后台</button>
|
||||
<button class="btn-base btn-outline" onclick="location.reload()"><i class="fa fa-refresh"></i>刷新页面</button>
|
||||
<button class="btn-base btn-primary" onclick="location.href='../dashboard.php'"><i class="fa fa-line-chart"></i>告警监控大屏</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- 1 临时清理单实例告警 clear_single_instance -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-eraser"></i>1. 临时隐藏单实例告警 clear_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="c_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="c_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="normal" onclick="req('clear_single_instance','c_rule','c_ins')"><i class="fa fa-play"></i>执行清理</button>
|
||||
</div>
|
||||
<div class="tip">仅插入恢复日志,下次故障自动重新展示;instance冒号无需手动编码,JS自动处理</div>
|
||||
</div>
|
||||
|
||||
<!-- 2 永久屏蔽单实例 offline_single_instance -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-ban"></i>2. 永久屏蔽单实例 offline_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="off_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="off_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="btn-danger" onclick="req('offline_single_instance','off_rule','off_ins')"><i class="fa fa-ban"></i>永久屏蔽</button>
|
||||
</div>
|
||||
<div class="tip">修改monitor_rule状态0,不执行恢复接口则永久不展示该实例告警</div>
|
||||
</div>
|
||||
|
||||
<!-- 3 恢复单实例 restore_single_instance -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-check-circle"></i>3. 恢复单实例 restore_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="res_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="res_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="btn-success" onclick="req('restore_single_instance','res_rule','res_ins')"><i class="fa fa-unlock"></i>恢复实例告警</button>
|
||||
</div>
|
||||
<div class="tip">解除单实例永久屏蔽状态,新故障会正常展示</div>
|
||||
</div>
|
||||
|
||||
<!-- 4 整条规则下线 clear_offline_rule -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-cog"></i>4. 整条规则全部下线 clear_offline_rule</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="cr_rule" placeholder="例 Host_Ping_Offline">
|
||||
<button class="btn-danger" onclick="reqSingle('clear_offline_rule','cr_rule')"><i class="fa fa-power-off"></i>下线整条规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5 恢复整条规则 restore_rule -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-cog"></i>5. 恢复整条规则 restore_rule</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="rr_rule" placeholder="例 Host_Ping_Offline">
|
||||
<button class="btn-success" onclick="reqSingle('restore_rule','rr_rule')"><i class="fa fa-refresh"></i>恢复整条规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6 删除单实例全部日志 delete_single_instance_log -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-trash"></i>6. 删除单实例所有告警日志 delete_single_instance_log</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="del_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="del_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="btn-warning" onclick="req('delete_single_instance_log','del_rule','del_ins')"><i class="fa fa-trash"></i>清空日志</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 7 同步Prometheus实时告警 sync_prom_alerts -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-refresh"></i>7. 手动同步Prometheus告警(清除脏恢复记录)</h3>
|
||||
<div class="row">
|
||||
<button class="normal" onclick="simpleReq('sync_prom_alerts')"><i class="fa fa-sync"></i>立即同步校准数据库</button>
|
||||
</div>
|
||||
<div class="tip">自动删除故障中残留的手动恢复记录,修复前端“故障存在但页面不显示”bug</div>
|
||||
</div>
|
||||
|
||||
<!-- 8 数据查询接口 -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-database"></i>8. 数据查询接口(获取面板原始数据)</h3>
|
||||
<div class="row">
|
||||
<button class="normal" onclick="simpleReq('day_total')"><i class="fa fa-bar-chart"></i>今日统计 day_total</button>
|
||||
<button class="normal" onclick="simpleReq('top_alert')"><i class="fa fa-pie-chart"></i>告警TOP10 top_alert</button>
|
||||
<button class="normal" onclick="simpleReq('log_list')"><i class="fa fa-list"></i>今日明细 log_list</button>
|
||||
<button class="normal" onclick="simpleReq('active_firing')"><i class="fa fa-exclamation-circle"></i>当前未恢复 active_firing</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 9 导出CSV报表(带日历选择) -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-download"></i>9. 导出告警CSV报表(支持历史日期)</h3>
|
||||
<div class="row">
|
||||
<label>选择日期:</label>
|
||||
<input type="date" id="csv_date" placeholder="留空导出今日">
|
||||
<button class="btn-success" onclick="exportCsv()"><i class="fa fa-download"></i>下载对应日期报表</button>
|
||||
</div>
|
||||
<div class="tip">不选择日期默认导出今日告警;选择历史日期可下载过往报表</div>
|
||||
</div>
|
||||
|
||||
<!-- 返回结果输出 -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-terminal"></i>接口返回结果(JSON)</h3>
|
||||
<div id="result">等待执行接口操作...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 接口路径 同目录 index.php
|
||||
const apiUrl = "./index.php";
|
||||
const resultDom = document.getElementById("result");
|
||||
|
||||
// 双参数接口 act + rule_name + instance
|
||||
function req(act, ruleId, insId){
|
||||
const rule = document.getElementById(ruleId).value.trim();
|
||||
const ins = document.getElementById(insId).value.trim();
|
||||
if(!rule || !ins){
|
||||
resultDom.innerText = "错误:rule_name 和 instance 不能为空";
|
||||
return;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("act", act);
|
||||
params.append("rule_name", rule);
|
||||
params.append("instance", ins);
|
||||
fetch(`${apiUrl}?${params.toString()}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 单参数接口 act + rule_name
|
||||
function reqSingle(act, ruleId){
|
||||
const rule = document.getElementById(ruleId).value.trim();
|
||||
if(!rule){
|
||||
resultDom.innerText = "错误:rule_name 不能为空";
|
||||
return;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("act", act);
|
||||
params.append("rule_name", rule);
|
||||
fetch(`${apiUrl}?${params.toString()}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 无参数接口
|
||||
function simpleReq(act){
|
||||
fetch(`${apiUrl}?act=${act}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 导出CSV 支持日历选择日期
|
||||
function exportCsv(){
|
||||
const dateVal = document.getElementById("csv_date").value.trim();
|
||||
let url = `${apiUrl}?act=export_csv`;
|
||||
if(dateVal){
|
||||
url += `&date=${encodeURIComponent(dateVal)}`;
|
||||
}
|
||||
window.open(url,"_blank");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
session_start();
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
session_destroy();
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
echo json_encode(["code"=>401,"msg"=>"未登录,请前往登录页面"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
header("Content-Type:application/json; charset=utf-8");
|
||||
|
||||
$promAddr = "http://10.150.117.190:9090";
|
||||
$gbUnit = 1073741824;
|
||||
$promQuery = urlencode('node_directory_size_bytes');
|
||||
$apiUrl = "{$promAddr}/api/v1/query?query={$promQuery}";
|
||||
|
||||
// 改用curl,不依赖allow_url_fopen
|
||||
$ch = curl_init($apiUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
||||
$resp = curl_exec($ch);
|
||||
$curlErr = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if(!$resp || !empty($curlErr)){
|
||||
echo json_encode(["code"=>500,"msg"=>"无法连接Prometheus:".$curlErr],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$raw = json_decode($resp, true);
|
||||
|
||||
$allData = [];
|
||||
if ($raw['status'] === 'success' && !empty($raw['data']['result'])) {
|
||||
foreach ($raw['data']['result'] as $item) {
|
||||
$dir = $item['metric']['directory'];
|
||||
$instanceIp = $item['metric']['instance'];
|
||||
$instanceIp = explode(':', $instanceIp)[0];
|
||||
$userName = basename($dir);
|
||||
$byteNum = floatval($item['value'][1]);
|
||||
$gbNum = round($byteNum / $gbUnit, 2);
|
||||
|
||||
$allData[] = [
|
||||
"instance_ip" => $instanceIp,
|
||||
"username" => $userName,
|
||||
"directory" => $dir,
|
||||
"size_gb" => $gbNum
|
||||
];
|
||||
}
|
||||
usort($allData, function ($a, $b) {
|
||||
return $b['size_gb'] <=> $a['size_gb'];
|
||||
});
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"code" => 0,
|
||||
"msg" => "查询成功",
|
||||
"list" => $allData
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
?>
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
session_start();
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
session_destroy();
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
echo json_encode(["code"=>401,"msg"=>"未登录,请前往登录页面"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
header("Content-Type:application/json; charset=utf-8");
|
||||
|
||||
$promAddr = "http://10.150.117.190:9090";
|
||||
$gbUnit = 1073741824;
|
||||
$promQuery = urlencode('node_directory_size_bytes');
|
||||
$apiUrl = "{$promAddr}/api/v1/query?query={$promQuery}";
|
||||
$resp = @file_get_contents($apiUrl);
|
||||
if(!$resp){
|
||||
echo json_encode(["code"=>500,"msg"=>"无法连接Prometheus"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$raw = json_decode($resp, true);
|
||||
|
||||
$allData = [];
|
||||
if ($raw['status'] === 'success' && !empty($raw['data']['result'])) {
|
||||
foreach ($raw['data']['result'] as $item) {
|
||||
$dir = $item['metric']['directory'];
|
||||
$instanceIp = $item['metric']['instance'];
|
||||
$instanceIp = explode(':', $instanceIp)[0];
|
||||
$userName = basename($dir);
|
||||
$byteNum = floatval($item['value'][1]);
|
||||
$gbNum = round($byteNum / $gbUnit, 2);
|
||||
|
||||
$allData[] = [
|
||||
"instance_ip" => $instanceIp,
|
||||
"username" => $userName,
|
||||
"directory" => $dir,
|
||||
"size_gb" => $gbNum
|
||||
];
|
||||
}
|
||||
usort($allData, function ($a, $b) {
|
||||
return $b['size_gb'] <=> $a['size_gb'];
|
||||
});
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"code" => 0,
|
||||
"msg" => "查询成功",
|
||||
"list" => $allData
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
?>
|
||||
@@ -0,0 +1,407 @@
|
||||
<?php
|
||||
session_start();
|
||||
$act = $_GET['act'] ?? '';
|
||||
|
||||
// 未登录直接拒绝所有接口访问
|
||||
if(empty($_SESSION['user_id'])){
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
echo json_encode(["code"=>401,"msg"=>"未登录,请前往登录页面"], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 统一数据库配置
|
||||
$dbHost = "10.150.117.190";
|
||||
$dbUser = "root";
|
||||
$dbPass = "hp93000";
|
||||
$dbName = "alert_mail_stat";
|
||||
|
||||
// 统一数据库连接函数
|
||||
function getDbConn() {
|
||||
global $dbHost, $dbUser, $dbPass, $dbName;
|
||||
$conn = mysqli_connect($dbHost, $dbUser, $dbPass, $dbName);
|
||||
mysqli_set_charset($conn, "utf8mb4");
|
||||
return $conn;
|
||||
}
|
||||
|
||||
// CSV导出接口(支持指定历史日期下载,不输出JSON头)
|
||||
if ($act === "export_csv") {
|
||||
$conn = getDbConn();
|
||||
if (!$conn) {
|
||||
header("Content-Type:text/html;charset=utf-8");
|
||||
echo "数据库连接失败:" . mysqli_connect_error();
|
||||
exit;
|
||||
}
|
||||
// 接收自定义日期参数,不传则取今日
|
||||
$targetDate = $_GET['date'] ?? date("Y-m-d");
|
||||
// 简单日期格式校验 Y-m-d
|
||||
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $targetDate)) {
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
echo json_encode(["code"=>400,"msg"=>"日期格式错误,请使用 Y-m-d,例如 2026-07-01"], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time) = '$targetDate' ORDER BY receive_time DESC";
|
||||
$res = mysqli_query($conn, $sql);
|
||||
|
||||
ob_clean();
|
||||
header("Content-Type: text/csv; charset=utf-8");
|
||||
header("Content-Disposition: attachment; filename=告警报表_" . $targetDate . ".csv");
|
||||
echo "\xEF\xBB\xBF";
|
||||
$header = ["告警状态", "告警名称", "实例", "级别", "故障开始时间", "接收时间"];
|
||||
echo implode(",", $header) . "\r\n";
|
||||
|
||||
while ($row = mysqli_fetch_assoc($res)) {
|
||||
$status = $row['alert_type'] == 1 ? "触发" : "恢复";
|
||||
$line = [
|
||||
$status,
|
||||
$row['alert_name'],
|
||||
$row['instance'],
|
||||
$row['severity'],
|
||||
$row['starts_at'],
|
||||
$row['receive_time']
|
||||
];
|
||||
foreach ($line as &$v) {
|
||||
$v = '"' . str_replace('"', '""', $v) . '"';
|
||||
}
|
||||
echo implode(",", $line) . "\r\n";
|
||||
}
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 所有JSON接口统一返回头
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
$conn = getDbConn();
|
||||
if (!$conn) {
|
||||
echo json_encode(["code" => 500, "msg" => "数据库连接失败:" . mysqli_connect_error()], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$today = date("Y-m-d");
|
||||
|
||||
// 调试代码:打印GET入参,测试完成后删除此段
|
||||
// $getAct = $_GET['act'] ?? '';
|
||||
//echo json_encode([
|
||||
// "debug_act" => $getAct,
|
||||
// "debug_rule" => $_GET['rule_name'] ?? '',
|
||||
// "debug_instance" => $_GET['instance'] ?? ''
|
||||
//], JSON_UNESCAPED_UNICODE);
|
||||
//exit;
|
||||
|
||||
// 接口1:下线整条监控规则(该规则下所有实例一并清理)
|
||||
if($act === "clear_offline_rule"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
if(empty($rule)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
// 标记该规则下全部实例为下线
|
||||
$updateSql = "UPDATE monitor_rule SET status=0, update_at=NOW() WHERE rule_name='$ruleEsc'";
|
||||
mysqli_query($conn, $updateSql);
|
||||
// 批量插入恢复记录,消除页面残留告警
|
||||
$insertSql = "
|
||||
INSERT INTO alert_log (alert_type,alert_name,instance,severity,starts_at,receive_time)
|
||||
SELECT DISTINCT 2,alert_name,instance,severity,NOW(),NOW()
|
||||
FROM alert_log
|
||||
WHERE alert_name='$ruleEsc'
|
||||
AND (alert_name,instance) NOT IN (
|
||||
SELECT alert_name,instance FROM alert_log WHERE alert_type=2
|
||||
)
|
||||
";
|
||||
mysqli_query($conn, $insertSql);
|
||||
echo json_encode(["code"=>0,"msg"=>"整条规则下线完成,所有实例告警已清除"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 接口2:单独下线单台实例(仅隐藏指定机器,同规则其他主机不受影响)
|
||||
if($act === "clear_single_instance"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
$inst = $_GET['instance'] ?? '';
|
||||
if(empty($rule) || empty($inst)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name或instance参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
$instEsc = mysqli_real_escape_string($conn, $inst);
|
||||
|
||||
// 第一步:删除当前实例旧的恢复记录,避免重复拦截
|
||||
$delSql = "DELETE FROM alert_log WHERE alert_type=2 AND alert_name='$ruleEsc' AND instance='$instEsc'";
|
||||
mysqli_query($conn, $delSql);
|
||||
|
||||
// 第二步:插入最新恢复记录,移除NOT IN限制,强制生成
|
||||
$insertSql = "
|
||||
INSERT INTO alert_log (alert_type,alert_name,instance,severity,starts_at,receive_time)
|
||||
SELECT DISTINCT 2,alert_name,instance,severity,NOW(),NOW()
|
||||
FROM alert_log
|
||||
WHERE alert_name='$ruleEsc' AND instance='$instEsc'
|
||||
";
|
||||
mysqli_query($conn, $insertSql);
|
||||
$affected = mysqli_affected_rows($conn);
|
||||
|
||||
echo json_encode([
|
||||
"code"=>0,
|
||||
"msg"=>"单实例告警清理完成",
|
||||
"insert_rows" => $affected
|
||||
],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 接口3:恢复整条已下线规则(status改为1,重新展示所有实例告警)
|
||||
if($act === "restore_rule"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
if(empty($rule)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
$updateSql = "UPDATE monitor_rule SET status=1, update_at=NOW() WHERE rule_name='$ruleEsc'";
|
||||
mysqli_query($conn, $updateSql);
|
||||
echo json_encode(["code"=>0,"msg"=>"监控规则已恢复,新故障会正常展示"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 接口4:恢复单台实例(仅把指定rule+instance置为启用)
|
||||
if($act === "restore_single_instance"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
$inst = $_GET['instance'] ?? '';
|
||||
if(empty($rule) || empty($inst)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name或instance参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
$instEsc = mysqli_real_escape_string($conn, $inst);
|
||||
$updateSql = "UPDATE monitor_rule SET status=1, update_at=NOW() WHERE rule_name='$ruleEsc' AND instance='$instEsc'";
|
||||
mysqli_query($conn, $updateSql);
|
||||
echo json_encode(["code"=>0,"msg"=>"单实例已恢复"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 接口5:删除单实例全部告警日志
|
||||
if($act === "delete_single_instance_log"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
$inst = $_GET['instance'] ?? '';
|
||||
if(empty($rule) || empty($inst)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name或instance参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
$instEsc = mysqli_real_escape_string($conn, $inst);
|
||||
$delSql = "DELETE FROM alert_log WHERE alert_name='$ruleEsc' AND instance='$instEsc'";
|
||||
mysqli_query($conn, $delSql);
|
||||
$aff = mysqli_affected_rows($conn);
|
||||
echo json_encode(["code"=>0,"msg"=>"单实例告警日志已删除","affected_rows"=>$aff],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 接口:永久屏蔽单台实例(monitor_rule 该条实例status=0,不手动恢复永远不告警)
|
||||
if($act === "offline_single_instance"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
$inst = $_GET['instance'] ?? '';
|
||||
if(empty($rule) || empty($inst)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name或instance参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
$instEsc = mysqli_real_escape_string($conn, $inst);
|
||||
|
||||
// 把这条【规则+实例】状态改为0,永久屏蔽
|
||||
$updateSql = "UPDATE monitor_rule SET status=0, update_at=NOW() WHERE rule_name='$ruleEsc' AND instance='$instEsc'";
|
||||
mysqli_query($conn, $updateSql);
|
||||
$aff = mysqli_affected_rows($conn);
|
||||
|
||||
// 同步生成恢复日志,界面立刻消失旧告警
|
||||
$delOld = "DELETE FROM alert_log WHERE alert_type=2 AND alert_name='$ruleEsc' AND instance='$instEsc'";
|
||||
mysqli_query($conn, $delOld);
|
||||
$insertSql = "
|
||||
INSERT INTO alert_log (alert_type,alert_name,instance,severity,starts_at,receive_time)
|
||||
SELECT DISTINCT 2,alert_name,instance,severity,NOW(),NOW()
|
||||
FROM alert_log
|
||||
WHERE alert_name='$ruleEsc' AND instance='$instEsc'
|
||||
";
|
||||
mysqli_query($conn, $insertSql);
|
||||
|
||||
echo json_encode([
|
||||
"code"=>0,
|
||||
"msg"=>"单实例已永久屏蔽,需调用restore_single_instance恢复",
|
||||
"update_rows"=>$aff
|
||||
],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 新增:同步Prometheus实时告警,自动清理残留恢复记录
|
||||
if($act === "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;
|
||||
}
|
||||
|
||||
$firingMap = [];
|
||||
foreach($promData['data']['alerts'] as $item){
|
||||
$an = $item['labels']['alertname'];
|
||||
$ins = $item['labels']['instance'];
|
||||
$key = "$an||$ins";
|
||||
$firingMap[$key] = 1;
|
||||
}
|
||||
|
||||
$res = mysqli_query($conn,"SELECT alert_name,instance FROM alert_log WHERE alert_type=2");
|
||||
$delCnt = 0;
|
||||
while($row = mysqli_fetch_assoc($res)){
|
||||
$k = $row['alert_name']."||".$row['instance'];
|
||||
if(isset($firingMap[$k])){
|
||||
$n = mysqli_real_escape_string($conn,$row['alert_name']);
|
||||
$i = mysqli_real_escape_string($conn,$row['instance']);
|
||||
mysqli_query($conn,"DELETE FROM alert_log WHERE alert_type=2 AND alert_name='$n' AND instance='$i'");
|
||||
$delCnt++;
|
||||
}
|
||||
}
|
||||
echo json_encode(["code"=>0,"msg"=>"同步完成","delete_clear_log"=>$delCnt],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ============后台管理专用接口 start============
|
||||
// 修改密码 BCrypt哈希加密,匹配历史密码算法
|
||||
if($act === "change_pwd"){
|
||||
$uid = $_SESSION['user_id'];
|
||||
$oldPwdRaw = $_GET['old_pwd'] ?? '';
|
||||
$newPwdRaw = $_GET['new_pwd'] ?? '';
|
||||
if(empty($oldPwdRaw) || empty($newPwdRaw)){
|
||||
echo json_encode(["code"=>400,"msg"=>"密码不能为空"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
// 查询原密码哈希
|
||||
$userSql = "SELECT password FROM admin_user WHERE user_id='$uid' LIMIT 1";
|
||||
$userRes = mysqli_query($conn,$userSql);
|
||||
$userRow = mysqli_fetch_assoc($userRes);
|
||||
if(!$userRow){
|
||||
echo json_encode(["code"=>401,"msg"=>"账户不存在"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$oldHash = $userRow['password'];
|
||||
// 校验旧密码(和原有登录逻辑一致)
|
||||
if(!password_verify($oldPwdRaw, $oldHash)){
|
||||
echo json_encode(["code"=>400,"msg"=>"原密码输入错误"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
// 生成新bcrypt哈希
|
||||
$newHash = password_hash($newPwdRaw, PASSWORD_DEFAULT);
|
||||
$updateSql = "UPDATE admin_user SET password='$newHash', update_at=NOW() WHERE user_id='$uid'";
|
||||
mysqli_query($conn,$updateSql);
|
||||
echo json_encode(["code"=>0,"msg"=>"密码修改成功"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 保存界面外观配置
|
||||
if($act === "save_theme"){
|
||||
// 兼容POST/GET
|
||||
$title = mysqli_real_escape_string($conn,$_POST['title'] ?? $_GET['title'] ?? '');
|
||||
$color = mysqli_real_escape_string($conn,$_POST['color'] ?? $_GET['color'] ?? '');
|
||||
$pagesize = mysqli_real_escape_string($conn,$_POST['pagesize'] ?? $_GET['pagesize'] ?? '20');
|
||||
$footer = mysqli_real_escape_string($conn,$_POST['footer'] ?? $_GET['footer'] ?? '');
|
||||
$lang = mysqli_real_escape_string($conn,$_POST['lang'] ?? $_GET['lang'] ?? 'zh');
|
||||
|
||||
$sql = "REPLACE INTO sys_config (k,v) VALUES
|
||||
('page_title','$title'),
|
||||
('theme_color','$color'),
|
||||
('page_size','$pagesize'),
|
||||
('footer_text','$footer'),
|
||||
('lang','$lang')";
|
||||
mysqli_query($conn,$sql);
|
||||
echo json_encode(["code"=>0,"msg"=>"外观配置已保存"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
// 保存侧边栏外链
|
||||
if($act === "save_sidebar_link"){
|
||||
$linkText = mysqli_real_escape_string($conn,$_GET['link_text'] ?? '');
|
||||
$sql = "REPLACE INTO sys_config (k,v) VALUES ('sidebar_links','$linkText')";
|
||||
mysqli_query($conn,$sql);
|
||||
echo json_encode(["code"=>0,"msg"=>"侧边外链保存成功"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 单独切换语言接口
|
||||
if ($_POST['act'] === 'save_lang') {
|
||||
$lang = mysqli_real_escape_string($conn, $_POST['lang']);
|
||||
$sql = "REPLACE INTO sys_config(k, v) VALUES ('lang', '$lang')";
|
||||
mysqli_query($conn, $sql);
|
||||
echo json_encode(["code" => 0]);
|
||||
exit;
|
||||
}
|
||||
// ============后台管理专用接口 end============
|
||||
// 1. 今日统计卡片
|
||||
if ($act === "day_total") {
|
||||
$sql = "SELECT
|
||||
SUM(IF(alert_type=1,1,0)) as fire_count,
|
||||
SUM(IF(alert_type=2,1,0)) as resolve_count,
|
||||
COUNT(DISTINCT instance) as instance_num
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today'";
|
||||
$data = mysqli_fetch_assoc(mysqli_query($conn, $sql));
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2. 今日告警TOP10图表
|
||||
if ($act === "top_alert") {
|
||||
$sql = "SELECT alert_name,COUNT(*) cnt FROM alert_log WHERE DATE(receive_time)='$today' GROUP BY alert_name ORDER BY cnt DESC LIMIT 10";
|
||||
$list = [];
|
||||
$q = mysqli_query($conn, $sql);
|
||||
while ($r = mysqli_fetch_assoc($q)) $list[] = $r;
|
||||
echo json_encode($list, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3. 今日全量告警明细表格
|
||||
if ($act === "log_list") {
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,ends_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time)='$today' ORDER BY receive_time DESC";
|
||||
$list = [];
|
||||
$q = mysqli_query($conn, $sql);
|
||||
while ($r = mysqli_fetch_assoc($q)) $list[] = $r;
|
||||
echo json_encode($list, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 4. 全局跨天未恢复告警(过滤整条下线规则,单实例仅靠恢复日志隐藏)
|
||||
if ($act === "active_firing") {
|
||||
$maxSql = "SELECT alert_name,instance,MAX(receive_time) max_rt FROM alert_log GROUP BY alert_name,instance";
|
||||
$maxRes = mysqli_query($conn, $maxSql);
|
||||
$activeList = [];
|
||||
while ($maxRow = mysqli_fetch_assoc($maxRes)) {
|
||||
$an = $conn->real_escape_string($maxRow['alert_name']);
|
||||
$ins = $conn->real_escape_string($maxRow['instance']);
|
||||
$mrt = $conn->real_escape_string($maxRow['max_rt']);
|
||||
|
||||
// 判断该规则是否整条下线,下线直接跳过
|
||||
$ruleCheck = mysqli_query($conn, "SELECT status FROM monitor_rule WHERE rule_name='$an' LIMIT 1");
|
||||
$ruleStatus = 1;
|
||||
if($ruleCheck && $ruleRow = mysqli_fetch_assoc($ruleCheck)){
|
||||
$ruleStatus = intval($ruleRow['status']);
|
||||
}
|
||||
if($ruleStatus === 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$rowSql = "SELECT alert_name,instance,severity,starts_at,receive_time,alert_type
|
||||
FROM alert_log
|
||||
WHERE alert_name='$an' AND instance='$ins' AND receive_time='$mrt'";
|
||||
$rowRes = mysqli_query($conn, $rowSql);
|
||||
$data = mysqli_fetch_assoc($rowRes);
|
||||
if ($data && (int)$data['alert_type'] === 1) {
|
||||
$activeList[] = $data;
|
||||
}
|
||||
}
|
||||
echo json_encode($activeList, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 非法请求兜底
|
||||
echo json_encode(["code" => 400, "msg" => "无效请求参数act"], JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
$host = "10.150.117.190";
|
||||
$user = "root";
|
||||
$pass = "hp93000";
|
||||
$dbname = "alert_mail_stat";
|
||||
$conn = mysqli_connect($host,$user,$pass,$dbname);
|
||||
mysqli_set_charset($conn,"utf8mb4");
|
||||
$act = $_GET['act'] ?? '';
|
||||
|
||||
// 1. 获取今日总统计:触发次数、恢复次数、故障实例数
|
||||
if($act == "day_total"){
|
||||
$today = date("Y-m-d");
|
||||
$sql = "SELECT
|
||||
SUM(IF(alert_type=1,1,0)) as fire_count,
|
||||
SUM(IF(alert_type=2,1,0)) as resolve_count,
|
||||
COUNT(DISTINCT instance) as instance_num
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today'";
|
||||
$res = mysqli_fetch_assoc(mysqli_query($conn,$sql));
|
||||
echo json_encode($res,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 2. 今日告警TOP排行(按发生次数)
|
||||
if($act == "top_alert"){
|
||||
$today = date("Y-m-d");
|
||||
$sql = "SELECT alert_name,COUNT(*) cnt FROM alert_log WHERE DATE(receive_time)='$today' GROUP BY alert_name ORDER BY cnt DESC LIMIT 10";
|
||||
$data = [];
|
||||
$q = mysqli_query($conn,$sql);
|
||||
while($r = mysqli_fetch_assoc($q)) $data[] = $r;
|
||||
echo json_encode($data,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 3. 今日明细列表(前端表格)
|
||||
if($act == "log_list"){
|
||||
$today = date("Y-m-d");
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,ends_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time)='$today' ORDER BY receive_time DESC";
|
||||
$data = [];
|
||||
$q = mysqli_query($conn,$sql);
|
||||
while($r = mysqli_fetch_assoc($q)) $data[] = $r;
|
||||
echo json_encode($data,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
// 宿主机内网IP,容器访问MySQL
|
||||
$host = "10.150.117.190";
|
||||
$user = "root";
|
||||
$pass = "hp93000";
|
||||
$dbname = "alert_mail_stat";
|
||||
|
||||
$conn = mysqli_connect($host,$user,$pass,$dbname);
|
||||
if(!$conn){
|
||||
echo json_encode(["code"=>500,"msg"=>"数据库连接失败:".mysqli_connect_error()]);
|
||||
exit;
|
||||
}
|
||||
mysqli_set_charset($conn,"utf8mb4");
|
||||
$act = $_GET['act'] ?? '';
|
||||
$today = date("Y-m-d");
|
||||
|
||||
// 当日总统计
|
||||
if($act == "day_total"){
|
||||
$sql = "SELECT
|
||||
SUM(IF(alert_type=1,1,0)) as fire_count,
|
||||
SUM(IF(alert_type=2,1,0)) as resolve_count,
|
||||
COUNT(DISTINCT instance) as instance_num
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today'";
|
||||
$res = mysqli_fetch_assoc(mysqli_query($conn,$sql));
|
||||
echo json_encode($res,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 告警TOP10
|
||||
if($act == "top_alert"){
|
||||
$sql = "SELECT alert_name,COUNT(*) cnt FROM alert_log WHERE DATE(receive_time)='$today' GROUP BY alert_name ORDER BY cnt DESC LIMIT 10";
|
||||
$data = [];
|
||||
$q = mysqli_query($conn,$sql);
|
||||
while($r = mysqli_fetch_assoc($q)) $data[] = $r;
|
||||
echo json_encode($data,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 明细列表
|
||||
if($act == "log_list"){
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,ends_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time)='$today' ORDER BY receive_time DESC";
|
||||
$data = [];
|
||||
$q = mysqli_query($conn,$sql);
|
||||
while($r = mysqli_fetch_assoc($q)) $data[] = $r;
|
||||
echo json_encode($data,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
// 宿主机内网IP,容器访问MySQL
|
||||
$host = "10.150.117.190";
|
||||
$user = "root";
|
||||
$pass = "hp93000";
|
||||
$dbname = "alert_mail_stat";
|
||||
|
||||
$conn = mysqli_connect($host,$user,$pass,$dbname);
|
||||
if(!$conn){
|
||||
echo json_encode(["code"=>500,"msg"=>"数据库连接失败:".mysqli_connect_error()]);
|
||||
exit;
|
||||
}
|
||||
mysqli_set_charset($conn,"utf8mb4");
|
||||
$act = $_GET['act'] ?? '';
|
||||
$today = date("Y-m-d");
|
||||
|
||||
// 当日总统计
|
||||
if($act == "day_total"){
|
||||
$sql = "SELECT
|
||||
SUM(IF(alert_type=1,1,0)) as fire_count,
|
||||
SUM(IF(alert_type=2,1,0)) as resolve_count,
|
||||
COUNT(DISTINCT instance) as instance_num
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today'";
|
||||
$res = mysqli_fetch_assoc(mysqli_query($conn,$sql));
|
||||
echo json_encode($res,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 告警TOP10
|
||||
if($act == "top_alert"){
|
||||
$sql = "SELECT alert_name,COUNT(*) cnt FROM alert_log WHERE DATE(receive_time)='$today' GROUP BY alert_name ORDER BY cnt DESC LIMIT 10";
|
||||
$data = [];
|
||||
$q = mysqli_query($conn,$sql);
|
||||
while($r = mysqli_fetch_assoc($q)) $data[] = $r;
|
||||
echo json_encode($data,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 明细列表
|
||||
if($act == "log_list"){
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,ends_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time)='$today' ORDER BY receive_time DESC";
|
||||
$data = [];
|
||||
$q = mysqli_query($conn,$sql);
|
||||
while($r = mysqli_fetch_assoc($q)) $data[] = $r;
|
||||
echo json_encode($data,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
$act = $_GET['act'] ?? '';
|
||||
if($act == "export_csv"){
|
||||
// 先不输出JSON头,单独处理下载
|
||||
$host = "10.150.117.190";
|
||||
$user = "root";
|
||||
$pass = "hp93000";
|
||||
$dbname = "alert_mail_stat";
|
||||
$conn = mysqli_connect($host,$user,$pass,$dbname);
|
||||
if(!$conn){
|
||||
header("Content-Type:text/html;charset=utf-8");
|
||||
echo "数据库连接失败:".mysqli_connect_error();
|
||||
exit;
|
||||
}
|
||||
mysqli_set_charset($conn,"utf8mb4");
|
||||
$today = date("Y-m-d");
|
||||
// 查询今日告警,去掉ends_at恢复时间字段
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today' ORDER BY receive_time DESC";
|
||||
$res = mysqli_query($conn,$sql);
|
||||
|
||||
// 下载头
|
||||
ob_clean();
|
||||
header("Content-Type: text/csv; charset=utf-8");
|
||||
header("Content-Disposition: attachment; filename=告警报表_".$today.".csv");
|
||||
// UTF8 BOM,Windows Excel不乱码
|
||||
echo "\xEF\xBB\xBF";
|
||||
// CSV表头
|
||||
$header = ["告警状态","告警名称","实例","级别","故障开始时间","接收时间"];
|
||||
echo implode(",",$header)."\r\n";
|
||||
// 逐行输出数据
|
||||
while($row = mysqli_fetch_assoc($res)){
|
||||
$status = $row['alert_type'] == 1 ? "触发" : "恢复";
|
||||
$line = [
|
||||
$status,
|
||||
$row['alert_name'],
|
||||
$row['instance'],
|
||||
$row['severity'],
|
||||
$row['starts_at'],
|
||||
$row['receive_time']
|
||||
];
|
||||
// 字段逗号转义,防止内容逗号破坏CSV格式
|
||||
foreach($line as &$v){
|
||||
$v = '"'.str_replace('"','""',$v).'"';
|
||||
}
|
||||
echo implode(",",$line)."\r\n";
|
||||
}
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 以下普通JSON接口
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
$host = "10.150.117.190";
|
||||
$user = "root";
|
||||
$pass = "hp93000";
|
||||
$dbname = "alert_mail_stat";
|
||||
|
||||
$conn = mysqli_connect($host,$user,$pass,$dbname);
|
||||
if(!$conn){
|
||||
echo json_encode(["code"=>500,"msg"=>"数据库连接失败:".mysqli_connect_error()]);
|
||||
exit;
|
||||
}
|
||||
mysqli_set_charset($conn,"utf8mb4");
|
||||
$today = date("Y-m-d");
|
||||
|
||||
// 当日总统计
|
||||
if($act == "day_total"){
|
||||
$sql = "SELECT
|
||||
SUM(IF(alert_type=1,1,0)) as fire_count,
|
||||
SUM(IF(alert_type=2,1,0)) as resolve_count,
|
||||
COUNT(DISTINCT instance) as instance_num
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today'";
|
||||
$res = mysqli_fetch_assoc(mysqli_query($conn,$sql));
|
||||
echo json_encode($res,JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 告警TOP10
|
||||
if($act == "top_alert"){
|
||||
$sql = "SELECT alert_name,COUNT(*) cnt FROM alert_log WHERE DATE(receive_time)='$today' GROUP BY alert_name ORDER BY cnt DESC LIMIT 10";
|
||||
$data = [];
|
||||
$q = mysqli_query($conn,$sql);
|
||||
while($r = mysqli_fetch_assoc($q)) $data[] = $r;
|
||||
echo json_encode($data,JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 明细列表
|
||||
if($act == "log_list"){
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,ends_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time)='$today' ORDER BY receive_time DESC";
|
||||
$data = [];
|
||||
$q = mysqli_query($conn,$sql);
|
||||
while($r = mysqli_fetch_assoc($q)) $data[] = $r;
|
||||
echo json_encode($data,JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 无效参数兜底
|
||||
echo json_encode(["code"=>400,"msg"=>"无效请求参数act"],JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
// 统一数据库配置
|
||||
$dbHost = "10.150.117.190";
|
||||
$dbUser = "root";
|
||||
$dbPass = "hp93000";
|
||||
$dbName = "alert_mail_stat";
|
||||
|
||||
// 统一数据库连接函数
|
||||
function getDbConn() {
|
||||
global $dbHost, $dbUser, $dbPass, $dbName;
|
||||
$conn = mysqli_connect($dbHost, $dbUser, $dbPass, $dbName);
|
||||
mysqli_set_charset($conn, "utf8mb4");
|
||||
return $conn;
|
||||
}
|
||||
|
||||
$act = $_GET['act'] ?? '';
|
||||
|
||||
// CSV导出接口(独立流式下载,不输出JSON头)
|
||||
if ($act === "export_csv") {
|
||||
$conn = getDbConn();
|
||||
if (!$conn) {
|
||||
header("Content-Type:text/html;charset=utf-8");
|
||||
echo "数据库连接失败:" . mysqli_connect_error();
|
||||
exit;
|
||||
}
|
||||
$today = date("Y-m-d");
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today' ORDER BY receive_time DESC";
|
||||
$res = mysqli_query($conn, $sql);
|
||||
|
||||
ob_clean();
|
||||
header("Content-Type: text/csv; charset=utf-8");
|
||||
header("Content-Disposition: attachment; filename=告警报表_" . $today . ".csv");
|
||||
echo "\xEF\xBB\xBF";
|
||||
$header = ["告警状态", "告警名称", "实例", "级别", "故障开始时间", "接收时间"];
|
||||
echo implode(",", $header) . "\r\n";
|
||||
|
||||
while ($row = mysqli_fetch_assoc($res)) {
|
||||
$status = $row['alert_type'] == 1 ? "触发" : "恢复";
|
||||
$line = [
|
||||
$status,
|
||||
$row['alert_name'],
|
||||
$row['instance'],
|
||||
$row['severity'],
|
||||
$row['starts_at'],
|
||||
$row['receive_time']
|
||||
];
|
||||
foreach ($line as &$v) {
|
||||
$v = '"' . str_replace('"', '""', $v) . '"';
|
||||
}
|
||||
echo implode(",", $line) . "\r\n";
|
||||
}
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 所有JSON接口统一返回头
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
$conn = getDbConn();
|
||||
if (!$conn) {
|
||||
echo json_encode(["code" => 500, "msg" => "数据库连接失败:" . mysqli_connect_error()], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$today = date("Y-m-d");
|
||||
|
||||
// 1. 今日统计卡片
|
||||
if ($act === "day_total") {
|
||||
$sql = "SELECT
|
||||
SUM(IF(alert_type=1,1,0)) as fire_count,
|
||||
SUM(IF(alert_type=2,1,0)) as resolve_count,
|
||||
COUNT(DISTINCT instance) as instance_num
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today'";
|
||||
$data = mysqli_fetch_assoc(mysqli_query($conn, $sql));
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2. 今日告警TOP10图表
|
||||
if ($act === "top_alert") {
|
||||
$sql = "SELECT alert_name,COUNT(*) cnt FROM alert_log WHERE DATE(receive_time)='$today' GROUP BY alert_name ORDER BY cnt DESC LIMIT 10";
|
||||
$list = [];
|
||||
$q = mysqli_query($conn, $sql);
|
||||
while ($r = mysqli_fetch_assoc($q)) $list[] = $r;
|
||||
echo json_encode($list, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3. 今日全量告警明细表格
|
||||
if ($act === "log_list") {
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,ends_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time)='$today' ORDER BY receive_time DESC";
|
||||
$list = [];
|
||||
$q = mysqli_query($conn, $sql);
|
||||
while ($r = mysqli_fetch_assoc($q)) $list[] = $r;
|
||||
echo json_encode($list, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 4. 全局跨天未恢复告警(解决6.30残留故障不显示)
|
||||
if ($act === "active_firing") {
|
||||
// 查询全部触发告警
|
||||
$fireSql = "SELECT DISTINCT alert_name,instance,severity,starts_at,receive_time FROM alert_log WHERE alert_type=1";
|
||||
$fireRes = mysqli_query($conn, $fireSql);
|
||||
$fireMap = [];
|
||||
while ($row = mysqli_fetch_assoc($fireRes)) {
|
||||
$key = $row['alert_name'] . "|" . $row['instance'];
|
||||
$fireMap[$key] = $row;
|
||||
}
|
||||
if (empty($fireMap)) {
|
||||
echo json_encode([], JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
// 查询全部恢复告警唯一标识
|
||||
$resolveSql = "SELECT DISTINCT CONCAT(alert_name,'|',instance) as k FROM alert_log WHERE alert_type=2";
|
||||
$resolveRes = mysqli_query($conn, $resolveSql);
|
||||
$resolveKeys = [];
|
||||
while ($r = mysqli_fetch_assoc($resolveRes)) {
|
||||
$resolveKeys[] = $r['k'];
|
||||
}
|
||||
// 过滤:存在触发、无对应恢复 = 当前活跃故障
|
||||
$activeList = [];
|
||||
foreach ($fireMap as $key => $row) {
|
||||
if (!in_array($key, $resolveKeys)) {
|
||||
$row['alert_type'] = 1;
|
||||
$activeList[] = $row;
|
||||
}
|
||||
}
|
||||
echo json_encode($activeList, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 非法请求兜底
|
||||
echo json_encode(["code" => 400, "msg" => "无效请求参数act"], JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
session_start();
|
||||
// 未登录直接拒绝所有接口访问
|
||||
if(empty($_SESSION['user_id'])){
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
echo json_encode(["code"=>401,"msg"=>"未登录,请前往登录页面"]);
|
||||
exit;
|
||||
}
|
||||
// 统一数据库配置
|
||||
$dbHost = "10.150.117.190";
|
||||
$dbUser = "root";
|
||||
$dbPass = "hp93000";
|
||||
$dbName = "alert_mail_stat";
|
||||
|
||||
// 统一数据库连接函数
|
||||
function getDbConn() {
|
||||
global $dbHost, $dbUser, $dbPass, $dbName;
|
||||
$conn = mysqli_connect($dbHost, $dbUser, $dbPass, $dbName);
|
||||
mysqli_set_charset($conn, "utf8mb4");
|
||||
return $conn;
|
||||
}
|
||||
|
||||
$act = $_GET['act'] ?? '';
|
||||
|
||||
// CSV导出接口(独立流式下载,不输出JSON头)
|
||||
if ($act === "export_csv") {
|
||||
$conn = getDbConn();
|
||||
if (!$conn) {
|
||||
header("Content-Type:text/html;charset=utf-8");
|
||||
echo "数据库连接失败:" . mysqli_connect_error();
|
||||
exit;
|
||||
}
|
||||
$today = date("Y-m-d");
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today' ORDER BY receive_time DESC";
|
||||
$res = mysqli_query($conn, $sql);
|
||||
|
||||
ob_clean();
|
||||
header("Content-Type: text/csv; charset=utf-8");
|
||||
header("Content-Disposition: attachment; filename=告警报表_" . $today . ".csv");
|
||||
echo "\xEF\xBB\xBF";
|
||||
$header = ["告警状态", "告警名称", "实例", "级别", "故障开始时间", "接收时间"];
|
||||
echo implode(",", $header) . "\r\n";
|
||||
|
||||
while ($row = mysqli_fetch_assoc($res)) {
|
||||
$status = $row['alert_type'] == 1 ? "触发" : "恢复";
|
||||
$line = [
|
||||
$status,
|
||||
$row['alert_name'],
|
||||
$row['instance'],
|
||||
$row['severity'],
|
||||
$row['starts_at'],
|
||||
$row['receive_time']
|
||||
];
|
||||
foreach ($line as &$v) {
|
||||
$v = '"' . str_replace('"', '""', $v) . '"';
|
||||
}
|
||||
echo implode(",", $line) . "\r\n";
|
||||
}
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 所有JSON接口统一返回头
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
$conn = getDbConn();
|
||||
if (!$conn) {
|
||||
echo json_encode(["code" => 500, "msg" => "数据库连接失败:" . mysqli_connect_error()], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$today = date("Y-m-d");
|
||||
|
||||
// 1. 今日统计卡片
|
||||
if ($act === "day_total") {
|
||||
$sql = "SELECT
|
||||
SUM(IF(alert_type=1,1,0)) as fire_count,
|
||||
SUM(IF(alert_type=2,1,0)) as resolve_count,
|
||||
COUNT(DISTINCT instance) as instance_num
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today'";
|
||||
$data = mysqli_fetch_assoc(mysqli_query($conn, $sql));
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2. 今日告警TOP10图表
|
||||
if ($act === "top_alert") {
|
||||
$sql = "SELECT alert_name,COUNT(*) cnt FROM alert_log WHERE DATE(receive_time)='$today' GROUP BY alert_name ORDER BY cnt DESC LIMIT 10";
|
||||
$list = [];
|
||||
$q = mysqli_query($conn, $sql);
|
||||
while ($r = mysqli_fetch_assoc($q)) $list[] = $r;
|
||||
echo json_encode($list, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3. 今日全量告警明细表格
|
||||
if ($act === "log_list") {
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,ends_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time)='$today' ORDER BY receive_time DESC";
|
||||
$list = [];
|
||||
$q = mysqli_query($conn, $sql);
|
||||
while ($r = mysqli_fetch_assoc($q)) $list[] = $r;
|
||||
echo json_encode($list, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 4. 全局跨天未恢复告警【修复版:只看每条告警最新状态,兼容低版本MariaDB】
|
||||
if ($act === "active_firing") {
|
||||
// 第一步:分组获取每个告警+实例的最新入库时间
|
||||
$maxSql = "SELECT alert_name,instance,MAX(receive_time) max_rt FROM alert_log GROUP BY alert_name,instance";
|
||||
$maxRes = mysqli_query($conn, $maxSql);
|
||||
$activeList = [];
|
||||
while ($maxRow = mysqli_fetch_assoc($maxRes)) {
|
||||
// 转义防止SQL注入
|
||||
$an = $conn->real_escape_string($maxRow['alert_name']);
|
||||
$ins = $conn->real_escape_string($maxRow['instance']);
|
||||
$mrt = $conn->real_escape_string($maxRow['max_rt']);
|
||||
// 根据告警名、实例、最新时间取出最后一条记录
|
||||
$rowSql = "SELECT alert_name,instance,severity,starts_at,receive_time,alert_type
|
||||
FROM alert_log
|
||||
WHERE alert_name='$an' AND instance='$ins' AND receive_time='$mrt'";
|
||||
$rowRes = mysqli_query($conn, $rowSql);
|
||||
$data = mysqli_fetch_assoc($rowRes);
|
||||
// 仅最后一条状态为触发,才判定为当前活跃未恢复告警
|
||||
if ($data && (int)$data['alert_type'] === 1) {
|
||||
$activeList[] = $data;
|
||||
}
|
||||
}
|
||||
echo json_encode($activeList, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 非法请求兜底
|
||||
echo json_encode(["code" => 400, "msg" => "无效请求参数act"], JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,330 @@
|
||||
<?php
|
||||
session_start();
|
||||
$act = $_GET['act'] ?? '';
|
||||
|
||||
// 未登录直接拒绝所有接口访问
|
||||
if(empty($_SESSION['user_id'])){
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
echo json_encode(["code"=>401,"msg"=>"未登录,请前往登录页面"], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 统一数据库配置
|
||||
$dbHost = "10.150.117.190";
|
||||
$dbUser = "root";
|
||||
$dbPass = "hp93000";
|
||||
$dbName = "alert_mail_stat";
|
||||
|
||||
// 统一数据库连接函数
|
||||
function getDbConn() {
|
||||
global $dbHost, $dbUser, $dbPass, $dbName;
|
||||
$conn = mysqli_connect($dbHost, $dbUser, $dbPass, $dbName);
|
||||
mysqli_set_charset($conn, "utf8mb4");
|
||||
return $conn;
|
||||
}
|
||||
|
||||
// CSV导出接口(独立流式下载,不输出JSON头)
|
||||
if ($act === "export_csv") {
|
||||
$conn = getDbConn();
|
||||
if (!$conn) {
|
||||
header("Content-Type:text/html;charset=utf-8");
|
||||
echo "数据库连接失败:" . mysqli_connect_error();
|
||||
exit;
|
||||
}
|
||||
$today = date("Y-m-d");
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today' ORDER BY receive_time DESC";
|
||||
$res = mysqli_query($conn, $sql);
|
||||
|
||||
ob_clean();
|
||||
header("Content-Type: text/csv; charset=utf-8");
|
||||
header("Content-Disposition: attachment; filename=告警报表_" . $today . ".csv");
|
||||
echo "\xEF\xBB\xBF";
|
||||
$header = ["告警状态", "告警名称", "实例", "级别", "故障开始时间", "接收时间"];
|
||||
echo implode(",", $header) . "\r\n";
|
||||
|
||||
while ($row = mysqli_fetch_assoc($res)) {
|
||||
$status = $row['alert_type'] == 1 ? "触发" : "恢复";
|
||||
$line = [
|
||||
$status,
|
||||
$row['alert_name'],
|
||||
$row['instance'],
|
||||
$row['severity'],
|
||||
$row['starts_at'],
|
||||
$row['receive_time']
|
||||
];
|
||||
foreach ($line as &$v) {
|
||||
$v = '"' . str_replace('"', '""', $v) . '"';
|
||||
}
|
||||
echo implode(",", $line) . "\r\n";
|
||||
}
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 所有JSON接口统一返回头
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
$conn = getDbConn();
|
||||
if (!$conn) {
|
||||
echo json_encode(["code" => 500, "msg" => "数据库连接失败:" . mysqli_connect_error()], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$today = date("Y-m-d");
|
||||
|
||||
// 调试代码:打印GET入参,测试完成后删除此段
|
||||
// $getAct = $_GET['act'] ?? '';
|
||||
//echo json_encode([
|
||||
// "debug_act" => $getAct,
|
||||
// "debug_rule" => $_GET['rule_name'] ?? '',
|
||||
// "debug_instance" => $_GET['instance'] ?? ''
|
||||
//], JSON_UNESCAPED_UNICODE);
|
||||
//exit;
|
||||
|
||||
// 接口1:下线整条监控规则(该规则下所有实例一并清理)
|
||||
if($act === "clear_offline_rule"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
if(empty($rule)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
// 标记该规则下全部实例为下线
|
||||
$updateSql = "UPDATE monitor_rule SET status=0, update_at=NOW() WHERE rule_name='$ruleEsc'";
|
||||
mysqli_query($conn, $updateSql);
|
||||
// 批量插入恢复记录,消除页面残留告警
|
||||
$insertSql = "
|
||||
INSERT INTO alert_log (alert_type,alert_name,instance,severity,starts_at,receive_time)
|
||||
SELECT DISTINCT 2,alert_name,instance,severity,NOW(),NOW()
|
||||
FROM alert_log
|
||||
WHERE alert_name='$ruleEsc'
|
||||
AND (alert_name,instance) NOT IN (
|
||||
SELECT alert_name,instance FROM alert_log WHERE alert_type=2
|
||||
)
|
||||
";
|
||||
mysqli_query($conn, $insertSql);
|
||||
echo json_encode(["code"=>0,"msg"=>"整条规则下线完成,所有实例告警已清除"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 接口2:单独下线单台实例(仅隐藏指定机器,同规则其他主机不受影响)
|
||||
if($act === "clear_single_instance"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
$inst = $_GET['instance'] ?? '';
|
||||
if(empty($rule) || empty($inst)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name或instance参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
$instEsc = mysqli_real_escape_string($conn, $inst);
|
||||
|
||||
// 第一步:删除当前实例旧的恢复记录,避免重复拦截
|
||||
$delSql = "DELETE FROM alert_log WHERE alert_type=2 AND alert_name='$ruleEsc' AND instance='$instEsc'";
|
||||
mysqli_query($conn, $delSql);
|
||||
|
||||
// 第二步:插入最新恢复记录,移除NOT IN限制,强制生成
|
||||
$insertSql = "
|
||||
INSERT INTO alert_log (alert_type,alert_name,instance,severity,starts_at,receive_time)
|
||||
SELECT DISTINCT 2,alert_name,instance,severity,NOW(),NOW()
|
||||
FROM alert_log
|
||||
WHERE alert_name='$ruleEsc' AND instance='$instEsc'
|
||||
";
|
||||
mysqli_query($conn, $insertSql);
|
||||
$affected = mysqli_affected_rows($conn);
|
||||
|
||||
echo json_encode([
|
||||
"code"=>0,
|
||||
"msg"=>"单实例告警清理完成",
|
||||
"insert_rows" => $affected
|
||||
],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 接口3:恢复整条已下线规则(status改为1,重新展示所有实例告警)
|
||||
if($act === "restore_rule"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
if(empty($rule)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
$updateSql = "UPDATE monitor_rule SET status=1, update_at=NOW() WHERE rule_name='$ruleEsc'";
|
||||
mysqli_query($conn, $updateSql);
|
||||
echo json_encode(["code"=>0,"msg"=>"监控规则已恢复,新故障会正常展示"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 接口4:恢复单台实例(仅把指定rule+instance置为启用)
|
||||
if($act === "restore_single_instance"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
$inst = $_GET['instance'] ?? '';
|
||||
if(empty($rule) || empty($inst)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name或instance参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
$instEsc = mysqli_real_escape_string($conn, $inst);
|
||||
$updateSql = "UPDATE monitor_rule SET status=1, update_at=NOW() WHERE rule_name='$ruleEsc' AND instance='$instEsc'";
|
||||
mysqli_query($conn, $updateSql);
|
||||
echo json_encode(["code"=>0,"msg"=>"单实例已恢复"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 接口5:删除单实例全部告警日志
|
||||
if($act === "delete_single_instance_log"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
$inst = $_GET['instance'] ?? '';
|
||||
if(empty($rule) || empty($inst)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name或instance参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
$instEsc = mysqli_real_escape_string($conn, $inst);
|
||||
$delSql = "DELETE FROM alert_log WHERE alert_name='$ruleEsc' AND instance='$instEsc'";
|
||||
mysqli_query($conn, $delSql);
|
||||
$aff = mysqli_affected_rows($conn);
|
||||
echo json_encode(["code"=>0,"msg"=>"单实例告警日志已删除","affected_rows"=>$aff],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 接口:永久屏蔽单台实例(monitor_rule 该条实例status=0,不手动恢复永远不告警)
|
||||
if($act === "offline_single_instance"){
|
||||
$rule = $_GET['rule_name'] ?? '';
|
||||
$inst = $_GET['instance'] ?? '';
|
||||
if(empty($rule) || empty($inst)){
|
||||
echo json_encode(["code"=>400,"msg"=>"缺少rule_name或instance参数"],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$ruleEsc = mysqli_real_escape_string($conn, $rule);
|
||||
$instEsc = mysqli_real_escape_string($conn, $inst);
|
||||
|
||||
// 把这条【规则+实例】状态改为0,永久屏蔽
|
||||
$updateSql = "UPDATE monitor_rule SET status=0, update_at=NOW() WHERE rule_name='$ruleEsc' AND instance='$instEsc'";
|
||||
mysqli_query($conn, $updateSql);
|
||||
$aff = mysqli_affected_rows($conn);
|
||||
|
||||
// 同步生成恢复日志,界面立刻消失旧告警
|
||||
$delOld = "DELETE FROM alert_log WHERE alert_type=2 AND alert_name='$ruleEsc' AND instance='$instEsc'";
|
||||
mysqli_query($conn, $delOld);
|
||||
$insertSql = "
|
||||
INSERT INTO alert_log (alert_type,alert_name,instance,severity,starts_at,receive_time)
|
||||
SELECT DISTINCT 2,alert_name,instance,severity,NOW(),NOW()
|
||||
FROM alert_log
|
||||
WHERE alert_name='$ruleEsc' AND instance='$instEsc'
|
||||
";
|
||||
mysqli_query($conn, $insertSql);
|
||||
|
||||
echo json_encode([
|
||||
"code"=>0,
|
||||
"msg"=>"单实例已永久屏蔽,需调用restore_single_instance恢复",
|
||||
"update_rows"=>$aff
|
||||
],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 新增:同步Prometheus实时告警,自动清理残留恢复记录
|
||||
if($act === "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;
|
||||
}
|
||||
|
||||
$firingMap = [];
|
||||
foreach($promData['data']['alerts'] as $item){
|
||||
$an = $item['labels']['alertname'];
|
||||
$ins = $item['labels']['instance'];
|
||||
$key = "$an||$ins";
|
||||
$firingMap[$key] = 1;
|
||||
}
|
||||
|
||||
$res = mysqli_query($conn,"SELECT alert_name,instance FROM alert_log WHERE alert_type=2");
|
||||
$delCnt = 0;
|
||||
while($row = mysqli_fetch_assoc($res)){
|
||||
$k = $row['alert_name']."||".$row['instance'];
|
||||
if(isset($firingMap[$k])){
|
||||
$n = mysqli_real_escape_string($conn,$row['alert_name']);
|
||||
$i = mysqli_real_escape_string($conn,$row['instance']);
|
||||
mysqli_query($conn,"DELETE FROM alert_log WHERE alert_type=2 AND alert_name='$n' AND instance='$i'");
|
||||
$delCnt++;
|
||||
}
|
||||
}
|
||||
echo json_encode(["code"=>0,"msg"=>"同步完成","delete_clear_log"=>$delCnt],JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 1. 今日统计卡片
|
||||
if ($act === "day_total") {
|
||||
$sql = "SELECT
|
||||
SUM(IF(alert_type=1,1,0)) as fire_count,
|
||||
SUM(IF(alert_type=2,1,0)) as resolve_count,
|
||||
COUNT(DISTINCT instance) as instance_num
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today'";
|
||||
$data = mysqli_fetch_assoc(mysqli_query($conn, $sql));
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2. 今日告警TOP10图表
|
||||
if ($act === "top_alert") {
|
||||
$sql = "SELECT alert_name,COUNT(*) cnt FROM alert_log WHERE DATE(receive_time)='$today' GROUP BY alert_name ORDER BY cnt DESC LIMIT 10";
|
||||
$list = [];
|
||||
$q = mysqli_query($conn, $sql);
|
||||
while ($r = mysqli_fetch_assoc($q)) $list[] = $r;
|
||||
echo json_encode($list, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3. 今日全量告警明细表格
|
||||
if ($act === "log_list") {
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,ends_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time)='$today' ORDER BY receive_time DESC";
|
||||
$list = [];
|
||||
$q = mysqli_query($conn, $sql);
|
||||
while ($r = mysqli_fetch_assoc($q)) $list[] = $r;
|
||||
echo json_encode($list, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 4. 全局跨天未恢复告警(过滤整条下线规则,单实例仅靠恢复日志隐藏)
|
||||
if ($act === "active_firing") {
|
||||
$maxSql = "SELECT alert_name,instance,MAX(receive_time) max_rt FROM alert_log GROUP BY alert_name,instance";
|
||||
$maxRes = mysqli_query($conn, $maxSql);
|
||||
$activeList = [];
|
||||
while ($maxRow = mysqli_fetch_assoc($maxRes)) {
|
||||
$an = $conn->real_escape_string($maxRow['alert_name']);
|
||||
$ins = $conn->real_escape_string($maxRow['instance']);
|
||||
$mrt = $conn->real_escape_string($maxRow['max_rt']);
|
||||
|
||||
// 判断该规则是否整条下线,下线直接跳过
|
||||
$ruleCheck = mysqli_query($conn, "SELECT status FROM monitor_rule WHERE rule_name='$an' LIMIT 1");
|
||||
$ruleStatus = 1;
|
||||
if($ruleCheck && $ruleRow = mysqli_fetch_assoc($ruleCheck)){
|
||||
$ruleStatus = intval($ruleRow['status']);
|
||||
}
|
||||
if($ruleStatus === 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$rowSql = "SELECT alert_name,instance,severity,starts_at,receive_time,alert_type
|
||||
FROM alert_log
|
||||
WHERE alert_name='$an' AND instance='$ins' AND receive_time='$mrt'";
|
||||
$rowRes = mysqli_query($conn, $rowSql);
|
||||
$data = mysqli_fetch_assoc($rowRes);
|
||||
if ($data && (int)$data['alert_type'] === 1) {
|
||||
$activeList[] = $data;
|
||||
}
|
||||
}
|
||||
echo json_encode($activeList, JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 非法请求兜底
|
||||
echo json_encode(["code" => 400, "msg" => "无效请求参数act"], JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
// 改为宿主机真实内网IP,不要写localhost/mysql容器名
|
||||
$host = "10.150.117.190";
|
||||
$user = "root";
|
||||
$pass = "hp93000";
|
||||
$dbname = "alert_mail_stat";
|
||||
$conn = mysqli_connect($host,$user,$pass,$dbname);
|
||||
mysqli_set_charset($conn,"utf8mb4");
|
||||
$act = $_GET['act'] ?? '';
|
||||
|
||||
// 1. 获取今日总统计:触发次数、恢复次数、故障实例数
|
||||
if($act == "day_total"){
|
||||
$today = date("Y-m-d");
|
||||
$sql = "SELECT
|
||||
SUM(IF(alert_type=1,1,0)) as fire_count,
|
||||
SUM(IF(alert_type=2,1,0)) as resolve_count,
|
||||
COUNT(DISTINCT instance) as instance_num
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today'";
|
||||
$res = mysqli_fetch_assoc(mysqli_query($conn,$sql));
|
||||
echo json_encode($res,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 2. 今日告警TOP排行(按发生次数)
|
||||
if($act == "top_alert"){
|
||||
$today = date("Y-m-d");
|
||||
$sql = "SELECT alert_name,COUNT(*) cnt FROM alert_log WHERE DATE(receive_time)='$today' GROUP BY alert_name ORDER BY cnt DESC LIMIT 10";
|
||||
$data = [];
|
||||
$q = mysqli_query($conn,$sql);
|
||||
while($r = mysqli_fetch_assoc($q)) $data[] = $r;
|
||||
echo json_encode($data,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
// 3. 今日明细列表(前端表格)
|
||||
if($act == "log_list"){
|
||||
$today = date("Y-m-d");
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,ends_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time)='$today' ORDER BY receive_time DESC";
|
||||
$data = [];
|
||||
$q = mysqli_query($conn,$sql);
|
||||
while($r = mysqli_fetch_assoc($q)) $data[] = $r;
|
||||
echo json_encode($data,JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
// 后台存放真实密码,前端完全看不到
|
||||
$realPwd = "hp93000";
|
||||
$inputPwd = $_POST['pwd'] ?? '';
|
||||
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
if($inputPwd === $realPwd){
|
||||
// 生成临时token返回前端
|
||||
echo json_encode(["code"=>200,"token"=>"alert_".md5(time().uniqid())]);
|
||||
}else{
|
||||
echo json_encode(["code"=>400,"msg"=>"密码错误"]);
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
// 统一数据库配置
|
||||
$dbHost = "10.150.117.190";
|
||||
$dbUser = "root";
|
||||
$dbPass = "hp93000";
|
||||
$dbName = "alert_mail_stat";
|
||||
|
||||
// 统一数据库连接函数
|
||||
function getDbConn() {
|
||||
global $dbHost, $dbUser, $dbPass, $dbName;
|
||||
$conn = mysqli_connect($dbHost, $dbUser, $dbPass, $dbName);
|
||||
mysqli_set_charset($conn, "utf8mb4");
|
||||
return $conn;
|
||||
}
|
||||
|
||||
$act = $_GET['act'] ?? '';
|
||||
|
||||
// CSV导出接口(独立流式下载,不输出JSON头)
|
||||
if ($act === "export_csv") {
|
||||
$conn = getDbConn();
|
||||
if (!$conn) {
|
||||
header("Content-Type:text/html;charset=utf-8");
|
||||
echo "数据库连接失败:" . mysqli_connect_error();
|
||||
exit;
|
||||
}
|
||||
$today = date("Y-m-d");
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today' ORDER BY receive_time DESC";
|
||||
$res = mysqli_query($conn, $sql);
|
||||
|
||||
ob_clean();
|
||||
header("Content-Type: text/csv; charset=utf-8");
|
||||
header("Content-Disposition: attachment; filename=告警报表_" . $today . ".csv");
|
||||
echo "\xEF\xBB\xBF";
|
||||
$header = ["告警状态", "告警名称", "实例", "级别", "故障开始时间", "接收时间"];
|
||||
echo implode(",", $header) . "\r\n";
|
||||
|
||||
while ($row = mysqli_fetch_assoc($res)) {
|
||||
$status = $row['alert_type'] == 1 ? "触发" : "恢复";
|
||||
$line = [
|
||||
$status,
|
||||
$row['alert_name'],
|
||||
$row['instance'],
|
||||
$row['severity'],
|
||||
$row['starts_at'],
|
||||
$row['receive_time']
|
||||
];
|
||||
foreach ($line as &$v) {
|
||||
$v = '"' . str_replace('"', '""', $v) . '"';
|
||||
}
|
||||
echo implode(",", $line) . "\r\n";
|
||||
}
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 所有JSON接口统一返回头
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
$conn = getDbConn();
|
||||
if (!$conn) {
|
||||
echo json_encode(["code" => 500, "msg" => "数据库连接失败:" . mysqli_connect_error()], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
$today = date("Y-m-d");
|
||||
|
||||
// 1.今日统计卡片
|
||||
if ($act === "day_total") {
|
||||
$sql = "SELECT
|
||||
SUM(IF(alert_type=1,1,0)) as fire_count,
|
||||
SUM(IF(alert_type=2,1,0)) as resolve_count,
|
||||
COUNT(DISTINCT instance) as instance_num
|
||||
FROM alert_log WHERE DATE(receive_time) = '$today'";
|
||||
$data = mysqli_fetch_assoc(mysqli_query($conn, $sql));
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2.今日告警TOP10图表
|
||||
if ($act === "top_alert") {
|
||||
$sql = "SELECT alert_name,COUNT(*) cnt FROM alert_log WHERE DATE(receive_time)='$today' GROUP BY alert_name ORDER BY cnt DESC LIMIT 10";
|
||||
$list = [];
|
||||
$q = mysqli_query($conn, $sql);
|
||||
while ($r = mysqli_fetch_assoc($q)) $list[] = $r;
|
||||
echo json_encode($list, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3.今日全量告警明细
|
||||
if ($act === "log_list") {
|
||||
$sql = "SELECT alert_type,alert_name,instance,severity,starts_at,ends_at,receive_time
|
||||
FROM alert_log WHERE DATE(receive_time)='$today' ORDER BY receive_time DESC";
|
||||
$list = [];
|
||||
$q = mysqli_query($conn, $sql);
|
||||
while ($r = mysqli_fetch_assoc($q)) $list[] = $r;
|
||||
echo json_encode($list, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 4.全局跨天未恢复告警(修复版,取每组最新一条判断状态)
|
||||
if ($act === "active_firing") {
|
||||
$maxSql = "SELECT alert_name,instance,MAX(receive_time) max_rt FROM alert_log GROUP BY alert_name,instance";
|
||||
$maxRes = mysqli_query($conn, $maxSql);
|
||||
$activeList = [];
|
||||
while ($row = mysqli_fetch_assoc($maxRes)) {
|
||||
$an = mysqli_real_escape_string($conn, $row['alert_name']);
|
||||
$ins = mysqli_real_escape_string($conn, $row['instance']);
|
||||
$rt = mysqli_real_escape_string($conn, $row['max_rt']);
|
||||
$sql = "SELECT alert_name,instance,severity,starts_at,receive_time,alert_type
|
||||
FROM alert_log WHERE alert_name='$an' AND instance='$ins' AND receive_time='$rt' LIMIT 1";
|
||||
$one = mysqli_fetch_assoc(mysqli_query($conn, $sql));
|
||||
if ($one && (int)$one['alert_type'] === 1) {
|
||||
$activeList[] = $one;
|
||||
}
|
||||
}
|
||||
echo json_encode($activeList, JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 非法act
|
||||
echo json_encode(["code" => 400, "msg" => "无效请求参数act"], JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
session_start();
|
||||
// 会话超时:30分钟无操作自动登出
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
session_destroy();
|
||||
header("Location: ../login.php");
|
||||
exit;
|
||||
}
|
||||
// 刷新登录时间
|
||||
$_SESSION['login_time'] = time();
|
||||
$userName = htmlspecialchars($_SESSION['username']);
|
||||
$userRole = $_SESSION['role'];
|
||||
$roleText = $userRole === 'admin' ? '管理员' : '只读用户';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>开发者调试面板 - Advantest 运维告警监控平台</title>
|
||||
<link rel="stylesheet" href="/static/css/font-awesome.min.css">
|
||||
<script src="/static/js/jquery.min.js"></script>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{
|
||||
background:#f9fafc;
|
||||
font-family:"Inter","PingFang SC","Microsoft YaHei";
|
||||
color:#1d2939;
|
||||
line-height:1.6;
|
||||
}
|
||||
/* 顶部导航 */
|
||||
.header{
|
||||
background:#ffffff;
|
||||
border-bottom:1px solid #eef2fb;
|
||||
color:#152c5b;
|
||||
padding:20px 40px;
|
||||
display:flex;
|
||||
justify-content:space-between;
|
||||
align-items:center;
|
||||
box-shadow:0 2px 12px rgba(21,44,91,0.05);
|
||||
}
|
||||
.header-left{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:14px;
|
||||
}
|
||||
.header-title{
|
||||
font-size:24px;
|
||||
font-weight:700;
|
||||
letter-spacing:1px;
|
||||
color:#152c5b;
|
||||
}
|
||||
.header-sub{
|
||||
font-size:14px;
|
||||
color:#64748b;
|
||||
}
|
||||
.header-right{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:18px;
|
||||
font-size:14px;
|
||||
flex-wrap:wrap;
|
||||
}
|
||||
.user-info{
|
||||
color:#475569;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:6px;
|
||||
}
|
||||
.btn-base{
|
||||
border:none;
|
||||
padding:9px 18px;
|
||||
border-radius:8px;
|
||||
cursor:pointer;
|
||||
font-size:14px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:6px;
|
||||
transition:all 0.2s ease;
|
||||
}
|
||||
.btn-outline{
|
||||
background:#ffffff;
|
||||
color:#152c5b;
|
||||
border:1px solid #cbd5e1;
|
||||
}
|
||||
.btn-outline:hover{
|
||||
background:#f7f8fc;
|
||||
border-color:#152c5b;
|
||||
}
|
||||
.btn-primary{
|
||||
background:#152c5b;
|
||||
color:#fff;
|
||||
}
|
||||
.btn-primary:hover{
|
||||
background:#0f1e42;
|
||||
}
|
||||
.btn-success{
|
||||
background:#10b981;
|
||||
color:#fff;
|
||||
}
|
||||
.btn-success:hover{
|
||||
background:#059669;
|
||||
}
|
||||
.btn-warning{
|
||||
background:#e6a23c;
|
||||
color:#fff;
|
||||
}
|
||||
.btn-danger{
|
||||
background:#f56c6c;
|
||||
color:#fff;
|
||||
}
|
||||
/* 主容器 */
|
||||
.container{
|
||||
padding:36px 40px;
|
||||
max-width:1600px;
|
||||
margin:0 auto;
|
||||
}
|
||||
h1{
|
||||
display:none;
|
||||
}
|
||||
/* 卡片样式 统一企业面板风格 */
|
||||
.card{
|
||||
background:#fff;
|
||||
border-radius:16px;
|
||||
padding:32px;
|
||||
margin-bottom:36px;
|
||||
box-shadow:0 3px 18px rgba(21,44,91,0.06);
|
||||
border:1px solid #eef2fb;
|
||||
}
|
||||
.card h3{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:10px;
|
||||
margin-bottom:26px;
|
||||
padding-bottom:16px;
|
||||
border-bottom:1px solid #eef2fb;
|
||||
font-size:19px;
|
||||
font-weight:600;
|
||||
color:#152c5b;
|
||||
}
|
||||
.card h3 i{
|
||||
color:#2563eb;
|
||||
font-size:20px;
|
||||
}
|
||||
.row{
|
||||
display:flex;
|
||||
gap:12px;
|
||||
align-items:center;
|
||||
margin-bottom:10px;
|
||||
flex-wrap:wrap;
|
||||
}
|
||||
label{
|
||||
font-size:14px;
|
||||
color:#475569;
|
||||
width:110px;
|
||||
}
|
||||
input{
|
||||
padding:8px 12px;
|
||||
border:1px solid #dcdfe6;
|
||||
border-radius:8px;
|
||||
width:280px;
|
||||
font-size:14px;
|
||||
}
|
||||
input[type="date"]{
|
||||
width:200px;
|
||||
}
|
||||
button{
|
||||
padding:9px 18px;
|
||||
border:none;
|
||||
border-radius:8px;
|
||||
cursor:pointer;
|
||||
font-size:14px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:6px;
|
||||
}
|
||||
button.normal{
|
||||
background:#409eff;
|
||||
color:#fff;
|
||||
}
|
||||
button.normal:hover{
|
||||
background:#2979e0;
|
||||
}
|
||||
.tip{
|
||||
font-size:13px;
|
||||
color:#94a3b8;
|
||||
margin-top:8px;
|
||||
padding-left:110px;
|
||||
}
|
||||
/* 返回结果区块 */
|
||||
#result{
|
||||
margin-top:20px;
|
||||
padding:20px;
|
||||
background:#1e1e1e;
|
||||
color:#fff;
|
||||
border-radius:12px;
|
||||
white-space:pre-wrap;
|
||||
min-height:260px;
|
||||
font-family:Consolas,monospace;
|
||||
font-size:13px;
|
||||
}
|
||||
/* 响应式适配 */
|
||||
@media (max-width:768px){
|
||||
.header{padding:16px 24px;flex-direction:column;gap:16px;align-items:flex-start}
|
||||
.container{padding:24px}
|
||||
.card{padding:24px}
|
||||
.row{flex-direction:column;align-items:flex-start}
|
||||
label{width:auto}
|
||||
input{width:100% !important;max-width:320px}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 顶部导航 -->
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<div>
|
||||
<div class="header-title"><i class="fa fa-code"></i> 开发者调试面板</div>
|
||||
<div class="header-sub">全部告警接口可视化调用 · 后端API调试工具</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span class="user-info"><i class="fa fa-user-circle"></i><?php echo $userName; ?>(<?php echo $roleText; ?>)</span>
|
||||
<button class="btn-base btn-outline" onclick="location.href='../admin.php'"><i class="fa fa-arrow-left"></i>返回系统后台</button>
|
||||
<button class="btn-base btn-outline" onclick="location.reload()"><i class="fa fa-refresh"></i>刷新页面</button>
|
||||
<button class="btn-base btn-primary" onclick="location.href='../dashboard.php'"><i class="fa fa-line-chart"></i>告警监控大屏</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- 1 临时清理单实例告警 clear_single_instance -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-eraser"></i>1. 临时隐藏单实例告警 clear_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="c_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="c_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="normal" onclick="req('clear_single_instance','c_rule','c_ins')"><i class="fa fa-play"></i>执行清理</button>
|
||||
</div>
|
||||
<div class="tip">仅插入恢复日志,下次故障自动重新展示;instance冒号无需手动编码,JS自动处理</div>
|
||||
</div>
|
||||
|
||||
<!-- 2 永久屏蔽单实例 offline_single_instance -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-ban"></i>2. 永久屏蔽单实例 offline_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="off_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="off_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="btn-danger" onclick="req('offline_single_instance','off_rule','off_ins')"><i class="fa fa-ban"></i>永久屏蔽</button>
|
||||
</div>
|
||||
<div class="tip">修改monitor_rule状态0,不执行恢复接口则永久不展示该实例告警</div>
|
||||
</div>
|
||||
|
||||
<!-- 3 恢复单实例 restore_single_instance -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-check-circle"></i>3. 恢复单实例 restore_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="res_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="res_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="btn-success" onclick="req('restore_single_instance','res_rule','res_ins')"><i class="fa fa-unlock"></i>恢复实例告警</button>
|
||||
</div>
|
||||
<div class="tip">解除单实例永久屏蔽状态,新故障会正常展示</div>
|
||||
</div>
|
||||
|
||||
<!-- 4 整条规则下线 clear_offline_rule -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-cog"></i>4. 整条规则全部下线 clear_offline_rule</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="cr_rule" placeholder="例 Host_Ping_Offline">
|
||||
<button class="btn-danger" onclick="reqSingle('clear_offline_rule','cr_rule')"><i class="fa fa-power-off"></i>下线整条规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5 恢复整条规则 restore_rule -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-cog"></i>5. 恢复整条规则 restore_rule</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="rr_rule" placeholder="例 Host_Ping_Offline">
|
||||
<button class="btn-success" onclick="reqSingle('restore_rule','rr_rule')"><i class="fa fa-refresh"></i>恢复整条规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6 删除单实例全部日志 delete_single_instance_log -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-trash"></i>6. 删除单实例所有告警日志 delete_single_instance_log</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="del_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="del_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="btn-warning" onclick="req('delete_single_instance_log','del_rule','del_ins')"><i class="fa fa-trash"></i>清空日志</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 7 同步Prometheus实时告警 sync_prom_alerts -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-refresh"></i>7. 手动同步Prometheus告警(清除脏恢复记录)</h3>
|
||||
<div class="row">
|
||||
<button class="normal" onclick="simpleReq('sync_prom_alerts')"><i class="fa fa-sync"></i>立即同步校准数据库</button>
|
||||
</div>
|
||||
<div class="tip">自动删除故障中残留的手动恢复记录,修复前端“故障存在但页面不显示”bug</div>
|
||||
</div>
|
||||
|
||||
<!-- 8 数据查询接口 -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-database"></i>8. 数据查询接口(获取面板原始数据)</h3>
|
||||
<div class="row">
|
||||
<button class="normal" onclick="simpleReq('day_total')"><i class="fa fa-bar-chart"></i>今日统计 day_total</button>
|
||||
<button class="normal" onclick="simpleReq('top_alert')"><i class="fa fa-pie-chart"></i>告警TOP10 top_alert</button>
|
||||
<button class="normal" onclick="simpleReq('log_list')"><i class="fa fa-list"></i>今日明细 log_list</button>
|
||||
<button class="normal" onclick="simpleReq('active_firing')"><i class="fa fa-exclamation-circle"></i>当前未恢复 active_firing</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 9 导出CSV报表(带日历选择) -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-download"></i>9. 导出告警CSV报表(支持历史日期)</h3>
|
||||
<div class="row">
|
||||
<label>选择日期:</label>
|
||||
<input type="date" id="csv_date" placeholder="留空导出今日">
|
||||
<button class="btn-success" onclick="exportCsv()"><i class="fa fa-download"></i>下载对应日期报表</button>
|
||||
</div>
|
||||
<div class="tip">不选择日期默认导出今日告警;选择历史日期可下载过往报表</div>
|
||||
</div>
|
||||
|
||||
<!-- 返回结果输出 -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-terminal"></i>接口返回结果(JSON)</h3>
|
||||
<div id="result">等待执行接口操作...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 接口路径 同目录 index.php
|
||||
const apiUrl = "./index.php";
|
||||
const resultDom = document.getElementById("result");
|
||||
|
||||
// 双参数接口 act + rule_name + instance
|
||||
function req(act, ruleId, insId){
|
||||
const rule = document.getElementById(ruleId).value.trim();
|
||||
const ins = document.getElementById(insId).value.trim();
|
||||
if(!rule || !ins){
|
||||
resultDom.innerText = "错误:rule_name 和 instance 不能为空";
|
||||
return;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("act", act);
|
||||
params.append("rule_name", rule);
|
||||
params.append("instance", ins);
|
||||
fetch(`${apiUrl}?${params.toString()}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 单参数接口 act + rule_name
|
||||
function reqSingle(act, ruleId){
|
||||
const rule = document.getElementById(ruleId).value.trim();
|
||||
if(!rule){
|
||||
resultDom.innerText = "错误:rule_name 不能为空";
|
||||
return;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("act", act);
|
||||
params.append("rule_name", rule);
|
||||
fetch(`${apiUrl}?${params.toString()}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 无参数接口
|
||||
function simpleReq(act){
|
||||
fetch(`${apiUrl}?act=${act}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 导出CSV 支持日历选择日期
|
||||
function exportCsv(){
|
||||
const dateVal = document.getElementById("csv_date").value.trim();
|
||||
let url = `${apiUrl}?act=export_csv`;
|
||||
if(dateVal){
|
||||
url += `&date=${encodeURIComponent(dateVal)}`;
|
||||
}
|
||||
window.open(url,"_blank");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
session_start();
|
||||
// 会话超时:30分钟无操作自动登出
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
session_destroy();
|
||||
header("Location: ../login.php");
|
||||
exit;
|
||||
}
|
||||
// 刷新登录时间
|
||||
$_SESSION['login_time'] = time();
|
||||
$userName = htmlspecialchars($_SESSION['username']);
|
||||
$userRole = $_SESSION['role'];
|
||||
$roleText = $userRole === 'admin' ? '管理员' : '只读用户';
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>开发者调试面板 - Advantest 运维告警监控平台</title>
|
||||
<link rel="stylesheet" href="/static/css/font-awesome.min.css">
|
||||
<script src="/static/js/jquery.min.js"></script>
|
||||
<style>
|
||||
*{box-sizing:border-box;margin:0;padding:0}
|
||||
body{
|
||||
background:#f9fafc;
|
||||
font-family:"Inter","PingFang SC","Microsoft YaHei";
|
||||
color:#1d2939;
|
||||
line-height:1.6;
|
||||
}
|
||||
/* 顶部导航 */
|
||||
.header{
|
||||
background:#ffffff;
|
||||
border-bottom:1px solid #eef2fb;
|
||||
color:#152c5b;
|
||||
padding:20px 40px;
|
||||
display:flex;
|
||||
justify-content:space-between;
|
||||
align-items:center;
|
||||
box-shadow:0 2px 12px rgba(21,44,91,0.05);
|
||||
}
|
||||
.header-left{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:14px;
|
||||
}
|
||||
.header-title{
|
||||
font-size:24px;
|
||||
font-weight:700;
|
||||
letter-spacing:1px;
|
||||
color:#152c5b;
|
||||
}
|
||||
.header-sub{
|
||||
font-size:14px;
|
||||
color:#64748b;
|
||||
}
|
||||
.header-right{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:18px;
|
||||
font-size:14px;
|
||||
flex-wrap:wrap;
|
||||
}
|
||||
.user-info{
|
||||
color:#475569;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:6px;
|
||||
}
|
||||
.btn-base{
|
||||
border:none;
|
||||
padding:9px 18px;
|
||||
border-radius:8px;
|
||||
cursor:pointer;
|
||||
font-size:14px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:6px;
|
||||
transition:all 0.2s ease;
|
||||
}
|
||||
.btn-outline{
|
||||
background:#ffffff;
|
||||
color:#152c5b;
|
||||
border:1px solid #cbd5e1;
|
||||
}
|
||||
.btn-outline:hover{
|
||||
background:#f7f8fc;
|
||||
border-color:#152c5b;
|
||||
}
|
||||
.btn-primary{
|
||||
background:#152c5b;
|
||||
color:#fff;
|
||||
}
|
||||
.btn-primary:hover{
|
||||
background:#0f1e42;
|
||||
}
|
||||
.btn-success{
|
||||
background:#10b981;
|
||||
color:#fff;
|
||||
}
|
||||
.btn-success:hover{
|
||||
background:#059669;
|
||||
}
|
||||
.btn-warning{
|
||||
background:#e6a23c;
|
||||
color:#fff;
|
||||
}
|
||||
.btn-danger{
|
||||
background:#f56c6c;
|
||||
color:#fff;
|
||||
}
|
||||
/* 主容器 */
|
||||
.container{
|
||||
padding:36px 40px;
|
||||
max-width:1600px;
|
||||
margin:0 auto;
|
||||
}
|
||||
h1{
|
||||
display:none;
|
||||
}
|
||||
/* 卡片样式 统一企业面板风格 */
|
||||
.card{
|
||||
background:#fff;
|
||||
border-radius:16px;
|
||||
padding:32px;
|
||||
margin-bottom:36px;
|
||||
box-shadow:0 3px 18px rgba(21,44,91,0.06);
|
||||
border:1px solid #eef2fb;
|
||||
}
|
||||
.card h3{
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:10px;
|
||||
margin-bottom:26px;
|
||||
padding-bottom:16px;
|
||||
border-bottom:1px solid #eef2fb;
|
||||
font-size:19px;
|
||||
font-weight:600;
|
||||
color:#152c5b;
|
||||
}
|
||||
.card h3 i{
|
||||
color:#2563eb;
|
||||
font-size:20px;
|
||||
}
|
||||
.row{
|
||||
display:flex;
|
||||
gap:12px;
|
||||
align-items:center;
|
||||
margin-bottom:10px;
|
||||
flex-wrap:wrap;
|
||||
}
|
||||
label{
|
||||
font-size:14px;
|
||||
color:#475569;
|
||||
width:110px;
|
||||
}
|
||||
input{
|
||||
padding:8px 12px;
|
||||
border:1px solid #dcdfe6;
|
||||
border-radius:8px;
|
||||
width:280px;
|
||||
font-size:14px;
|
||||
}
|
||||
input[type="date"]{
|
||||
width:200px;
|
||||
}
|
||||
button{
|
||||
padding:9px 18px;
|
||||
border:none;
|
||||
border-radius:8px;
|
||||
cursor:pointer;
|
||||
font-size:14px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:6px;
|
||||
}
|
||||
button.normal{
|
||||
background:#409eff;
|
||||
color:#fff;
|
||||
}
|
||||
button.normal:hover{
|
||||
background:#2979e0;
|
||||
}
|
||||
.tip{
|
||||
font-size:13px;
|
||||
color:#94a3b8;
|
||||
margin-top:8px;
|
||||
padding-left:110px;
|
||||
}
|
||||
/* 返回结果区块 */
|
||||
#result{
|
||||
margin-top:20px;
|
||||
padding:20px;
|
||||
background:#1e1e1e;
|
||||
color:#fff;
|
||||
border-radius:12px;
|
||||
white-space:pre-wrap;
|
||||
min-height:260px;
|
||||
font-family:Consolas,monospace;
|
||||
font-size:13px;
|
||||
}
|
||||
/* 响应式适配 */
|
||||
@media (max-width:768px){
|
||||
.header{padding:16px 24px;flex-direction:column;gap:16px;align-items:flex-start}
|
||||
.container{padding:24px}
|
||||
.card{padding:24px}
|
||||
.row{flex-direction:column;align-items:flex-start}
|
||||
label{width:auto}
|
||||
input{width:100% !important;max-width:320px}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 顶部导航 -->
|
||||
<div class="header">
|
||||
<div class="header-left">
|
||||
<div>
|
||||
<div class="header-title"><i class="fa fa-code"></i> 开发者调试面板</div>
|
||||
<div class="header-sub">全部告警接口可视化调用 · 后端API调试工具</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<span class="user-info"><i class="fa fa-user-circle"></i><?php echo $userName; ?>(<?php echo $roleText; ?>)</span>
|
||||
<button class="btn-base btn-outline" onclick="location.href='../admin.php'"><i class="fa fa-arrow-left"></i>返回系统后台</button>
|
||||
<button class="btn-base btn-outline" onclick="location.reload()"><i class="fa fa-refresh"></i>刷新页面</button>
|
||||
<button class="btn-base btn-primary" onclick="location.href='../dashboard.php'"><i class="fa fa-line-chart"></i>告警监控大屏</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
<!-- 1 临时清理单实例告警 clear_single_instance -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-eraser"></i>1. 临时隐藏单实例告警 clear_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="c_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="c_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="normal" onclick="req('clear_single_instance','c_rule','c_ins')"><i class="fa fa-play"></i>执行清理</button>
|
||||
</div>
|
||||
<div class="tip">仅插入恢复日志,下次故障自动重新展示;instance冒号无需手动编码,JS自动处理</div>
|
||||
</div>
|
||||
|
||||
<!-- 2 永久屏蔽单实例 offline_single_instance -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-ban"></i>2. 永久屏蔽单实例 offline_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="off_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="off_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="btn-danger" onclick="req('offline_single_instance','off_rule','off_ins')"><i class="fa fa-ban"></i>永久屏蔽</button>
|
||||
</div>
|
||||
<div class="tip">修改monitor_rule状态0,不执行恢复接口则永久不展示该实例告警</div>
|
||||
</div>
|
||||
|
||||
<!-- 3 恢复单实例 restore_single_instance -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-check-circle"></i>3. 恢复单实例 restore_single_instance</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="res_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="res_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="btn-success" onclick="req('restore_single_instance','res_rule','res_ins')"><i class="fa fa-unlock"></i>恢复实例告警</button>
|
||||
</div>
|
||||
<div class="tip">解除单实例永久屏蔽状态,新故障会正常展示</div>
|
||||
</div>
|
||||
|
||||
<!-- 4 整条规则下线 clear_offline_rule -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-cog"></i>4. 整条规则全部下线 clear_offline_rule</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="cr_rule" placeholder="例 Host_Ping_Offline">
|
||||
<button class="btn-danger" onclick="reqSingle('clear_offline_rule','cr_rule')"><i class="fa fa-power-off"></i>下线整条规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 5 恢复整条规则 restore_rule -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-cog"></i>5. 恢复整条规则 restore_rule</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="rr_rule" placeholder="例 Host_Ping_Offline">
|
||||
<button class="btn-success" onclick="reqSingle('restore_rule','rr_rule')"><i class="fa fa-refresh"></i>恢复整条规则</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 6 删除单实例全部日志 delete_single_instance_log -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-trash"></i>6. 删除单实例所有告警日志 delete_single_instance_log</h3>
|
||||
<div class="row">
|
||||
<label>rule_name:</label>
|
||||
<input id="del_rule" placeholder="例 Port_Down">
|
||||
<label>instance:</label>
|
||||
<input id="del_ins" placeholder="例 10.150.10.82:389">
|
||||
<button class="btn-warning" onclick="req('delete_single_instance_log','del_rule','del_ins')"><i class="fa fa-trash"></i>清空日志</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 7 同步Prometheus实时告警 sync_prom_alerts -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-refresh"></i>7. 手动同步Prometheus告警(清除脏恢复记录)</h3>
|
||||
<div class="row">
|
||||
<button class="normal" onclick="simpleReq('sync_prom_alerts')"><i class="fa fa-sync"></i>立即同步校准数据库</button>
|
||||
</div>
|
||||
<div class="tip">自动删除故障中残留的手动恢复记录,修复前端“故障存在但页面不显示”bug</div>
|
||||
</div>
|
||||
|
||||
<!-- 8 数据查询接口 -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-database"></i>8. 数据查询接口(获取面板原始数据)</h3>
|
||||
<div class="row">
|
||||
<button class="normal" onclick="simpleReq('day_total')"><i class="fa fa-bar-chart"></i>今日统计 day_total</button>
|
||||
<button class="normal" onclick="simpleReq('top_alert')"><i class="fa fa-pie-chart"></i>告警TOP10 top_alert</button>
|
||||
<button class="normal" onclick="simpleReq('log_list')"><i class="fa fa-list"></i>今日明细 log_list</button>
|
||||
<button class="normal" onclick="simpleReq('active_firing')"><i class="fa fa-exclamation-circle"></i>当前未恢复 active_firing</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 9 导出CSV报表(带日历选择) -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-download"></i>9. 导出告警CSV报表(支持历史日期)</h3>
|
||||
<div class="row">
|
||||
<label>选择日期:</label>
|
||||
<input type="date" id="csv_date" placeholder="留空导出今日">
|
||||
<button class="btn-success" onclick="exportCsv()"><i class="fa fa-download"></i>下载对应日期报表</button>
|
||||
</div>
|
||||
<div class="tip">不选择日期默认导出今日告警;选择历史日期可下载过往报表</div>
|
||||
</div>
|
||||
|
||||
<!-- 返回结果输出 -->
|
||||
<div class="card">
|
||||
<h3><i class="fa fa-terminal"></i>接口返回结果(JSON)</h3>
|
||||
<div id="result">等待执行接口操作...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 接口路径 同目录 index.php
|
||||
const apiUrl = "./index.php";
|
||||
const resultDom = document.getElementById("result");
|
||||
|
||||
// 双参数接口 act + rule_name + instance
|
||||
function req(act, ruleId, insId){
|
||||
const rule = document.getElementById(ruleId).value.trim();
|
||||
const ins = document.getElementById(insId).value.trim();
|
||||
if(!rule || !ins){
|
||||
resultDom.innerText = "错误:rule_name 和 instance 不能为空";
|
||||
return;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("act", act);
|
||||
params.append("rule_name", rule);
|
||||
params.append("instance", ins);
|
||||
fetch(`${apiUrl}?${params.toString()}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 单参数接口 act + rule_name
|
||||
function reqSingle(act, ruleId){
|
||||
const rule = document.getElementById(ruleId).value.trim();
|
||||
if(!rule){
|
||||
resultDom.innerText = "错误:rule_name 不能为空";
|
||||
return;
|
||||
}
|
||||
const params = new URLSearchParams();
|
||||
params.append("act", act);
|
||||
params.append("rule_name", rule);
|
||||
fetch(`${apiUrl}?${params.toString()}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 无参数接口
|
||||
function simpleReq(act){
|
||||
fetch(`${apiUrl}?act=${act}`)
|
||||
.then(r=>r.json())
|
||||
.then(json=>{
|
||||
resultDom.innerText = JSON.stringify(json,null,2);
|
||||
})
|
||||
.catch(e=>{
|
||||
resultDom.innerText = "请求异常:"+e;
|
||||
})
|
||||
}
|
||||
|
||||
// 导出CSV 支持日历选择日期
|
||||
function exportCsv(){
|
||||
const dateVal = document.getElementById("csv_date").value.trim();
|
||||
let url = `${apiUrl}?act=export_csv`;
|
||||
if(dateVal){
|
||||
url += `&date=${encodeURIComponent(dateVal)}`;
|
||||
}
|
||||
window.open(url,"_blank");
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
exit(json_encode(['code' => 0, 'msg' => 'ok']));
|
||||
}
|
||||
|
||||
// 数据库配置
|
||||
$dbHost = '127.0.0.1';
|
||||
$dbUser = 'root';
|
||||
$dbPwd = 'hp93000';
|
||||
$dbName = 'monitor';
|
||||
|
||||
session_start();
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
echo json_encode(['code' => 401, 'msg' => '登录失效,请重新登录']);
|
||||
exit;
|
||||
}
|
||||
$uid = $_SESSION['user_id'];
|
||||
|
||||
$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName);
|
||||
if (!$conn) {
|
||||
echo json_encode(['code' => 500, 'msg' => '数据库连接失败:' . mysqli_connect_error()]);
|
||||
exit;
|
||||
}
|
||||
mysqli_set_charset($conn, 'utf8mb4');
|
||||
|
||||
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
|
||||
|
||||
// 获取当前用户订阅配置
|
||||
if ($action === 'get_subscribe') {
|
||||
$sql = "SELECT * FROM alert_subscribe WHERE uid = ? LIMIT 1";
|
||||
$stmt = mysqli_prepare($conn, $sql);
|
||||
mysqli_stmt_bind_param($stmt, 's', $uid);
|
||||
mysqli_stmt_execute($stmt);
|
||||
$res = mysqli_stmt_get_result($stmt);
|
||||
$data = mysqli_fetch_assoc($res);
|
||||
if (empty($data)) {
|
||||
$data = [
|
||||
'severity' => '',
|
||||
'instance' => '',
|
||||
'alertname' => '',
|
||||
'dingtalk_webhook' => '',
|
||||
'wecom_webhook' => '',
|
||||
'mail_receiver' => '',
|
||||
'enable' => 0
|
||||
];
|
||||
}
|
||||
echo json_encode(['code' => 0, 'msg' => 'ok', 'data' => $data], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 保存/更新订阅配置
|
||||
if ($action === 'save_subscribe') {
|
||||
$post = json_decode(file_get_contents('php://input'), true);
|
||||
$severity = $post['severity'] ?? '';
|
||||
$instance = $post['instance'] ?? '';
|
||||
$alertname = $post['alertname'] ?? '';
|
||||
$dingtalk = $post['dingtalk_webhook'] ?? '';
|
||||
$wecom = $post['wecom_webhook'] ?? '';
|
||||
$mail = $post['mail_receiver'] ?? '';
|
||||
$enable = intval($post['enable'] ?? 0);
|
||||
|
||||
$check = mysqli_query($conn, "SELECT id FROM alert_subscribe WHERE uid = '$uid' LIMIT 1");
|
||||
if (mysqli_num_rows($check) > 0) {
|
||||
$sql = "UPDATE alert_subscribe SET severity=?,instance=?,alertname=?,dingtalk_webhook=?,wecom_webhook=?,mail_receiver=?,enable=? WHERE uid=?";
|
||||
$stmt = mysqli_prepare($conn, $sql);
|
||||
mysqli_stmt_bind_param($stmt, 'ssssssis', $severity, $instance, $alertname, $dingtalk, $wecom, $mail, $enable, $uid);
|
||||
} else {
|
||||
$sql = "INSERT INTO alert_subscribe(uid,severity,instance,alertname,dingtalk_webhook,wecom_webhook,mail_receiver,enable) VALUES (?,?,?,?,?,?,?,?)";
|
||||
$stmt = mysqli_prepare($conn, $sql);
|
||||
mysqli_stmt_bind_param($stmt, 'ssssssss', $uid, $severity, $instance, $alertname, $dingtalk, $wecom, $mail, $enable);
|
||||
}
|
||||
$ok = mysqli_stmt_execute($stmt);
|
||||
if ($ok) {
|
||||
echo json_encode(['code' => 0, 'msg' => '订阅配置保存成功'], JSON_UNESCAPED_UNICODE);
|
||||
} else {
|
||||
echo json_encode(['code' => 500, 'msg' => '保存失败:' . mysqli_error($conn)], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
echo json_encode(['code' => 400, 'msg' => '无效操作']);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
?>
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
exit(json_encode(['code' => 0, 'msg' => 'ok']));
|
||||
}
|
||||
|
||||
// 数据库配置
|
||||
$dbHost = '10.150.117.190';
|
||||
$dbUser = 'root';
|
||||
$dbPwd = 'hp93000';
|
||||
$dbName = 'monitor';
|
||||
|
||||
// 登录会话校验(和你原有登录体系一致)
|
||||
session_start();
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
echo json_encode(['code' => 401, 'msg' => '登录失效,请重新登录']);
|
||||
exit;
|
||||
}
|
||||
$uid = $_SESSION['user_id'];
|
||||
|
||||
// 连接数据库
|
||||
$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName);
|
||||
if (!$conn) {
|
||||
echo json_encode(['code' => 500, 'msg' => '数据库连接失败:' . mysqli_connect_error()]);
|
||||
exit;
|
||||
}
|
||||
mysqli_set_charset($conn, 'utf8mb4');
|
||||
|
||||
$action = isset($_REQUEST['action']) ? $_REQUEST['action'] : '';
|
||||
|
||||
// 1、获取当前用户订阅配置
|
||||
if ($action === 'get_subscribe') {
|
||||
$sql = "SELECT * FROM alert_subscribe WHERE uid = ? LIMIT 1";
|
||||
$stmt = mysqli_prepare($conn, $sql);
|
||||
mysqli_stmt_bind_param($stmt, 's', $uid);
|
||||
mysqli_stmt_execute($stmt);
|
||||
$res = mysqli_stmt_get_result($stmt);
|
||||
$data = mysqli_fetch_assoc($res);
|
||||
if (empty($data)) {
|
||||
$data = [
|
||||
'severity' => '',
|
||||
'instance' => '',
|
||||
'alertname' => '',
|
||||
'dingtalk_webhook' => '',
|
||||
'wecom_webhook' => '',
|
||||
'mail_receiver' => '',
|
||||
'enable' => 0
|
||||
];
|
||||
}
|
||||
echo json_encode(['code' => 0, 'msg' => 'ok', 'data' => $data], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2、保存/更新订阅配置
|
||||
if ($action === 'save_subscribe') {
|
||||
$post = json_decode(file_get_contents('php://input'), true);
|
||||
$severity = $post['severity'] ?? '';
|
||||
$instance = $post['instance'] ?? '';
|
||||
$alertname = $post['alertname'] ?? '';
|
||||
$dingtalk = $post['dingtalk_webhook'] ?? '';
|
||||
$wecom = $post['wecom_webhook'] ?? '';
|
||||
$mail = $post['mail_receiver'] ?? '';
|
||||
$enable = intval($post['enable'] ?? 0);
|
||||
|
||||
// 判断是否已有记录
|
||||
$check = mysqli_query($conn, "SELECT id FROM alert_subscribe WHERE uid = '$uid' LIMIT 1");
|
||||
if (mysqli_num_rows($check) > 0) {
|
||||
$sql = "UPDATE alert_subscribe SET severity=?,instance=?,alertname=?,dingtalk_webhook=?,wecom_webhook=?,mail_receiver=?,enable=? WHERE uid=?";
|
||||
$stmt = mysqli_prepare($conn, $sql);
|
||||
mysqli_stmt_bind_param($stmt, 'ssssssis', $severity, $instance, $alertname, $dingtalk, $wecom, $mail, $enable, $uid);
|
||||
} else {
|
||||
$sql = "INSERT INTO alert_subscribe(uid,severity,instance,alertname,dingtalk_webhook,wecom_webhook,mail_receiver,enable) VALUES (?,?,?,?,?,?,?,?)";
|
||||
$stmt = mysqli_prepare($conn, $sql);
|
||||
mysqli_stmt_bind_param($stmt, 'ssssssss', $uid, $severity, $instance, $alertname, $dingtalk, $wecom, $mail, $enable);
|
||||
}
|
||||
$ok = mysqli_stmt_execute($stmt);
|
||||
if ($ok) {
|
||||
echo json_encode(['code' => 0, 'msg' => '订阅配置保存成功'], JSON_UNESCAPED_UNICODE);
|
||||
} else {
|
||||
echo json_encode(['code' => 500, 'msg' => '保存失败:' . mysqli_error($conn)], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3、分页查询推送日志
|
||||
if ($action === 'list_push_log') {
|
||||
$page = isset($_REQUEST['page']) ? intval($_REQUEST['page']) : 1;
|
||||
$pageSize = isset($_REQUEST['page_size']) ? intval($_REQUEST['page_size']) : 10;
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
|
||||
// 总条数
|
||||
$cntSql = "SELECT COUNT(*) as total FROM alert_push_log WHERE uid = ?";
|
||||
$stmtCnt = mysqli_prepare($conn, $cntSql);
|
||||
mysqli_stmt_bind_param($stmtCnt, 's', $uid);
|
||||
mysqli_stmt_execute($stmtCnt);
|
||||
$cntRow = mysqli_fetch_assoc(mysqli_stmt_get_result($stmtCnt));
|
||||
$total = $cntRow['total'];
|
||||
|
||||
// 分页数据
|
||||
$sql = "SELECT * FROM alert_push_log WHERE uid = ? ORDER BY push_time DESC LIMIT ?,?";
|
||||
$stmt = mysqli_prepare($conn, $sql);
|
||||
mysqli_stmt_bind_param($stmt, 'sii', $uid, $offset, $pageSize);
|
||||
mysqli_stmt_execute($stmt);
|
||||
$res = mysqli_stmt_get_result($stmt);
|
||||
$list = [];
|
||||
while ($row = mysqli_fetch_assoc($res)) {
|
||||
$list[] = $row;
|
||||
}
|
||||
echo json_encode([
|
||||
'code' => 0,
|
||||
'msg' => 'ok',
|
||||
'data' => [
|
||||
'list' => $list,
|
||||
'page' => $page,
|
||||
'page_size' => $pageSize,
|
||||
'total' => $total,
|
||||
'pages' => ceil($total / $pageSize)
|
||||
]
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 无匹配操作
|
||||
echo json_encode(['code' => 400, 'msg' => '无效操作']);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"theme_color": "#152c5b",
|
||||
"card_bg": "#ffffff",
|
||||
"body_bg": "#f9fafc",
|
||||
"font_family": "Inter,PingFang SC,Microsoft YaHei",
|
||||
"sidebar_links": [
|
||||
{
|
||||
"icon": "fa-github",
|
||||
"title": "运维监控中心",
|
||||
"url": "http://10.150.117.190:3000/d/linux-server-alert-fix-startsat/linux-fu-wu-qi-gao-jing-zhong-xin?orgId=1&refresh=10s",
|
||||
"target": "_blank"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"user_id": "admin001",
|
||||
"username": "admin",
|
||||
"role": "admin",
|
||||
"password": "e10adc3949ba59abbe56e057f20f883e"
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
session_start();
|
||||
$expire = 1800;
|
||||
if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) {
|
||||
echo json_encode(["code"=>401,"msg"=>"未登录"]);
|
||||
exit;
|
||||
}
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
$userFile = "./user_info.json";
|
||||
if(!file_exists($userFile)){
|
||||
echo json_encode(["code"=>500,"msg"=>"账号配置文件缺失"]);
|
||||
exit;
|
||||
}
|
||||
$userData = json_decode(file_get_contents($userFile),true);
|
||||
$act = $_POST['act'] ?? '';
|
||||
|
||||
if($act == "change_pwd"){
|
||||
$old = $_POST['old_pwd'];
|
||||
$new = $_POST['new_pwd'];
|
||||
$confirm = $_POST['confirm_pwd'];
|
||||
if($userData['password'] != md5($old)){
|
||||
echo json_encode(["code"=>400,"msg"=>"原密码错误"]);
|
||||
exit;
|
||||
}
|
||||
if($new != $confirm){
|
||||
echo json_encode(["code"=>400,"msg"=>"两次新密码不一致"]);
|
||||
exit;
|
||||
}
|
||||
$userData['password'] = md5($new);
|
||||
file_put_contents($userFile,json_encode($userData,JSON_UNESCAPED_UNICODE));
|
||||
echo json_encode(["code"=>0,"msg"=>"密码修改成功,请重新登录"]);
|
||||
exit;
|
||||
}
|
||||
echo json_encode(["code"=>400,"msg"=>"非法操作"]);
|
||||
?>
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', 1);
|
||||
header("Content-Type:application/json;charset=utf-8");
|
||||
|
||||
// 改成宿主机IP
|
||||
$host = "10.150.117.190";
|
||||
$user = "root";
|
||||
$pass = "hp93000";
|
||||
$dbname = "alert_mail_stat";
|
||||
|
||||
$conn = mysqli_connect($host,$user,$pass,$dbname);
|
||||
if (!$conn) {
|
||||
echo json_encode([
|
||||
"code" => 500,
|
||||
"msg" => "数据库连接失败:" . mysqli_connect_error()
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
mysqli_set_charset($conn,"utf8mb4");
|
||||
|
||||
$rawBody = file_get_contents('php://input');
|
||||
$alertData = json_decode($rawBody, true);
|
||||
|
||||
if (!is_array($alertData) || empty($alertData['alerts'])) {
|
||||
echo json_encode([
|
||||
"code" => 400,
|
||||
"msg" => "未获取到有效告警数据"
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
mysqli_close($conn);
|
||||
exit;
|
||||
}
|
||||
|
||||
$insertCount = 0;
|
||||
foreach ($alertData['alerts'] as $alert) {
|
||||
$alertName = mysqli_real_escape_string($conn, $alert['labels']['alertname'] ?? "未知告警");
|
||||
$instance = mysqli_real_escape_string($conn, $alert['labels']['instance'] ?? "");
|
||||
$severity = mysqli_real_escape_string($conn, $alert['labels']['severity'] ?? "warning");
|
||||
$status = $alert['status'] ?? "firing";
|
||||
$startsAt = mysqli_real_escape_string($conn, $alert['startsAt'] ?? date("Y-m-d H:i:s"));
|
||||
$endsAt = isset($alert['endsAt']) ? "'" . mysqli_real_escape_string($conn, $alert['endsAt']) . "'" : "NULL";
|
||||
$fingerprint= mysqli_real_escape_string($conn, $alert['fingerprint'] ?? md5($alertName.$instance.$startsAt));
|
||||
$content = mysqli_real_escape_string($conn, json_encode($alert, JSON_UNESCAPED_UNICODE));
|
||||
$alertType = $status === "resolved" ? 2 : 1;
|
||||
|
||||
$checkSql = "SELECT id FROM alert_log WHERE mail_uid = '$fingerprint'";
|
||||
$checkRes = mysqli_query($conn, $checkSql);
|
||||
if (mysqli_num_rows($checkRes) > 0) continue;
|
||||
|
||||
$insertSql = "INSERT INTO alert_log
|
||||
(mail_uid, alert_type, alert_name, instance, severity, starts_at, ends_at, content, receive_time)
|
||||
VALUES ('$fingerprint', $alertType, '$alertName', '$instance', '$severity', '$startsAt', $endsAt, '$content', NOW())";
|
||||
|
||||
if (mysqli_query($conn, $insertSql)) $insertCount++;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
"code" => 200,
|
||||
"msg" => "告警处理完成",
|
||||
"insert_num" => $insertCount
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
|
||||
mysqli_close($conn);
|
||||
?>
|
||||
@@ -0,0 +1,427 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
echo json_encode(['code' => 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 = "<h3>工单 #$orderId</h3>
|
||||
<p>关联告警ID:$relateId</p>
|
||||
<p>标题:$title</p>
|
||||
<pre style='background:#f5f5f5;padding:10px'>$content</pre>";
|
||||
// 此处接入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;
|
||||
?>
|
||||
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
echo json_encode(['code' => 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;
|
||||
?>
|
||||
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
echo json_encode(['code' => 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;
|
||||
?>
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
echo json_encode(['code' => 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;
|
||||
?>
|
||||
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
echo json_encode(['code' => 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 = "<h3>工单内容</h3><pre>".$content."</pre>";
|
||||
// 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;
|
||||
?>
|
||||
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
echo json_encode(['code' => 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 = "<h3>工单 #$orderId</h3>
|
||||
<p>关联告警ID:$relateId</p>
|
||||
<p>标题:$title</p>
|
||||
<pre style='background:#f5f5f5;padding:10px'>$content</pre>";
|
||||
// 此处接入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;
|
||||
?>
|
||||
@@ -0,0 +1,378 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
echo json_encode(['code' => 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 = "<h3>工单 #$orderId</h3>
|
||||
<p>关联告警ID:$relateId</p>
|
||||
<p>标题:$title</p>
|
||||
<pre style='background:#f5f5f5;padding:10px'>$content</pre>";
|
||||
// 此处接入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;
|
||||
?>
|
||||
@@ -0,0 +1,407 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
echo json_encode(['code' => 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 = "<h3>工单 #$orderId</h3>
|
||||
<p>关联告警ID:$relateId</p>
|
||||
<p>标题:$title</p>
|
||||
<pre style='background:#f5f5f5;padding:10px'>$content</pre>";
|
||||
// 此处接入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;
|
||||
?>
|
||||
@@ -0,0 +1,427 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
header("Access-Control-Allow-Headers: Content-Type");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
echo json_encode(['code' => 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 = "<h3>工单 #$orderId</h3>
|
||||
<p>关联告警ID:$relateId</p>
|
||||
<p>标题:$title</p>
|
||||
<pre style='background:#f5f5f5;padding:10px'>$content</pre>";
|
||||
// 此处接入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;
|
||||
?>
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
header("Content-Type: application/json; charset=utf-8");
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
header("Access-Control-Allow-Methods: GET,POST,OPTIONS");
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
|
||||
exit(json_encode(['code' => 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;
|
||||
?>
|
||||
Reference in New Issue
Block a user