commit 0bb2eb4c3647f18ada49fc94286459f409a79a04 Author: admin <605696661@qq.com> Date: Thu Jul 9 16:31:30 2026 +0800 更新 diff --git a/admin.php b/admin.php new file mode 100644 index 0000000..d63b483 --- /dev/null +++ b/admin.php @@ -0,0 +1,619 @@ + $expire)) { + session_destroy(); + header("Location: login.php"); + exit; +} +$_SESSION['login_time'] = time(); +$userName = htmlspecialchars($_SESSION['username']); +$userRole = $_SESSION['role']; +$roleText = $userRole === 'admin' ? '管理员' : '只读用户'; + +// 数据库读取现有配置,页面打开自动回填 +$dbHost = "10.150.117.190"; +$dbUser = "root"; +$dbPwd = "hp93000"; +$dbName = "alert_mail_stat"; +$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName); +mysqli_set_charset($conn, "utf8mb4"); +$res = mysqli_query($conn, "SELECT k,v FROM sys_config"); +$config = []; +while ($row = mysqli_fetch_assoc($res)) { + $config[$row['k']] = $row['v']; +} +mysqli_close($conn); + +// 赋值表单默认值 +$pageTitle = $config['page_title'] ?? "告警监控系统"; +$themeColor = $config['theme_color'] ?? "#409eff"; +$pageSize = $config['page_size'] ?? "20"; +$footerText = $config['footer_text'] ?? ""; +$sidebarLinks = $config['sidebar_links'] ?? ""; +?> + + +
+ + +| 数据加载中... |
| 数据加载中... |
| 数据加载中... |
| Loading... |
|
+
+ Loading...
+ |
+
关联告警ID:$relateId
+标题:$title
+$content"; +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/api/work_order_api.php.1 b/api/work_order_api.php.1 new file mode 100644 index 0000000..62697b4 --- /dev/null +++ b/api/work_order_api.php.1 @@ -0,0 +1,117 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 数据库本地连接,避免远程拦截 +$dbHost = '10.150.117.190'; +$dbUser = 'root'; +$dbPwd = 'hp93000'; +$dbName = 'monitor'; +$dbPort = 3306; +$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName, $dbPort); +if (!$conn) { + echo json_encode([ + 'code' => 500, + 'msg' => '数据库连接失败:' . mysqli_connect_error() + ]); + exit; +} +mysqli_set_charset($conn, 'utf8mb4'); + +// 获取当前用户是否管理员 +$isAdmin = 0; +$roleSql = "SELECT r.is_admin FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($conn, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRow = mysqli_fetch_assoc(mysqli_stmt_get_result($stmtRole)); +if ($roleRow) $isAdmin = intval($roleRow['is_admin']); + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// 1、新建工单(修复第50行引用报错) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $status = 1; // 单独定义变量,不能直接写1传入bind_param + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,status,create_time,update_time) VALUES (?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($conn, $sql); + // 全部使用变量传参,禁止直接写数字/字符串 + mysqli_stmt_bind_param($stmt, 'ssssi', $title, $content, $loginUid, $assign, $status); + $ok = mysqli_stmt_execute($stmt); + if ($ok) { + echo json_encode(['code' => 0, 'msg' => '工单提交成功']); + } else { + echo json_encode(['code' => 500, 'msg' => '提交失败:' . mysqli_error($conn)]); + } + exit; +} + +// 2、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + // 普通用户权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($conn, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 3、管理员分发工单 +if ($action === "assign_order") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 无效操作 +echo json_encode(['code' => 400, 'msg' => '无效操作']); +mysqli_close($conn); +exit; +?> diff --git a/api/work_order_api.php.2.0 b/api/work_order_api.php.2.0 new file mode 100644 index 0000000..c80f790 --- /dev/null +++ b/api/work_order_api.php.2.0 @@ -0,0 +1,144 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 数据库本地连接 +$dbHost = '10.150.117.190'; +$dbUser = 'root'; +$dbPwd = 'hp93000'; +$dbName = 'monitor'; +$dbPort = 3306; +$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName, $dbPort); +if (!$conn) { + echo json_encode([ + 'code' => 500, + 'msg' => '数据库连接失败:' . mysqli_connect_error() + ]); + exit; +} +mysqli_set_charset($conn, 'utf8mb4'); + +// 获取当前用户是否管理员 +$isAdmin = 0; +$roleSql = "SELECT r.is_admin FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($conn, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRow = mysqli_fetch_assoc(mysqli_stmt_get_result($stmtRole)); +if ($roleRow) $isAdmin = intval($roleRow['is_admin']); + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// 1、新建工单 +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $status = 1; + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,status,create_time,update_time) VALUES (?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'ssssi', $title, $content, $loginUid, $assign, $status); + $ok = mysqli_stmt_execute($stmt); + if ($ok) { + echo json_encode(['code' => 0, 'msg' => '工单提交成功']); + } else { + echo json_encode(['code' => 500, 'msg' => '提交失败:' . mysqli_error($conn)]); + } + exit; +} + +// 2、编辑工单(新增,支持修改标题、内容备注、分配人) +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($conn, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'sssi', $title, $content, $assign, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单修改备注保存成功']); + exit; +} + +// 3、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($conn, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 4、管理员分发工单 +if ($action === "assign_order") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +echo json_encode(['code' => 400, 'msg' => '无效操作']); +mysqli_close($conn); +exit; +?> diff --git a/api/work_order_api.php.3.0 b/api/work_order_api.php.3.0 new file mode 100644 index 0000000..9230ec7 --- /dev/null +++ b/api/work_order_api.php.3.0 @@ -0,0 +1,146 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单数据库本地连接 +$dbHost = '10.150.117.190'; +$dbUser = 'root'; +$dbPwd = 'hp93000'; +$dbName = 'monitor'; +$dbPort = 3306; +$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName, $dbPort); +if (!$conn) { + echo json_encode([ + 'code' => 500, + 'msg' => '数据库连接失败:' . mysqli_connect_error() + ]); + exit; +} +mysqli_set_charset($conn, 'utf8mb4'); + +// 判断管理员 +$isAdmin = 0; +$roleSql = "SELECT r.is_admin FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($conn, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRow = mysqli_fetch_assoc(mysqli_stmt_get_result($stmtRole)); +if ($roleRow) $isAdmin = intval($roleRow['is_admin']); + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// 1、新建工单(新增relate_alert_id字段) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $status = 1; + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,status,create_time,update_time) VALUES (?,?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'sssssi', $title, $content, $loginUid, $assign, $relateAlertId, $status); + $ok = mysqli_stmt_execute($stmt); + if ($ok) { + echo json_encode(['code' => 0, 'msg' => '工单提交成功']); + } else { + echo json_encode(['code' => 500, 'msg' => '提交失败:' . mysqli_error($conn)]); + } + exit; +} + +// 2、编辑工单(不修改关联告警ID) +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($conn, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'sssi', $title, $content, $assign, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单修改备注保存成功']); + exit; +} + +// 3、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($conn, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 4、管理员分发工单 +if ($action === "assign_order") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 无匹配操作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($conn); +exit; +?> diff --git a/api/work_order_api.php.4.0 b/api/work_order_api.php.4.0 new file mode 100644 index 0000000..cc5d80f --- /dev/null +++ b/api/work_order_api.php.4.0 @@ -0,0 +1,247 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单业务库 monitor +$dbWorkHost = 'localhost'; +$dbWorkUser = 'root'; +$dbWorkPwd = 'hp93000'; +$dbWorkName = 'monitor'; +$connWork = mysqli_connect($dbWorkHost, $dbWorkUser, $dbWorkPwd, $dbWorkName); +if (!$connWork) { + echo json_encode(['code' => 500, 'msg' => '工单数据库连接失败:' . mysqli_connect_error()]); + exit; +} +mysqli_set_charset($connWork, 'utf8mb4'); + +// 配置库 alert_mail_stat +$dbConfHost = "10.150.117.190"; +$dbConfUser = "root"; +$dbConfPwd = "hp93000"; +$dbConfName = "alert_mail_stat"; +$connConf = mysqli_connect($dbConfHost, $dbConfUser, $dbConfPwd, $dbConfName); +mysqli_set_charset($connConf, "utf8mb4"); + +// 获取当前用户角色权限 +$isAdmin = 0; +$permList = ""; +$roleSql = "SELECT r.is_admin, r.perm_list FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($connWork, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRow = mysqli_fetch_assoc(mysqli_stmt_get_result($stmtRole)); +if ($roleRow) { + $isAdmin = intval($roleRow['is_admin']); + $permList = $roleRow['perm_list']; +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// 1、获取当前活跃告警(解决前端下拉空白) +if ($action === "get_firing_alert") { + $alertSql = "SELECT alert_id,alert_name,instance FROM alert_firing ORDER BY receive_time DESC LIMIT 100"; + $res = mysqli_query($connConf, $alertSql); + $list = []; + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// 2、新建工单(新增通知用户、通知渠道、关联告警) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + $status = 1; + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,notify_user,notify_channel,status,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssssi', $title, $content, $loginUid, $assign, $relateAlertId, $notifyUser, $notifyChannel, $status); + $ok = mysqli_stmt_execute($stmt); + if (!$ok) { + echo json_encode(['code' => 500, 'msg' => '创建失败:' . mysqli_error($connWork)]); + exit; + } + $orderId = mysqli_insert_id($connWork); + // 发送通知 + sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单提交成功,通知已下发']); + exit; +} + +// 3、编辑工单(更新通知、不修改关联告警) +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,notify_user=?,notify_channel=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssi', $title, $content, $assign, $notifyUser, $notifyChannel, $id); + mysqli_stmt_execute($stmt); + sendWorkOrderNotify($connConf, $connWork, $id, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单保存成功,通知已更新下发']); + exit; +} + +// 4、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 5、管理员分发工单 +if ($action === "assign_order") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 通知发送统一函数 +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 获取SMTP配置 + $smtpRow = mysqli_fetch_assoc(mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1")); + $smtpConfig = $smtpRow ?? []; + + // 获取工单详情 + $orderRow = mysqli_fetch_assoc(mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId")); + $title = $orderRow['title']; + $content = $orderRow['content']; + + // 遍历渠道发送 + foreach ($channelArr as $ch) { + switch ($ch) { + case "mail": + sendMailNotify($smtpConfig, $userArr, $title, $content); + break; + case "dingtalk": + // 此处填入钉钉机器人发送逻辑,按需补充 + break; + case "wecom": + // 此处填入企业微信机器人发送逻辑,按需补充 + break; + } + } +} + +// 邮件发送函数 +function sendMailNotify($smtp, $uidList, $title, $content) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_user']) || empty($smtp['smtp_pass'])) return; + // 查询用户邮箱 + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($GLOBALS['connWork'], "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", $emailList); + $subject = "【工单通知】" . $title; + $body = "
".$content.""; + // PHPMailer 发送逻辑,环境需安装phpmailer + /* + require_once 'PHPMailer.php'; + require_once 'SMTP.php'; + $mail = new PHPMailer\PHPMailer\PHPMailer(true); + try { + $mail->isSMTP(); + $mail->Host = $smtp['smtp_host']; + $mail->SMTPAuth = true; + $mail->Username = $smtp['smtp_user']; + $mail->Password = $smtp['smtp_pass']; + $mail->SMTPSecure = $smtp['smtp_ssl'] ? PHPMailer\PHPMailer\PHPMailer::ENCRYPTION_SMTPS : ''; + $mail->Port = $smtp['smtp_port']; + $mail->setFrom($smtp['send_from'], $smtp['send_from']); + $mail->addAddress($to); + $mail->isHTML(true); + $mail->Subject = $subject; + $mail->Body = $body; + $mail->send(); + } catch (Exception $e) {} + */ +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/api/work_order_api.php.5.0 b/api/work_order_api.php.5.0 new file mode 100644 index 0000000..c1f6af8 --- /dev/null +++ b/api/work_order_api.php.5.0 @@ -0,0 +1,267 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单业务库 monitor +$dbWorkHost = '10.150.117.190'; +$dbWorkUser = 'root'; +$dbWorkPwd = 'hp93000'; +$dbWorkName = 'monitor'; +$connWork = mysqli_connect($dbWorkHost, $dbWorkUser, $dbWorkPwd, $dbWorkName); +if (!$connWork) { + echo json_encode(['code' => 500, 'msg' => '工单数据库连接失败:' . mysqli_connect_error()]); + exit; +} +mysqli_set_charset($connWork, 'utf8mb4'); + +// 配置库 alert_mail_stat(告警、通知配置统一此处) +$dbConfHost = "10.150.117.190"; +$dbConfUser = "root"; +$dbConfPwd = "hp93000"; +$dbConfName = "alert_mail_stat"; +$connConf = mysqli_connect($dbConfHost, $dbConfUser, $dbConfPwd, $dbConfName); +if(!$connConf){ + echo json_encode(['code' => 500, 'msg' => '配置库连接失败']); + exit; +} +mysqli_set_charset($connConf, "utf8mb4"); + +// 获取当前用户角色权限 +$isAdmin = 0; +$permList = ""; +$roleSql = "SELECT r.is_admin, r.perm_list FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($connWork, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRes = mysqli_stmt_get_result($stmtRole); +$roleRow = $roleRes ? mysqli_fetch_assoc($roleRes) : []; +if ($roleRow) { + $isAdmin = intval($roleRow['is_admin']); + $permList = $roleRow['perm_list']; +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// 1、获取实时活跃告警(alert_mail_stat.alert_firing 数据源) +if ($action === "get_firing_alert") { + $alertSql = "SELECT alert_id,alert_name,instance FROM alert_firing WHERE status='firing' ORDER BY receive_time DESC LIMIT 100"; + $res = mysqli_query($connConf, $alertSql); + $list = []; + if($res instanceof mysqli_result){ + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// 2、新建工单(关联告警、多渠道通知) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + $status = 1; + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,notify_user,notify_channel,status,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssssi', $title, $content, $loginUid, $assign, $relateAlertId, $notifyUser, $notifyChannel, $status); + $ok = mysqli_stmt_execute($stmt); + if (!$ok) { + echo json_encode(['code' => 500, 'msg' => '创建失败:' . mysqli_error($connWork)]); + exit; + } + $orderId = mysqli_insert_id($connWork); + sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单提交成功,通知已下发']); + exit; +} + +// 3、编辑工单 +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 非管理员权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,notify_user=?,notify_channel=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssi', $title, $content, $assign, $notifyUser, $notifyChannel, $id); + mysqli_stmt_execute($stmt); + sendWorkOrderNotify($connConf, $connWork, $id, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单保存成功,通知已更新下发']); + exit; +} + +// 4、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 5、管理员分发工单(前端统一action=assign,废弃assign_order) +if ($action === "assign") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// ==================== 全局通知统一函数 ==================== +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 读取完整通知配置(邮箱+钉钉+企微) + $cfgRes = mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1"); + $notifyCfg = $cfgRes ? mysqli_fetch_assoc($cfgRes) : []; + if(empty($notifyCfg)) return; + + // 获取工单完整信息 + $orderRes = mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId"); + $order = $orderRes ? mysqli_fetch_assoc($orderRes) : []; + $title = $order['title']; + $content = $order['content']; + $relateId = $order['relate_alert_id'] ?: "无"; + + $msgText = "【运维工单通知】\n工单ID:$orderId\n关联告警:$relateId\n标题:$title\n详情:$content"; + + foreach ($channelArr as $ch) { + $ch = trim($ch); + switch ($ch) { + case "mail": + sendMailNotify($notifyCfg, $userArr, $title, $content, $relateId, $orderId); + break; + case "dingtalk": + sendDingNotify($notifyCfg, $msgText); + break; + case "wecom": + sendWecomNotify($notifyCfg, $msgText); + break; + } + } +} + +// 邮件发送 +function sendMailNotify($smtp, $uidList, $title, $content, $relateId, $orderId) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_account'])) return; + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($GLOBALS['connWork'], "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", array_unique($emailList)); + $subject = "【工单通知】#$orderId $title"; + $body = "
关联告警ID:$relateId
+标题:$title
+$content"; + // 此处接入PHPMailer发送逻辑 +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/api/work_order_api.php.6.0 b/api/work_order_api.php.6.0 new file mode 100644 index 0000000..7502531 --- /dev/null +++ b/api/work_order_api.php.6.0 @@ -0,0 +1,378 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单业务库 monitor +$dbWorkHost = '10.150.117.190'; +$dbWorkUser = 'root'; +$dbWorkPwd = 'hp93000'; +$dbWorkName = 'monitor'; +$connWork = mysqli_connect($dbWorkHost, $dbWorkUser, $dbWorkPwd, $dbWorkName); +if (!$connWork) { + echo json_encode(['code' => 500, 'msg' => '工单数据库连接失败:' . mysqli_connect_error()]); + exit; +} +mysqli_set_charset($connWork, 'utf8mb4'); + +// 配置库 alert_mail_stat(告警、通知配置统一此处) +$dbConfHost = "10.150.117.190"; +$dbConfUser = "root"; +$dbConfPwd = "hp93000"; +$dbConfName = "alert_mail_stat"; +$connConf = mysqli_connect($dbConfHost, $dbConfUser, $dbConfPwd, $dbConfName); +if(!$connConf){ + echo json_encode(['code' => 500, 'msg' => '配置库连接失败']); + exit; +} +mysqli_set_charset($connConf, "utf8mb4"); + +// 获取当前用户角色权限 +$isAdmin = 0; +$permList = ""; +$roleSql = "SELECT r.is_admin, r.perm_list FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($connWork, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRes = mysqli_stmt_get_result($stmtRole); +$roleRow = $roleRes ? mysqli_fetch_assoc($roleRes) : []; +if ($roleRow) { + $isAdmin = intval($roleRow['is_admin']); + $permList = $roleRow['perm_list']; +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// ===================== 新增1:根据告警ID查询关联工单 ===================== +if ($action === "get_workorder_by_alertid") { + $alertId = trim($_GET['alert_id'] ?? ''); + if (empty($alertId)) { + echo json_encode(['code' => 400, 'msg' => '缺少告警ID参数']); + exit; + } + $alertEsc = mysqli_real_escape_string($connWork, $alertId); + $sql = "SELECT id,title,status,assign_uid,create_time FROM work_order WHERE relate_alert_id = ? ORDER BY create_time DESC"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 's', $alertEsc); + mysqli_stmt_execute($stmt); + $res = mysqli_stmt_get_result($stmt); + $list = []; + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// ===================== 新增2:自动同步告警状态,批量更新工单为已完成 ===================== +if ($action === "auto_sync_workorder_status") { + // 1. 查询所有已恢复的告警(alert_type=2),去重获取告警ID + $resolveSql = "SELECT DISTINCT CONCAT(alert_name,'_',instance) as alert_id FROM alert_log WHERE alert_type=2"; + $resolveRes = mysqli_query($connConf, $resolveSql); + $resolveAlertIds = []; + while ($row = mysqli_fetch_assoc($resolveRes)) { + $resolveAlertIds[] = $row['alert_id']; + } + if (empty($resolveAlertIds)) { + echo json_encode(['code' => 0, 'msg' => '暂无已恢复告警,无需更新工单', 'update_count' => 0]); + exit; + } + + // 2. 拼接IN条件,查询未完成、关联该告警的工单 + $inStr = implode("','", array_map(function($v) use ($connWork) { + return mysqli_real_escape_string($connWork, $v); + }, $resolveAlertIds)); + // status=1待处理 / status=2处理中,统一改为3已完成 + $updateSql = "UPDATE work_order SET status=3,update_time=NOW() WHERE relate_alert_id IN ('$inStr') AND status IN (1,2)"; + mysqli_query($connWork, $updateSql); + $updateCnt = mysqli_affected_rows($connWork); + + echo json_encode([ + 'code' => 0, + 'msg' => '工单状态自动同步完成,已恢复告警绑定工单全部置为已完成', + 'update_count' => $updateCnt + ], JSON_UNESCAPED_UNICODE); + exit; +} + +// 1、获取实时活跃告警(alert_mail_stat.alert_firing 数据源) +if ($action === "get_firing_alert") { + $alertSql = "SELECT alert_id,alert_name,instance FROM alert_firing WHERE status='firing' ORDER BY receive_time DESC LIMIT 100"; + $res = mysqli_query($connConf, $alertSql); + $list = []; + if($res instanceof mysqli_result){ + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// 2、新建工单(关联告警、多渠道通知 + 自动判断告警是否已恢复) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + // 默认待处理1;若关联告警已恢复,直接置完成3 + $status = 1; + + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + + // 关联了告警,判断该告警是否存在恢复记录 + if (!empty($relateAlertId)) { + $alertEsc = mysqli_real_escape_string($connConf, $relateAlertId); + $checkResolveSql = "SELECT 1 FROM alert_log WHERE CONCAT(alert_name,'_',instance) = ? AND alert_type=2 LIMIT 1"; + $stmtCheck = mysqli_prepare($connConf, $checkResolveSql); + mysqli_stmt_bind_param($stmtCheck, 's', $alertEsc); + mysqli_stmt_execute($stmtCheck); + $checkRes = mysqli_stmt_get_result($stmtCheck); + if (mysqli_num_rows($checkRes) > 0) { + $status = 3; + } + } + + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,notify_user,notify_channel,status,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssssi', $title, $content, $loginUid, $assign, $relateAlertId, $notifyUser, $notifyChannel, $status); + $ok = mysqli_stmt_execute($stmt); + if (!$ok) { + echo json_encode(['code' => 500, 'msg' => '创建失败:' . mysqli_error($connWork)]); + exit; + } + $orderId = mysqli_insert_id($connWork); + sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单提交成功,通知已下发', 'init_status' => $status]); + exit; +} + +// 3、编辑工单 +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 非管理员权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,notify_user=?,notify_channel=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssi', $title, $content, $assign, $notifyUser, $notifyChannel, $id); + mysqli_stmt_execute($stmt); + sendWorkOrderNotify($connConf, $connWork, $id, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单保存成功,通知已更新下发']); + exit; +} + +// 4、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 5、管理员分发工单(前端统一action=assign,废弃assign_order) +if ($action === "assign") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 新增:同步Prometheus实时告警,自动清理恢复告警(完全独立,无需index.php) +if ($action === "sync_prom_alerts") { + header("Content-Type: application/json;charset=utf-8"); + $promUrl = "http://10.150.117.190:9090/api/v1/alerts"; + $resp = @file_get_contents($promUrl); + if($resp === false){ + echo json_encode(["code"=>500,"msg"=>"无法连接Prometheus接口"],JSON_UNESCAPED_UNICODE); + exit; + } + $promData = json_decode($resp,true); + if(!isset($promData['data']['alerts'])){ + echo json_encode(["code"=>500,"msg"=>"Prometheus返回数据异常"],JSON_UNESCAPED_UNICODE); + exit; + } + + // 1. 先将表内所有告警标记为已恢复 + mysqli_query($connConf,"UPDATE alert_firing SET status='resolved'"); + + // 2. 遍历当前Prometheus活跃告警,写入/更新 + $alerts = $promData['data']['alerts'] ?? []; + foreach($alerts as $item){ + $alertName = $item['labels']['alertname'] ?? "unknown"; + $instance = $item['labels']['instance'] ?? "unknown"; + $alertId = $alertName."_".$instance; + $sql = "INSERT INTO alert_firing(alert_id,alert_name,instance,status,receive_time) VALUES (?,?,?,'firing',NOW()) ON DUPLICATE KEY UPDATE status='firing',receive_time=NOW()"; + $stmt = mysqli_prepare($connConf,$sql); + mysqli_stmt_bind_param($stmt,"sss",$alertId,$alertName,$instance); + mysqli_stmt_execute($stmt); + } + $syncNum = count($alerts); + + // 同步完成后自动执行工单状态更新,无需额外调用 + $syncWorkUrl = apiOrder . "?action=auto_sync_workorder_status"; + @file_get_contents($syncWorkUrl); + + echo json_encode([ + "code"=>0, + "msg"=>"Prometheus告警同步完成,已自动同步工单状态", + "active_alert_total"=>$syncNum + ],JSON_UNESCAPED_UNICODE); + exit; +} + +// ==================== 全局通知统一函数 ==================== +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 读取完整通知配置(邮箱+钉钉+企微) + $cfgRes = mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1"); + $notifyCfg = $cfgRes ? mysqli_fetch_assoc($cfgRes) : []; + if(empty($notifyCfg)) return; + + // 获取工单完整信息 + $orderRes = mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId"); + $order = $orderRes ? mysqli_fetch_assoc($orderRes) : []; + $title = $order['title']; + $content = $order['content']; + $relateId = $order['relate_alert_id'] ?: "无"; + + $msgText = "【运维工单通知】\n工单ID:$orderId\n关联告警:$relateId\n标题:$title\n详情:$content"; + + foreach ($channelArr as $ch) { + $ch = trim($ch); + switch ($ch) { + case "mail": + sendMailNotify($notifyCfg, $userArr, $title, $content, $relateId, $orderId); + break; + case "dingtalk": + sendDingNotify($notifyCfg, $msgText); + break; + case "wecom": + sendWecomNotify($notifyCfg, $msgText); + break; + } + } +} + +// 邮件发送 +function sendMailNotify($smtp, $uidList, $title, $content, $relateId, $orderId) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_account'])) return; + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($GLOBALS['connWork'], "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", array_unique($emailList)); + $subject = "【工单通知】#$orderId $title"; + $body = "
关联告警ID:$relateId
+标题:$title
+$content"; + // 此处接入PHPMailer发送逻辑 +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/api/work_order_api.php.7.0 b/api/work_order_api.php.7.0 new file mode 100644 index 0000000..6a210d6 --- /dev/null +++ b/api/work_order_api.php.7.0 @@ -0,0 +1,407 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单业务库 monitor +$dbWorkHost = '10.150.117.190'; +$dbWorkUser = 'root'; +$dbWorkPwd = 'hp93000'; +$dbWorkName = 'monitor'; +$connWork = mysqli_connect($dbWorkHost, $dbWorkUser, $dbWorkPwd, $dbWorkName); +if (!$connWork) { + echo json_encode(['code' => 500, 'msg' => '工单数据库连接失败:' . mysqli_connect_error()]); + exit; +} +mysqli_set_charset($connWork, 'utf8mb4'); + +// 配置库 alert_mail_stat(告警、通知配置统一此处) +$dbConfHost = "10.150.117.190"; +$dbConfUser = "root"; +$dbConfPwd = "hp93000"; +$dbConfName = "alert_mail_stat"; +$connConf = mysqli_connect($dbConfHost, $dbConfUser, $dbConfPwd, $dbConfName); +if(!$connConf){ + echo json_encode(['code' => 500, 'msg' => '配置库连接失败']); + exit; +} +mysqli_set_charset($connConf, "utf8mb4"); + +// 获取当前用户角色权限 +$isAdmin = 0; +$permList = ""; +$roleSql = "SELECT r.is_admin, r.perm_list FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($connWork, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRes = mysqli_stmt_get_result($stmtRole); +$roleRow = $roleRes ? mysqli_fetch_assoc($roleRes) : []; +if ($roleRow) { + $isAdmin = intval($roleRow['is_admin']); + $permList = $roleRow['perm_list']; +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// ===================== 新增1:根据告警ID查询关联工单 ===================== +if ($action === "get_workorder_by_alertid") { + $alertId = trim($_GET['alert_id'] ?? ''); + if (empty($alertId)) { + echo json_encode(['code' => 400, 'msg' => '缺少告警ID参数']); + exit; + } + $alertEsc = mysqli_real_escape_string($connWork, $alertId); + $sql = "SELECT id,title,status,assign_uid,create_time FROM work_order WHERE relate_alert_id = ? ORDER BY create_time DESC"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 's', $alertEsc); + mysqli_stmt_execute($stmt); + $res = mysqli_stmt_get_result($stmt); + $list = []; + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// ===================== 新增2:自动同步告警状态,批量更新工单为已完成 ===================== +if ($action === "auto_sync_workorder_status") { + // 1. 查询所有已恢复的告警(alert_type=2),去重获取告警ID + $resolveSql = "SELECT DISTINCT CONCAT(alert_name,'_',instance) as alert_id FROM alert_log WHERE alert_type=2"; + $resolveRes = mysqli_query($connConf, $resolveSql); + $resolveAlertIds = []; + while ($row = mysqli_fetch_assoc($resolveRes)) { + $resolveAlertIds[] = $row['alert_id']; + } + if (empty($resolveAlertIds)) { + echo json_encode(['code' => 0, 'msg' => '暂无已恢复告警,无需更新工单', 'update_count' => 0]); + exit; + } + + // 2. 拼接IN条件,查询未完成、关联该告警的工单 + $inStr = implode("','", array_map(function($v) use ($connWork) { + return mysqli_real_escape_string($connWork, $v); + }, $resolveAlertIds)); + // status=1待处理 / status=2处理中,统一改为3已完成 + $updateSql = "UPDATE work_order SET status=3,update_time=NOW() WHERE relate_alert_id IN ('$inStr') AND status IN (1,2)"; + mysqli_query($connWork, $updateSql); + $updateCnt = mysqli_affected_rows($connWork); + + echo json_encode([ + 'code' => 0, + 'msg' => '工单状态自动同步完成,已恢复告警绑定工单全部置为已完成', + 'update_count' => $updateCnt + ], JSON_UNESCAPED_UNICODE); + exit; +} + +// 1、获取实时活跃告警(alert_mail_stat.alert_firing 数据源) +if ($action === "get_firing_alert") { + $alertSql = "SELECT alert_id,alert_name,instance FROM alert_firing WHERE status='firing' ORDER BY receive_time DESC LIMIT 100"; + $res = mysqli_query($connConf, $alertSql); + $list = []; + if($res instanceof mysqli_result){ + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// 2、新建工单(关联告警、多渠道通知 + 自动判断告警是否已恢复) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + // 默认待处理1;若关联告警已恢复,直接置完成3 + $status = 1; + + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + + // 关联了告警,判断该告警是否存在恢复记录 + if (!empty($relateAlertId)) { + $alertEsc = mysqli_real_escape_string($connConf, $relateAlertId); + $checkResolveSql = "SELECT 1 FROM alert_log WHERE CONCAT(alert_name,'_',instance) = ? AND alert_type=2 LIMIT 1"; + $stmtCheck = mysqli_prepare($connConf, $checkResolveSql); + mysqli_stmt_bind_param($stmtCheck, 's', $alertEsc); + mysqli_stmt_execute($stmtCheck); + $checkRes = mysqli_stmt_get_result($stmtCheck); + if (mysqli_num_rows($checkRes) > 0) { + $status = 3; + } + } + + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,notify_user,notify_channel,status,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssssi', $title, $content, $loginUid, $assign, $relateAlertId, $notifyUser, $notifyChannel, $status); + $ok = mysqli_stmt_execute($stmt); + if (!$ok) { + echo json_encode(['code' => 500, 'msg' => '创建失败:' . mysqli_error($connWork)]); + exit; + } + $orderId = mysqli_insert_id($connWork); + sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单提交成功,通知已下发', 'init_status' => $status]); + exit; +} + +// 3、编辑工单 +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 非管理员权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,notify_user=?,notify_channel=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'sssssi', $title, $content, $assign, $notifyUser, $notifyChannel, $id); + mysqli_stmt_execute($stmt); + sendWorkOrderNotify($connConf, $connWork, $id, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单保存成功,通知已更新下发']); + exit; +} + +// 4、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 5、管理员分发工单(前端统一action=assign,废弃assign_order) +if ($action === "assign") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 新增:同步Prometheus实时告警,自动清理恢复告警(完全独立,无需index.php) +if ($action === "sync_prom_alerts") { + header("Content-Type: application/json;charset=utf-8"); + $promUrl = "http://10.150.117.190:9090/api/v1/alerts"; + $resp = @file_get_contents($promUrl); + if($resp === false){ + echo json_encode(["code"=>500,"msg"=>"无法连接Prometheus接口"],JSON_UNESCAPED_UNICODE); + exit; + } + $promData = json_decode($resp,true); + if(!isset($promData['data']['alerts'])){ + echo json_encode(["code"=>500,"msg"=>"Prometheus返回数据异常"],JSON_UNESCAPED_UNICODE); + exit; + } + + // 1. 先将表内所有告警标记为已恢复 + mysqli_query($connConf,"UPDATE alert_firing SET status='resolved'"); + + // 2. 遍历当前Prometheus活跃告警,写入/更新 + $alerts = $promData['data']['alerts'] ?? []; + foreach($alerts as $item){ + $alertName = $item['labels']['alertname'] ?? "unknown"; + $instance = $item['labels']['instance'] ?? "unknown"; + $alertId = $alertName."_".$instance; + $sql = "INSERT INTO alert_firing(alert_id,alert_name,instance,status,receive_time) VALUES (?,?,?,'firing',NOW()) ON DUPLICATE KEY UPDATE status='firing',receive_time=NOW()"; + $stmt = mysqli_prepare($connConf,$sql); + mysqli_stmt_bind_param($stmt,"sss",$alertId,$alertName,$instance); + mysqli_stmt_execute($stmt); + } + $syncNum = count($alerts); + + // 同步完成后自动执行工单状态更新,无需额外调用 + $syncWorkUrl = apiOrder . "?action=auto_sync_workorder_status"; + @file_get_contents($syncWorkUrl); + + echo json_encode([ + "code"=>0, + "msg"=>"Prometheus告警同步完成,已自动同步工单状态", + "active_alert_total"=>$syncNum + ],JSON_UNESCAPED_UNICODE); + exit; +} + +// 定时同步:告警恢复自动完工单 +if($_POST['action'] === "sync_alert_auto_close"){ + // 1、拉取仪表盘实时未恢复告警 + $alertApi = "./index.php?act=active_firing"; + $raw = file_get_contents($alertApi); + $activeList = json_decode($raw, true) ?: []; + $activeKeys = []; + foreach($activeList as $item){ + $activeKeys[] = $item['alert_name'] . "|" . $item['instance']; + } + // 2、查询所有未完成、绑定告警的工单 + $sql = "SELECT id, relate_alert_id FROM work_order WHERE status IN(1,2) AND relate_alert_id <> ''"; + $res = mysqli_query($conn, $sql); + while($row = mysqli_fetch_assoc($res)){ + // 工单关联告警不在当前活跃列表=已恢复,自动设为完成 + if(!in_array($row['relate_alert_id'], $activeKeys)){ + $updateSql = "UPDATE work_order SET status=3, update_time=NOW() WHERE id = ".(int)$row['id']; + mysqli_query($conn, $updateSql); + } + } + echo json_encode(['code'=>0, 'msg'=>'同步完成']); + exit; +} + +// 读取通知配置 +$notifyRow = mysqli_fetch_assoc(mysqli_query($conn, "SELECT * FROM sys_workorder_notify LIMIT 1")); +if(!$notifyRow || $notifyRow['notify_open'] != 1) return; +// 组装钉钉、企业微信请求,curl发送webhook + +// ==================== 全局通知统一函数 ==================== +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 读取完整通知配置(邮箱+钉钉+企微) + $cfgRes = mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1"); + $notifyCfg = $cfgRes ? mysqli_fetch_assoc($cfgRes) : []; + if(empty($notifyCfg)) return; + + // 获取工单完整信息 + $orderRes = mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId"); + $order = $orderRes ? mysqli_fetch_assoc($orderRes) : []; + $title = $order['title']; + $content = $order['content']; + $relateId = $order['relate_alert_id'] ?: "无"; + + $msgText = "【运维工单通知】\n工单ID:$orderId\n关联告警:$relateId\n标题:$title\n详情:$content"; + + foreach ($channelArr as $ch) { + $ch = trim($ch); + switch ($ch) { + case "mail": + sendMailNotify($notifyCfg, $userArr, $title, $content, $relateId, $orderId); + break; + case "dingtalk": + sendDingNotify($notifyCfg, $msgText); + break; + case "wecom": + sendWecomNotify($notifyCfg, $msgText); + break; + } + } +} + +// 邮件发送 +function sendMailNotify($smtp, $uidList, $title, $content, $relateId, $orderId) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_account'])) return; + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($GLOBALS['connWork'], "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", array_unique($emailList)); + $subject = "【工单通知】#$orderId $title"; + $body = "
关联告警ID:$relateId
+标题:$title
+$content"; + // 此处接入PHPMailer发送逻辑 +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/api/work_order_api.php.8.0 b/api/work_order_api.php.8.0 new file mode 100644 index 0000000..63e3a97 --- /dev/null +++ b/api/work_order_api.php.8.0 @@ -0,0 +1,427 @@ + 0, 'msg' => 'ok']); + exit; +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 工单业务库 monitor +$dbWorkHost = '10.150.117.190'; +$dbWorkUser = 'root'; +$dbWorkPwd = 'hp93000'; +$dbWorkName = 'monitor'; +$connWork = mysqli_connect($dbWorkHost, $dbWorkUser, $dbWorkPwd, $dbWorkName); +if (!$connWork) { + echo json_encode(['code' => 500, 'msg' => '工单数据库连接失败:' . mysqli_connect_error()]); + exit; +} +mysqli_set_charset($connWork, 'utf8mb4'); + +// 配置库 alert_mail_stat(告警、通知配置统一此处) +$dbConfHost = "10.150.117.190"; +$dbConfUser = "root"; +$dbConfPwd = "hp93000"; +$dbConfName = "alert_mail_stat"; +$connConf = mysqli_connect($dbConfHost, $dbConfUser, $dbConfPwd, $dbConfName); +if(!$connConf){ + echo json_encode(['code' => 500, 'msg' => '配置库连接失败']); + exit; +} +mysqli_set_charset($connConf, "utf8mb4"); + +// 获取当前用户角色权限 +$isAdmin = 0; +$permList = ""; +$roleSql = "SELECT r.is_admin, r.perm_list FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid = ?"; +$stmtRole = mysqli_prepare($connWork, $roleSql); +mysqli_stmt_bind_param($stmtRole, 's', $loginUid); +mysqli_stmt_execute($stmtRole); +$roleRes = mysqli_stmt_get_result($stmtRole); +$roleRow = $roleRes ? mysqli_fetch_assoc($roleRes) : []; +if ($roleRow) { + $isAdmin = intval($roleRow['is_admin']); + $permList = $roleRow['perm_list']; +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// ===================== 新增1:根据告警ID查询关联工单 ===================== +if ($action === "get_workorder_by_alertid") { + $alertId = trim($_GET['alert_id'] ?? ''); + if (empty($alertId)) { + echo json_encode(['code' => 400, 'msg' => '缺少告警ID参数']); + exit; + } + $alertEsc = mysqli_real_escape_string($connWork, $alertId); + $sql = "SELECT id,title,status,assign_uid,create_time FROM work_order WHERE relate_alert_id = ? ORDER BY create_time DESC"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 's', $alertEsc); + mysqli_stmt_execute($stmt); + $res = mysqli_stmt_get_result($stmt); + $list = []; + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// ===================== 新增2:自动同步告警状态,批量更新工单为已完成 ===================== +if ($action === "auto_sync_workorder_status") { + // 1. 查询所有已恢复的告警(alert_type=2),去重获取告警ID + $resolveSql = "SELECT DISTINCT CONCAT(alert_name,'_',instance) as alert_id FROM alert_log WHERE alert_type=2"; + $resolveRes = mysqli_query($connConf, $resolveSql); + $resolveAlertIds = []; + while ($row = mysqli_fetch_assoc($resolveRes)) { + $resolveAlertIds[] = $row['alert_id']; + } + if (empty($resolveAlertIds)) { + echo json_encode(['code' => 0, 'msg' => '暂无已恢复告警,无需更新工单', 'update_count' => 0]); + exit; + } + + // 2. 拼接IN条件,查询未完成、关联该告警的工单 + $inStr = implode("','", array_map(function($v) use ($connWork) { + return mysqli_real_escape_string($connWork, $v); + }, $resolveAlertIds)); + // status=1待处理 / status=2处理中,统一改为3已完成 + $updateSql = "UPDATE work_order SET status=3,update_time=NOW() WHERE relate_alert_id IN ('$inStr') AND status IN (1,2)"; + mysqli_query($connWork, $updateSql); + $updateCnt = mysqli_affected_rows($connWork); + + echo json_encode([ + 'code' => 0, + 'msg' => '工单状态自动同步完成,已恢复告警绑定工单全部置为已完成', + 'update_count' => $updateCnt + ], JSON_UNESCAPED_UNICODE); + exit; +} + +// 1、获取实时活跃告警(alert_mail_stat.alert_firing 数据源) +if ($action === "get_firing_alert") { + $alertSql = "SELECT alert_id,alert_name,instance FROM alert_firing WHERE status='firing' ORDER BY receive_time DESC LIMIT 100"; + $res = mysqli_query($connConf, $alertSql); + $list = []; + if($res instanceof mysqli_result){ + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + } + echo json_encode(['code' => 0, 'list' => $list]); + exit; +} + +// 2、新建工单(关联告警、多渠道通知 + 自动判断告警是否已恢复) +if ($action === "create") { + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + // 默认待处理1;若关联告警已恢复,直接置完成3 + $status = 1; + + if (!$title || !$content) { + echo json_encode(['code' => 400, 'msg' => '标题和内容不能为空']); + exit; + } + + // 关联了告警,判断该告警是否存在恢复记录 + if (!empty($relateAlertId)) { + $alertEsc = mysqli_real_escape_string($connConf, $relateAlertId); + $checkResolveSql = "SELECT 1 FROM alert_log WHERE CONCAT(alert_name,'_',instance) = ? AND alert_type=2 LIMIT 1"; + $stmtCheck = mysqli_prepare($connConf, $checkResolveSql); + mysqli_stmt_bind_param($stmtCheck, 's', $alertEsc); + mysqli_stmt_execute($stmtCheck); + $checkRes = mysqli_stmt_get_result($stmtCheck); + if (mysqli_num_rows($checkRes) > 0) { + $status = 3; + } + } + + // 新增工单默认未读 is_read=0 + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,relate_alert_id,notify_user,notify_channel,status,is_read,create_time,update_time) VALUES (?,?,?,?,?,?,?,?,0,NOW(),NOW())"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ssssssss', $title, $content, $loginUid, $assign, $relateAlertId, $notifyUser, $notifyChannel, $status); + $ok = mysqli_stmt_execute($stmt); + if (!$ok) { + echo json_encode(['code' => 500, 'msg' => '创建失败:' . mysqli_error($connWork)]); + exit; + } + $orderId = mysqli_insert_id($connWork); + sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单提交成功,通知已下发', 'init_status' => $status]); + exit; +} + +// 3、编辑工单 +if ($action === "edit") { + $id = intval($post['id'] ?? 0); + $title = trim($post['title'] ?? ''); + $content = trim($post['content'] ?? ''); + $assign = trim($post['assign_uid'] ?? ''); + $relateAlertId = trim($post['relate_alert_id'] ?? ''); + $notifyUser = trim($post['notify_user'] ?? ''); + $notifyChannel = trim($post['notify_channel'] ?? ''); + if ($id <= 0 || empty($title) || empty($content)) { + echo json_encode(['code' => 400, 'msg' => '参数不能为空']); + exit; + } + // 非管理员权限校验 + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限编辑该工单']); + exit; + } + } + $sql = "UPDATE work_order SET title=?,content=?,assign_uid=?,relate_alert_id=?,notify_user=?,notify_channel=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ssssssi', $title, $content, $assign, $relateAlertId, $notifyUser, $notifyChannel, $id); + mysqli_stmt_execute($stmt); + sendWorkOrderNotify($connConf, $connWork, $id, $notifyUser, $notifyChannel); + echo json_encode(['code' => 0, 'msg' => '工单保存成功,通知已更新下发']); + exit; +} + +// 4、修改工单状态 +if ($action === "update_status") { + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if ($id <= 0) { + echo json_encode(['code' => 400, 'msg' => '工单ID错误']); + exit; + } + if ($isAdmin !== 1) { + $chkSql = "SELECT id FROM work_order WHERE id=? AND (create_uid=? OR assign_uid=?)"; + $stmtChk = mysqli_prepare($connWork, $chkSql); + mysqli_stmt_bind_param($stmtChk, 'iss', $id, $loginUid, $loginUid); + mysqli_stmt_execute($stmtChk); + $chkRes = mysqli_stmt_get_result($stmtChk); + if (mysqli_num_rows($chkRes) === 0) { + echo json_encode(['code' => 403, 'msg' => '无权限操作该工单']); + exit; + } + } + // 修改状态自动标记已读 + $sql = "UPDATE work_order SET status=?,is_read=1,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'ii', $status, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '状态更新成功']); + exit; +} + +// 5、管理员分发工单(前端统一action=assign,废弃assign_order) +if ($action === "assign") { + if ($isAdmin !== 1) { + echo json_encode(['code' => 403, 'msg' => '仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = trim($post['assign_uid'] ?? ''); + // 修复BUG:仅按工单唯一ID更新,不再使用标题模糊匹配,选中哪一行只修改该行工单 + $sql = "UPDATE work_order SET assign_uid=?,is_read=0,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, 'si', $assignUid, $id); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '工单分发成功']); + exit; +} + +// 一键标记当前用户全部待处理工单为已读 +if ($action === 'mark_all_read') { + $uid = $post['uid'] ?? ''; + $sql = "UPDATE work_order SET is_read=1 WHERE assign_uid=? AND status=1"; + $stmt = mysqli_prepare($connWork, $sql); + mysqli_stmt_bind_param($stmt, "s", $uid); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '已全部标记为已读']); + exit; +} + +// 语言切换 +if ($action === 'save_lang') { + $lang = $post['lang'] ?? 'zh'; + $sql = "UPDATE sys_config SET v=? WHERE k='lang'"; + $stmt = mysqli_prepare($connConf, $sql); + mysqli_stmt_bind_param($stmt, "s", $lang); + mysqli_stmt_execute($stmt); + echo json_encode(['code' => 0, 'msg' => '成功']); + exit; +} + +// 新增:同步Prometheus实时告警,自动清理恢复告警(完全独立,无需index.php) +if ($action === "sync_prom_alerts") { + $promUrl = "http://10.150.117.190:9090/api/v1/alerts"; + $resp = @file_get_contents($promUrl); + if($resp === false){ + echo json_encode(["code"=>500,"msg"=>"无法连接Prometheus接口"],JSON_UNESCAPED_UNICODE); + exit; + } + $promData = json_decode($resp,true); + if(!isset($promData['data']['alerts'])){ + echo json_encode(["code"=>500,"msg"=>"Prometheus返回数据异常"],JSON_UNESCAPED_UNICODE); + exit; + } + + // 1. 先将表内所有告警标记为已恢复 + mysqli_query($connConf,"UPDATE alert_firing SET status='resolved'"); + + // 2. 遍历当前Prometheus活跃告警,写入/更新 + $alerts = $promData['data']['alerts'] ?? []; + foreach($alerts as $item){ + $alertName = $item['labels']['alertname'] ?? "unknown"; + $instance = $item['labels']['instance'] ?? "unknown"; + $alertId = $alertName."_".$instance; + $sql = "INSERT INTO alert_firing(alert_id,alert_name,instance,status,receive_time) VALUES (?,?,?,'firing',NOW()) ON DUPLICATE KEY UPDATE status='firing',receive_time=NOW()"; + $stmt = mysqli_prepare($connConf,$sql); + mysqli_stmt_bind_param($stmt,"sss",$alertId,$alertName,$instance); + mysqli_stmt_execute($stmt); + } + $syncNum = count($alerts); + + // 同步完成后自动执行工单状态更新 + $syncWorkUrl = $_SERVER['REQUEST_SCHEME'] . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . "?action=auto_sync_workorder_status"; + @file_get_contents($syncWorkUrl); + + echo json_encode([ + "code"=>0, + "msg"=>"Prometheus告警同步完成,已自动同步工单状态", + "active_alert_total"=>$syncNum + ],JSON_UNESCAPED_UNICODE); + exit; +} + +// 定时同步:告警恢复自动完工单 +if($action === "sync_alert_auto_close"){ + // 1、拉取仪表盘实时未恢复告警 + $alertApi = "./index.php?act=active_firing"; + $raw = @file_get_contents($alertApi); + $activeList = json_decode($raw, true) ?: []; + $activeKeys = []; + foreach($activeList as $item){ + $activeKeys[] = $item['alert_name'] . "|" . $item['instance']; + } + // 2、查询所有未完成、绑定告警的工单 + $sql = "SELECT id, relate_alert_id FROM work_order WHERE status IN(1,2) AND relate_alert_id <> ''"; + $res = mysqli_query($connWork, $sql); + while($row = mysqli_fetch_assoc($res)){ + // 工单关联告警不在当前活跃列表=已恢复,自动设为完成 + if(!in_array($row['relate_alert_id'], $activeKeys)){ + $updateSql = "UPDATE work_order SET status=3, update_time=NOW() WHERE id = ".(int)$row['id']; + mysqli_query($connWork, $updateSql); + } + } + echo json_encode(['code'=>0, 'msg'=>'同步完成']); + exit; +} + +// ==================== 全局通知统一函数 ==================== +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 读取完整通知配置(邮箱+钉钉+企微) + $cfgRes = mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1"); + $notifyCfg = $cfgRes ? mysqli_fetch_assoc($cfgRes) : []; + if(empty($notifyCfg)) return; + + // 获取工单完整信息 + $orderRes = mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId"); + $order = $orderRes ? mysqli_fetch_assoc($orderRes) : []; + $title = $order['title']; + $content = $order['content']; + $relateId = $order['relate_alert_id'] ?: "无"; + + $msgText = "【运维工单通知】\n工单ID:$orderId\n关联告警:$relateId\n标题:$title\n详情:$content"; + + foreach ($channelArr as $ch) { + $ch = trim($ch); + switch ($ch) { + case "mail": + sendMailNotify($notifyCfg, $connWork, $userArr, $title, $content, $relateId, $orderId); + break; + case "dingtalk": + sendDingNotify($notifyCfg, $msgText); + break; + case "wecom": + sendWecomNotify($notifyCfg, $msgText); + break; + } + } +} + +// 邮件发送 +function sendMailNotify($smtp, $connWork, $uidList, $title, $content, $relateId, $orderId) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_account'])) return; + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($connWork, "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", array_unique($emailList)); + $subject = "【工单通知】#$orderId $title"; + $body = "
关联告警ID:$relateId
+标题:$title
+$content"; + // 此处接入PHPMailer发送逻辑 +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/api/work_order_api.php.9.0 b/api/work_order_api.php.9.0 new file mode 100644 index 0000000..e2dfc88 --- /dev/null +++ b/api/work_order_api.php.9.0 @@ -0,0 +1,515 @@ + 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 = ""; +$roleId = 0; +$roleSql = "SELECT r.is_admin, r.perm_list, u.role_id 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']; + $roleId = intval($roleRow['role_id'] ?? 0); +} + +// 读取当前角色页面权限列表(用于分发工单权限判断) +$allowPages = []; +if ($roleId > 0) { + $permSql = "SELECT page_key FROM sys_role_permission WHERE role_id = ?"; + $stmtPerm = mysqli_prepare($connWork, $permSql); + mysqli_stmt_bind_param($stmtPerm, 'i', $roleId); + mysqli_stmt_execute($stmtPerm); + $pRes = mysqli_stmt_get_result($stmtPerm); + while ($p = mysqli_fetch_assoc($pRes)) { + $allowPages[] = $p['page_key']; + } +} + +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"), true) ?: []; + +// ===================== 工单列表查询(新增,解决数据可见权限隔离) ===================== +if ($action === "get_list") { + $page = intval($_GET['page'] ?? 1); + $size = intval($_GET['size'] ?? 20); + $offset = ($page - 1) * $size; + $status = trim($_GET['status'] ?? ''); + $keyword = trim($_GET['keyword'] ?? ''); + + $where = []; + $param = []; + $paramType = ''; + + // 普通用户:仅能查看自己创建 或 分配给自己的工单;管理员无限制查看全部 + if ($isAdmin !== 1) { + $where[] = "(create_uid = ? OR assign_uid = ?)"; + $param[] = $loginUid; + $param[] = $loginUid; + $paramType .= 'ss'; + } + + if ($status !== '') { + $where[] = "status = ?"; + $param[] = $status; + $paramType .= 'i'; + } + if ($keyword !== '') { + $where[] = "(title LIKE ? OR content LIKE ?)"; + $param[] = "%$keyword%"; + $param[] = "%$keyword%"; + $paramType .= 'ss'; + } + + $whereSql = $where ? "WHERE " . implode(" AND ", $where) : ""; + + // 分页列表 + $listSql = "SELECT * FROM work_order $whereSql ORDER BY create_time DESC LIMIT ?,?"; + $param[] = $offset; + $param[] = $size; + $paramType .= 'ii'; + + $stmtList = mysqli_prepare($connWork, $listSql); + mysqli_stmt_bind_param($stmtList, $paramType, ...$param); + mysqli_stmt_execute($stmtList); + $res = mysqli_stmt_get_result($stmtList); + $list = []; + while ($row = mysqli_fetch_assoc($res)) { + $list[] = $row; + } + + // 总数统计 + $countSql = "SELECT COUNT(id) total FROM work_order $whereSql"; + $stmtCount = mysqli_prepare($connWork, $countSql); + array_pop($param); + array_pop($param); + $paramTypeCount = rtrim($paramType, 'ii'); + if ($paramTypeCount) { + mysqli_stmt_bind_param($stmtCount, $paramTypeCount, ...$param); + } + mysqli_stmt_execute($stmtCount); + $countRow = mysqli_fetch_assoc(mysqli_stmt_get_result($stmtCount)); + $total = intval($countRow['total']); + + echo json_encode([ + 'code' => 0, + 'list' => $list, + 'total' => $total, + 'page' => $page, + 'size' => $size + ], JSON_UNESCAPED_UNICODE); + exit; +} + +// ===================== 根据告警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; +} + +// ===================== 自动同步告警状态,批量更新工单为已完成 ===================== +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、管理员/拥有system_manage权限角色 分发工单 +if ($action === "assign") { + // 修复:超级管理员 或 拥有system_manage页面权限均可分发工单 + if ($isAdmin !== 1 && !in_array("system_manage", $allowPages)) { + 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实时告警,自动清理恢复告警 +if ($action === "sync_prom_alerts") { + $promUrl = "http://10.150.117.190:9090/api/v1/alerts"; + $resp = @file_get_contents($promUrl); + if($resp === false){ + echo json_encode(["code"=>500,"msg"=>"无法连接Prometheus接口"],JSON_UNESCAPED_UNICODE); + exit; + } + $promData = json_decode($resp,true); + if(!isset($promData['data']['alerts'])){ + echo json_encode(["code"=>500,"msg"=>"Prometheus返回数据异常"],JSON_UNESCAPED_UNICODE); + exit; + } + + // 1. 先将表内所有告警标记为已恢复 + mysqli_query($connConf,"UPDATE alert_firing SET status='resolved'"); + + // 2. 遍历当前Prometheus活跃告警,写入/更新 + $alerts = $promData['data']['alerts'] ?? []; + foreach($alerts as $item){ + $alertName = $item['labels']['alertname'] ?? "unknown"; + $instance = $item['labels']['instance'] ?? "unknown"; + $alertId = $alertName."_".$instance; + $sql = "INSERT INTO alert_firing(alert_id,alert_name,instance,status,receive_time) VALUES (?,?,?,'firing',NOW()) ON DUPLICATE KEY UPDATE status='firing',receive_time=NOW()"; + $stmt = mysqli_prepare($connConf,$sql); + mysqli_stmt_bind_param($stmt,"sss",$alertId,$alertName,$instance); + mysqli_stmt_execute($stmt); + } + $syncNum = count($alerts); + + // 同步完成后自动执行工单状态更新 + $syncWorkUrl = $_SERVER['REQUEST_SCHEME'] . "://" . $_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'] . "?action=auto_sync_workorder_status"; + @file_get_contents($syncWorkUrl); + + echo json_encode([ + "code"=>0, + "msg"=>"Prometheus告警同步完成,已自动同步工单状态", + "active_alert_total"=>$syncNum + ],JSON_UNESCAPED_UNICODE); + exit; +} + +// 定时同步:告警恢复自动完工单 +if($action === "sync_alert_auto_close"){ + // 1、拉取仪表盘实时未恢复告警 + $alertApi = "./index.php?act=active_firing"; + $raw = @file_get_contents($alertApi); + $activeList = json_decode($raw, true) ?: []; + $activeKeys = []; + foreach($activeList as $item){ + $activeKeys[] = $item['alert_name'] . "|" . $item['instance']; + } + // 2、查询所有未完成、绑定告警的工单 + $sql = "SELECT id, relate_alert_id FROM work_order WHERE status IN(1,2) AND relate_alert_id <> ''"; + $res = mysqli_query($connWork, $sql); + while($row = mysqli_fetch_assoc($res)){ + // 工单关联告警不在当前活跃列表=已恢复,自动设为完成 + if(!in_array($row['relate_alert_id'], $activeKeys)){ + $updateSql = "UPDATE work_order SET status=3, update_time=NOW() WHERE id = ".(int)$row['id']; + mysqli_query($connWork, $updateSql); + } + } + echo json_encode(['code'=>0, 'msg'=>'同步完成']); + exit; +} + +// ==================== 全局通知统一函数 ==================== +function sendWorkOrderNotify($connConf, $connWork, $orderId, $notifyUserStr, $channelStr) +{ + if (empty($notifyUserStr) || empty($channelStr)) return; + $userArr = explode(",", $notifyUserStr); + $channelArr = explode(",", $channelStr); + + // 读取完整通知配置(邮箱+钉钉+企微) + $cfgRes = mysqli_query($connConf, "SELECT * FROM sys_smtp_config WHERE id=1"); + $notifyCfg = $cfgRes ? mysqli_fetch_assoc($cfgRes) : []; + if(empty($notifyCfg)) return; + + // 获取工单完整信息 + $orderRes = mysqli_query($connWork, "SELECT * FROM work_order WHERE id=$orderId"); + $order = $orderRes ? mysqli_fetch_assoc($orderRes) : []; + $title = $order['title']; + $content = $order['content']; + $relateId = $order['relate_alert_id'] ?: "无"; + + $msgText = "【运维工单通知】\n工单ID:$orderId\n关联告警:$relateId\n标题:$title\n详情:$content"; + + foreach ($channelArr as $ch) { + $ch = trim($ch); + switch ($ch) { + case "mail": + sendMailNotify($notifyCfg, $connWork, $userArr, $title, $content, $relateId, $orderId); + break; + case "dingtalk": + sendDingNotify($notifyCfg, $msgText); + break; + case "wecom": + sendWecomNotify($notifyCfg, $msgText); + break; + } + } +} + +// 邮件发送 +function sendMailNotify($smtp, $connWork, $uidList, $title, $content, $relateId, $orderId) +{ + if (empty($smtp['smtp_host']) || empty($smtp['smtp_account'])) return; + $emailList = []; + foreach ($uidList as $uid) { + $uid = trim($uid); + if (empty($uid)) continue; + $res = mysqli_query($connWork, "SELECT email FROM sys_user WHERE uid='$uid'"); + $u = mysqli_fetch_assoc($res); + if (!empty($u['email'])) $emailList[] = $u['email']; + } + if (empty($emailList)) return; + $to = implode(",", array_unique($emailList)); + $subject = "【工单通知】#$orderId $title"; + $body = "
关联告警ID:$relateId
+标题:$title
+$content"; + // 此处接入PHPMailer发送逻辑 +} + +// 钉钉机器人推送 +function sendDingNotify($cfg, $text) +{ + $webhook = $cfg['ding_webhook'] ?? ''; + $secret = $cfg['ding_secret'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 企业微信机器人推送 +function sendWecomNotify($cfg, $text) +{ + $webhook = $cfg['wecom_webhook'] ?? ''; + if(empty($webhook)) return; + $data = json_encode([ + "msgtype" => "text", + "text" => ["content" => $text] + ]); + $opts = ["http" => ["method" => "POST", "header" => "Content-Type:application/json", "content" => $data]]; + file_get_contents($webhook, false, stream_context_create($opts)); +} + +// 无匹配动作 +echo json_encode(['code' => 400, 'msg' => '无效请求动作']); +mysqli_close($connWork); +mysqli_close($connConf); +exit; +?> diff --git a/api/work_order_api.php.bak b/api/work_order_api.php.bak new file mode 100644 index 0000000..ce696eb --- /dev/null +++ b/api/work_order_api.php.bak @@ -0,0 +1,104 @@ + 0])); +} +session_start(); +$expire = 1800; +if (empty($_SESSION['user_id']) || (time() - $_SESSION['login_time'] > $expire)) { + echo json_encode(['code' => 401, 'msg' => '登录失效']); + exit; +} +$loginUid = $_SESSION['user_id']; + +// 数据库配置,优先本地localhost +$dbHost = '10.150.117.190'; +$dbUser = 'root'; +$dbPwd = 'hp93000'; +$dbName = 'monitor'; +$dbPort = 3306; + +// 关键:连接后立刻判断是否失败 +$conn = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName, $dbPort); +if (!$conn) { + echo json_encode([ + 'code' => 500, + 'msg' => '数据库连接失败:' . mysqli_connect_error() . ' 错误码:' . mysqli_connect_errno() + ]); + exit; +} +// 只有连接成功才执行字符集 +mysqli_set_charset($conn, 'utf8mb4'); + +// 下面原有业务代码不变 ... +// 获取角色 +$isAdmin = 0; +$r = mysqli_query($conn,"SELECT r.is_admin FROM sys_user u LEFT JOIN sys_role r ON u.role_id=r.id WHERE u.uid='$loginUid'"); +if($rr = mysqli_fetch_assoc($r)) $isAdmin = intval($rr['is_admin']); +$action = $_REQUEST['action'] ?? ''; +$post = json_decode(file_get_contents("php://input"),true) ?: []; + +// 1、新建工单 +if($action === "create"){ + $title = $post['title'] ?? ''; + $content = $post['content'] ?? ''; + $assign = $post['assign_uid'] ?? ''; + if(!$title || !$content){ + echo json_encode(['code'=>400,'msg'=>'标题和内容不能为空']); + exit; + } + $sql = "INSERT INTO work_order(title,content,create_uid,assign_uid,status,create_time,update_time) VALUES (?,?,?,?,?,NOW(),NOW())"; + $stmt = mysqli_prepare($conn,$sql); + mysqli_stmt_bind_param($stmt,'ssssi',$title,$content,$loginUid,$assign,1); + $ok = mysqli_stmt_execute($stmt); + if($ok) echo json_encode(['code'=>0,'msg'=>'工单提交成功']); + else echo json_encode(['code'=>500,'msg'=>'提交失败:'.mysqli_error($conn)]); + exit; +} + +// 2、修改工单状态 +if($action === "update_status"){ + $id = intval($post['id'] ?? 0); + $status = intval($post['status'] ?? 1); + if($id <=0){ + echo json_encode(['code'=>400,'msg'=>'工单ID错误']); + exit; + } + // 权限校验:普通用户只能改自己创建/分配给自己的工单 + if($isAdmin !== 1){ + $chk = mysqli_query($conn,"SELECT id FROM work_order WHERE id=$id AND (create_uid='$loginUid' OR assign_uid='$loginUid')"); + if(mysqli_num_rows($chk) === 0){ + echo json_encode(['code'=>403,'msg'=>'无权限操作该工单']); + exit; + } + } + $sql = "UPDATE work_order SET status=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn,$sql); + mysqli_stmt_bind_param($stmt,'ii',$status,$id); + mysqli_stmt_execute($stmt); + echo json_encode(['code'=>0,'msg'=>'状态更新成功']); + exit; +} + +// 3、管理员分发工单 +if($action === "assign_order"){ + if($isAdmin !== 1){ + echo json_encode(['code'=>403,'msg'=>'仅管理员可分发工单']); + exit; + } + $id = intval($post['id'] ?? 0); + $assignUid = $post['assign_uid'] ?? ''; + $sql = "UPDATE work_order SET assign_uid=?,update_time=NOW() WHERE id=?"; + $stmt = mysqli_prepare($conn,$sql); + mysqli_stmt_bind_param($stmt,'si',$assignUid,$id); + mysqli_stmt_execute($stmt); + echo json_encode(['code'=>0,'msg'=>'工单分发成功']); + exit; +} + +echo json_encode(['code'=>400,'msg'=>'无效操作']); +mysqli_close($conn); +exit; +?> diff --git a/auth.php b/auth.php new file mode 100644 index 0000000..126661c --- /dev/null +++ b/auth.php @@ -0,0 +1,163 @@ + $expireTime) { + session_destroy(); + header("Location: login.php"); + exit; +} +$_SESSION['login_time'] = time(); + +// 登录用户基础信息 +$loginUserId = $_SESSION['user_id']; +$userName = htmlspecialchars($_SESSION['username'] ?? ''); +$isAdmin = intval($_SESSION['is_admin'] ?? 0); +$userRoleId = intval($_SESSION['role_id'] ?? 0); + +// ====================== 双库固定连接 ====================== +$dbHost = "10.150.117.190"; +$dbUser = "root"; +$dbPass = "hp93000"; + +// 业务库 alert_mail_stat(工单、SMTP、大盘) +$dbAlertName = "alert_mail_stat"; +$connAlert = mysqli_connect($dbHost, $dbUser, $dbPass, $dbAlertName); +if (!$connAlert) { + die("业务库alert_mail_stat连接失败:" . mysqli_connect_error()); +} +mysqli_set_charset($connAlert, "utf8mb4"); + +// 权限库 monitor(用户、角色、权限、消息) +$dbMonitorName = "monitor"; +$connMonitor = mysqli_connect($dbHost, $dbUser, $dbPass, $dbMonitorName); +if (!$connMonitor) { + die("权限库monitor连接失败:" . mysqli_connect_error()); +} +mysqli_set_charset($connMonitor, "utf8mb4"); +// ======================================================================== + +// ========== 新增:读取角色真实名称 ========== +$roleRealName = "未知角色"; +if ($userRoleId > 0) { + $getRoleSql = "SELECT role_name FROM sys_role WHERE id = $userRoleId"; + $roleResult = mysqli_query($connMonitor, $getRoleSql); + if ($roleResult && $roleRow = mysqli_fetch_assoc($roleResult)) { + $roleRealName = $roleRow['role_name']; + } +} +// 超级管理员强制覆盖文字 +if ($isAdmin === 1) { + $roleRealName = "超级管理员"; +} +// =========================================== + +// 1. 登录时一次性加载当前角色全部权限,存入全局变量(侧边栏共用) +$allowPages = []; +if ($userRoleId > 0) { + $permSql = "SELECT page_key FROM sys_role_permission WHERE role_id = $userRoleId"; + $permRes = mysqli_query($connMonitor, $permSql); + if ($permRes) { + while ($row = mysqli_fetch_assoc($permRes)) { + $allowPages[] = $row['page_key']; + } + } +} + +// 2. 获取当前访问页面文件名(去掉.php后缀) +$currentPage = basename($_SERVER['SCRIPT_NAME'], '.php'); + +// 3. 非管理员自动拦截无权限页面(全局统一校验,无需每个页面重复写) +if ($isAdmin !== 1 && !in_array($currentPage, $allowPages)) { + echo ""; + exit; +} + +// 读取右上角未读消息数量 +$unreadMsgCount = 0; +$msgSql = "SELECT COUNT(id) cnt FROM sys_user_msg WHERE user_id = $loginUserId AND is_read = 0"; +$msgRes = mysqli_query($connMonitor, $msgSql); +if ($msgRes) { + $msgRow = mysqli_fetch_assoc($msgRes); + $unreadMsgCount = intval($msgRow['cnt']); +} + +// 读取系统基础配置 +$sysConfig = []; +$configSql = "SELECT k, v FROM sys_config"; +$configRes = mysqli_query($connAlert, $configSql); +if ($configRes) { + while ($row = mysqli_fetch_assoc($configRes)) { + $sysConfig[$row['k']] = $row['v']; + } +} +$currentLang = $sysConfig['lang'] ?? "zh"; +$sidebarRawLinks = $sysConfig['quick_link'] ?? ""; + +// 多语言字典 +$langMap = [ + 'zh' => [ + 'html_lang' => 'zh-CN', + 'page_title' => '运维监控平台', + 'monitor_panel' => '监控面板', + 'alert_overview' => '告警总览', + 'quick_link' => '快捷外链', + 'system_manage' => '系统管理', + 'notify_config' => '消息通知配置', + 'role_manage' => '角色管理', + 'user_manage' => '用户管理', + 'current_user' => '当前用户:', + 'sys_time' => '系统时间:', + 'refresh' => '刷新页面', + 'logout' => '退出登录', + 'unread_msg' => '未读消息' + ], + 'en' => [ + 'html_lang' => 'en', + 'page_title' => "Monitor Platform", + 'monitor_panel' => "Monitor Panel", + 'alert_overview' => "Alert Dashboard", + 'quick_link' => "Quick Link", + 'system_manage' => "System Manage", + 'notify_config' => "Notify Config", + 'role_manage' => "Role Manage", + 'user_manage' => "User Manage", + 'current_user' => "User: ", + 'sys_time' => "Time: ", + 'refresh' => "Refresh", + 'logout' => "Logout", + 'unread_msg' => "Unread Msg" + ] +]; +$t = $langMap[$currentLang]; + +// 解析侧边栏外链 +$sidebarLinkList = []; +if (!empty($sidebarRawLinks)) { + $lines = explode("\n", $sidebarRawLinks); + foreach ($lines as $line) { + $line = trim($line); + if (empty($line)) continue; + $item = explode("|", $line); + if (count($item) === 2) { + $sidebarLinkList[] = [ + 'name' => trim($item[0]), + 'url' => trim($item[1]) + ]; + } + } +} + +// 【已删除冲突无用函数 hasPagePerm】 +?> diff --git a/dashbaord.en-zh.php b/dashbaord.en-zh.php new file mode 100644 index 0000000..82f6e8c --- /dev/null +++ b/dashbaord.en-zh.php @@ -0,0 +1,1094 @@ + $expire)) { + session_destroy(); + header("Location: login.php"); + exit; +} +// 刷新登录计时 +$_SESSION['login_time'] = time(); +$userName = htmlspecialchars($_SESSION['username']); +$userRole = $_SESSION['role']; +$roleText = $userRole === 'admin' ? '管理员' : '只读用户'; + +// 读取全局系统配置(admin后台保存后同步生效) +$dbHost = "10.150.117.190"; +$dbUser = "root"; +$dbPwd = "hp93000"; +$dbName = "alert_mail_stat"; +$connConfig = mysqli_connect($dbHost, $dbUser, $dbPwd, $dbName); +mysqli_set_charset($connConfig, "utf8mb4"); +$configSql = "SELECT k,v FROM sys_config"; +$configRes = mysqli_query($connConfig, $configSql); +$sysConfig = []; +while ($row = mysqli_fetch_assoc($configRes)) { + $sysConfig[$row['k']] = $row['v']; +} +mysqli_close($connConfig); + +// 赋值全局配置 +$pageTitle = htmlspecialchars($sysConfig['page_title'] ?? "告警监控系统"); +$themeColor = htmlspecialchars($sysConfig['theme_color'] ?? "#409eff"); +$pageSize = $sysConfig['page_size'] ?? "20"; +$footerText = htmlspecialchars($sysConfig['footer_text'] ?? ""); +$sidebarRawLinks = $sysConfig['sidebar_links'] ?? ""; +// 读取语言配置 zh=中文 en=英文 +$currentLang = $sysConfig['lang'] ?? "zh"; + +// 全局多语言词典 +$langMap = [ + 'zh' => [ + 'html_lang' => 'zh-CN', + 'page_subtitle' => '爱德万测试 · 运维告警监控平台', + 'monitor_panel' => '监控面板', + 'alert_overview' => '告警监控总览', + 'disk_capacity' => '服务器磁盘容量', + 'quick_link' => '快捷外链', + 'system_manage' => '系统管理', + 'system_config' => '后台配置管理', + 'current_user' => '当前用户:', + 'sys_time' => '系统时间:', + 'refresh_all' => '刷新全部数据', + 'export_csv' => '导出告警CSV', + 'logout' => '退出登录', + 'lang_text' => '语言:', + 'today_fire' => '今日触发告警', + 'unresolved_now' => '当前未恢复告警', + 'resolved_today' => '今日恢复告警', + 'instance_total' => '故障实例总数', + 'recovery_rate' => '告警恢复率', + 'today_fire_sub' => 'Total Firing Today', + 'unresolved_sub' => 'Unresolved Now', + 'resolved_sub' => 'Resolved Today', + 'instance_sub' => 'Affected Host Instance', + 'rate_sub' => 'Daily Recovery Rate', + 'top10_alert' => '今日告警频次 TOP 10', + 'active_alert' => '当前未恢复活跃告警', + 'alert_count' => '条未恢复故障', + 'all_alert_log' => '今日全量告警明细列表', + 'total_alert' => '条告警记录', + 'tag' => '告警状态', + 'alert_name' => '告警名称', + 'instance_addr' => '实例地址', + 'severity' => '告警级别', + 'occur_time' => '故障发生时间', + 'empty_normal' => '当前无未恢复告警,系统运行正常', + 'empty_no_data' => '暂无今日告警数据', + 'disk_stat' => '用户目录磁盘占用统计', + 'filter_ip' => '筛选实例IP:', + 'export_disk' => '导出当前表格CSV', + 'all_instance' => '全部实例', + 'disk_loading' => '磁盘数据加载中...', + 'no_disk_data' => '暂无磁盘指标数据', + 'no_ip_data' => '该实例下无目录数据', + 'csv_tip' => '正在生成CSV报表,请稍候...', + 'alert_axis' => '告警次数', + 'data_fail' => '数据加载失败,请重新登录', + 'server_fail' => '接口数据加载失败,请检查后端服务', + 'disk_api_fail' => '磁盘接口异常:', + 'disk_request_fail' => '请求df.php接口失败,请检查服务', + 'no_export_data' => '无数据可导出' + ], + 'en' => [ + 'html_lang' => 'en', + 'page_subtitle' => 'Advantest · O&M Alert Monitor Platform', + 'monitor_panel' => 'Monitor Panel', + 'alert_overview' => 'Alert Overview', + 'disk_capacity' => 'Server Disk Capacity', + 'quick_link' => 'Quick Links', + 'system_manage' => 'System Manage', + 'system_config' => 'System Config', + 'current_user' => 'User: ', + 'sys_time' => 'System Time: ', + 'refresh_all' => 'Refresh All Data', + 'export_csv' => 'Export Alert CSV', + 'logout' => 'Logout', + 'lang_text' => 'Lang: ', + 'today_fire' => 'Today Triggered Alerts', + 'unresolved_now' => 'Unresolved Alerts', + 'resolved_today' => 'Resolved Today', + 'instance_total' => 'Affected Instances', + 'recovery_rate' => 'Daily Recovery Rate', + 'today_fire_sub' => 'Total Firing Today', + 'unresolved_sub' => 'Unresolved Now', + 'resolved_sub' => 'Resolved Today', + 'instance_sub' => 'Affected Host Instance', + 'rate_sub' => 'Daily Recovery Rate', + 'top10_alert' => 'Top 10 Alert Frequency Today', + 'active_alert' => 'Active Unresolved Alerts', + 'alert_count' => ' unresolved alerts', + 'all_alert_log' => 'Today Full Alert List', + 'total_alert' => ' alert records', + 'tag' => 'Status', + 'alert_name' => 'Alert Name', + 'instance_addr' => 'Instance IP', + 'severity' => 'Severity', + 'occur_time' => 'Occur Time', + 'empty_normal' => 'No active alerts, system normal', + 'empty_no_data' => 'No alert data today', + 'disk_stat' => 'User Directory Disk Usage', + 'filter_ip' => 'Filter IP: ', + 'export_disk' => 'Export Table CSV', + 'all_instance' => 'All Instances', + 'disk_loading' => 'Loading disk data...', + 'no_disk_data' => 'No disk data available', + 'no_ip_data' => 'No directory data for this instance', + 'csv_tip' => 'Generating CSV file, please wait...', + 'alert_axis' => 'Alert Count', + 'data_fail' => 'Data load failed, please relogin', + 'server_fail' => 'API request failed, check backend service', + 'disk_api_fail' => 'Disk API Error: ', + 'disk_request_fail' => 'Request df.php failed, check service', + 'no_export_data' => 'No data to export' + ] +]; +$t = $langMap[$currentLang]; + +// 解析侧边外链列表 +$sidebarLinkList = []; +if (!empty($sidebarRawLinks)) { + $lines = explode("\n", $sidebarRawLinks); + foreach ($lines as $line) { + $line = trim($line); + if (empty($line)) continue; + $item = explode("|", $line, 2); + if (count($item) === 2) { + $sidebarLinkList[] = [ + "name" => trim($item[0]), + "url" => trim($item[1]) + ]; + } + } +} +?> + + + + + +
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| IP | +Username | +Path | +Size(GB) | +
|---|---|---|---|
| + + + | +|||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 状态 | +告警名称 | +实例地址 | +级别 | +故障开始时间 | +接收时间 | +
|---|---|---|---|---|---|
|
+
+
+
+ 数据加载中...
+ |
+ |||||
| 状态 | +告警名称 | +实例地址 | +级别 | +故障开始时间 | +恢复时间 | +接收时间 | +
|---|---|---|---|---|---|---|
|
+
+
+
+ 数据加载中...
+ |
+ ||||||
0
+0
+0
+| 类型 | +告警名称 | +实例 | +级别 | +故障开始 | +恢复时间 | +邮件接收时间 | +
|---|
| 状态 | +告警名称 | +实例地址 | +级别 | +故障开始时间 | +恢复时间 | +接收时间 | +
|---|---|---|---|---|---|---|
|
+
+
+
+ 数据加载中...
+ |
+ ||||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| IP | +Username | +Path | +Size(GB) | +
|---|---|---|---|
| + + + | +|||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| IP | +Username | +Path | +Size(GB) | +
|---|---|---|---|
| + + + | +|||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| IP | +Username | +Path | +Size(GB) | +
|---|---|---|---|
| + + + | +|||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| IP | +Username | +Path | +Size(GB) | +
|---|---|---|---|
| + + + | +|||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
|
+
+ 磁盘数据加载中...
+ |
+ |||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
| IP | +Username | +Path | +Size(GB) | +
|---|---|---|---|
| + + + | +|||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 当前无未恢复告警,系统运行正常
+ |
+ ||||
| 告警状态 | +告警名称 | +实例地址 | +告警级别 | +故障发生时间 | +
|---|---|---|---|---|
|
+
+
+
+ 暂无今日告警数据
+ |
+ ||||
| 实例IP | +用户名 | +目录路径 | +占用容量 | +
|---|---|---|---|
| 数据加载中... | +|||
| + | + | + | + | + | + | + |
|---|---|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||||
| + | + | + | + | + | + |
|---|---|---|---|---|---|
|
+
+
+
+
+ |
+ |||||
| + | + | + | + | + | + | + |
|---|---|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||||
| + | + | + | + | + |
|---|---|---|---|---|
|
+
+
+
+
+ |
+ ||||
=t.x&&e<=t.x+t.width&&n>=t.y&&n<=t.y+t.height},t.prototype.contain=function(e,n){return t.contain(this,e,n)},t.prototype.clone=function(){return new t(this.x,this.y,this.width,this.height)},t.prototype.copy=function(t){qe(this,t)},t.prototype.plain=function(){return{x:this.x,y:this.y,width:this.width,height:this.height}},t.prototype.isFinite=function(){return isFinite(this.x)&&isFinite(this.y)&&isFinite(this.width)&&isFinite(this.height)},t.prototype.isZero=function(){return 0===this.width||0===this.height},t.create=function(e){return new t(e?e.x:0,e?e.y:0,e?e.width:0,e?e.height:0)},t.copy=function(t,e){return t.x=e.x,t.y=e.y,t.width=e.width,t.height=e.height,t},t.applyTransform=function(t,e,n){if(n){if(n[1]<1e-5&&n[1]>-1e-5&&n[2]<1e-5&&n[2]>-1e-5){var i=n[0],r=n[3],o=n[4],a=n[5];return t.x=e.x*i+o,t.y=e.y*r+a,t.width=e.width*i,t.height=e.height*r,t.width<0&&(t.x+=t.width,t.width=-t.width),void(t.height<0&&(t.y+=t.height,t.height=-t.height))}Be.x=Ge.x=e.x,Be.y=Fe.y=e.y,Ve.x=Fe.x=e.x+e.width,Ve.y=Ge.y=e.y+e.height,Be.transform(n),Fe.transform(n),Ve.transform(n),Ge.transform(n),t.x=Oe(Be.x,Ve.x,Ge.x,Fe.x),t.y=Oe(Be.y,Ve.y,Ge.y,Fe.y);var s=Re(Be.x,Ve.x,Ge.x,Fe.x),l=Re(Be.y,Ve.y,Ge.y,Fe.y);t.width=s-t.x,t.height=l-t.y}else t!==e&&qe(t,e)},t.calculateTransform=function(t,e,n){var i=n.width/e.width,r=n.height/e.height;return Te(t=Se(t||[]),t,kt(en,-e.x,-e.y)),De(t,t,kt(en,i,r)),Te(t,t,kt(en,n.x,n.y)),t},t}(),Ze=Xe.create,je=Xe.set,qe=Xe.copy,Ke=Xe.calculateTransform,$e=Xe.applyTransform,Je=Xe.contain,Qe=new Xe(0,0,0,0),tn=new Xe(0,0,0,0),en=[];function nn(t,e,n,i,r,o,a,s){var l=Ne(e-n),u=Ne(i-t),c=Oe(l,u),h=Ee[r],d=Ee[1-r],p=ze[r];e jh[1]){if(r=!1,qh.negativeSize||n)return r;var s=Xh(jh[0]-Zh[1]),l=Xh(Zh[0]-jh[1]);Uh(s,l)>$h.len()&&(s 1&&i>0?s:a}};return o;function a(){return e=0;o--){var a=t[o],s=void 0;if(a!==r&&!a.ignore&&(s=dn(a,n,i))&&(!e.topTarget&&(e.topTarget=a),s!==on)){e.target=a;break}}}function fn(t,e,n){var i=t.painter;return e<0||e>i.getWidth()||n<0||n>i.getHeight()}B(["click","mousedown","mouseup","mousewheel","dblclick","contextmenu"],function(t){hn.prototype[t]=function(e){var n,i,r=e.zrX,o=e.zrY,a=fn(this,r,o);if("mouseup"===t&&a||(i=(n=this.findHover(r,o)).target),"mousedown"===t)this._downEl=i,this._downPoint=[e.zrX,e.zrY],this._upEl=i;else if("mouseup"===t)this._upEl=i;else if("click"===t){if(this._downEl!==this._upEl||!this._downPoint||Ft(this._downPoint,[e.zrX,e.zrY])>4)return;this._downPoint=null}this.dispatchToElement(n,t,e)}});function gn(t,e,n,i){var r=e+1;if(r===n)return 1;if(i(t[r++],t[e])<0){for(;r0;)a=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),a+=r,l+=r}else{for(s=r+1;ls&&(l=s);var u=a;a=r-l,l=r-u}for(a++;as&&(l=s);var u=a;a=r-l,l=r-u}else{for(s=i-r;l=0;)a=l,(l=1+(l<<1))<=0&&(l=s);l>s&&(l=s),a+=r,l+=r}for(a++;a=0;l--)t[p+l]=t[d+l];return void(t[h]=a[c])}var f=r;for(;;){var g=0,v=0,y=!1;do{if(e(a[c],t[u])<0){if(t[h--]=t[u--],g++,v=0,0===--i){y=!0;break}}else if(t[h--]=a[c--],v++,g=0,1===--s){y=!0;break}}while((g|v)1;){var t=o-2;if(t>=1&&i[t-1]<=i[t]+i[t+1]||t>=2&&i[t-2]<=i[t]+i[t-1])i[t-1]i[t+1])break;s(t)}},forceMergeRuns:function(){for(;o>1;){var t=o-2;t>0&&i[t-1]=32;)e|=1&t,t>>=1;return t+e}(r);do{if((o=gn(t,n,i,e))s&&(l=s),vn(t,n,n+l,n+o,e),o=l}a.pushRun(n,o),a.mergeRuns(),r-=o,n+=o}while(0!==r);a.forceMergeRuns()}}}var bn=!1;function wn(){bn||(bn=!0,console.warn("z / z2 / zlevel of displayable is invalid, which may cause unexpected errors"))}function Sn(t,e){return t.zlevel===e.zlevel?t.z===e.z?t.z2-e.z2:t.z-e.z:t.zlevel-e.zlevel}var Mn=function(){function t(){this._roots=[],this._displayList=[],this._displayListLen=0,this.displayableSortFunc=Sn}return t.prototype.traverse=function(t,e){for(var n=0;n0&&t.arc(u+Z.cx,c+Z.cy,Y,fh(Z.y0,Z.x0),fh(Z.y1,Z.x1),!h),t.arc(u,c,r,fh(Z.cy+Z.y1,Z.cx+Z.x1),fh(j.cy+j.y1,j.cx+j.x1),h),U>0&&t.arc(u+j.cx,c+j.cy,U,fh(j.y1,j.x1),fh(j.y0,j.x0),!h))}else t.lineTo(u+P,c+O),t.arc(u,c,r,l,s,h);else t.lineTo(u+P,c+O)}else t.moveTo(u,c);t.closePath()}}}var wh=function(){this.cx=0,this.cy=0,this.r0=0,this.r=0,this.startAngle=0,this.endAngle=2*Math.PI,this.clockwise=!0,this.cornerRadius=0},Sh=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultShape=function(){return new wh},e.prototype.buildPath=function(t,e){bh(t,e)},e.prototype.isZeroArea=function(){return this.shape.startAngle===this.shape.endAngle||this.shape.r===this.shape.r0},e}(Kl);Sh.prototype.type="sector";var Mh=function(){this.cx=0,this.cy=0,this.r=0,this.r0=0},Ih=function(t){function e(e){return t.call(this,e)||this}return n(e,t),e.prototype.getDefaultShape=function(){return new Mh},e.prototype.buildPath=function(t,e){var n=e.cx,i=e.cy,r=2*Math.PI;t.moveTo(n+e.r,i),t.arc(n,i,e.r,0,r,!1),t.moveTo(n+e.r0,i),t.arc(n,i,e.r0,0,r,!0)},e}(Kl);function Th(t,e,n){var i=e.smooth,r=e.points;if(r&&r.length>=2){if(i){var o=function(t,e,n,i){var r,o,a,s,l=[],u=[],c=[],h=[];if(i){a=[1/0,1/0],s=[-1/0,-1/0];for(var d=0,p=t.length;dg[1]&&(g[1]=f)}return this._rawCount=this._count=s,{start:a,end:s}},t.prototype._initDataFromProvider=function(t,e,n){for(var i=this._provider,r=this._chunks,o=this._dimensions,a=o.length,s=this._rawExtent,l=V(o,function(t){return t.property}),u=0;uv[1]&&(v[1]=g)}}!i.persistent&&i.clean&&i.clean(),this._rawCount=this._count=e,this._extent=[]},t.prototype.count=function(){return this._count},t.prototype.get=function(t,e){if(!(e>=0&&e