Files
2026-07-09 16:31:30 +08:00

441 lines
14 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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" style="margin-bottom:12px;gap:10px;align-items:center;flex-wrap:wrap;">
<label>单日导出:</label>
<input type="date" id="csv_date" placeholder="留空导出今日">
<button class="btn-success" onclick="exportCsvSingle()"><i class="fa fa-download"></i>下载单日报表</button>
</div>
<div class="tip" style="margin-bottom:16px;">不选择日期默认导出今日告警;选择历史日期下载单日报表</div>
<!-- 第二组:区间导出(新增) -->
<div class="row" style="gap:10px;align-items:center;flex-wrap:wrap;">
<label>区间导出:</label>
<input type="date" id="csv_start">
<span></span>
<input type="date" id="csv_end">
<button class="btn-primary" onclick="exportCsvRange()"><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;
})
}
// 单日导出
function exportCsvSingle(){
const dateVal = document.getElementById("csv_date").value.trim();
let url = `${apiUrl}?act=export_csv`;
if(dateVal){
url += `&date=${encodeURIComponent(dateVal)}`;
}
window.open(url,"_blank");
}
// 区间导出
function exportCsvRange(){
const startDate = document.getElementById("csv_start").value.trim();
const endDate = document.getElementById("csv_end").value.trim();
if(!startDate || !endDate){
alert("请同时选择开始日期和结束日期");
return;
}
if(startDate > endDate){
alert("开始日期不能晚于结束日期");
return;
}
let url = `${apiUrl}?act=export_csv`;
url += `&start=${encodeURIComponent(startDate)}&end=${encodeURIComponent(endDate)}`;
window.open(url,"_blank");
}
</script>
</body>
</html>