From 4dd3e3238dbd73e8caea3dd062383165b77e9925 Mon Sep 17 00:00:00 2001 From: attacker Date: Fri, 19 Jun 2026 01:51:16 +0000 Subject: [PATCH] =?UTF-8?q?fix:=20agent=5Fversion=20=E4=B8=BA=E7=A9=BA=20&?= =?UTF-8?q?=20=E6=81=A2=E5=A4=8D=E6=89=B9=E9=87=8F=E5=8D=B8=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端上报时若 agent_version 为空则不覆盖已有版本(防止旧Agent清空版本号) - 安装/升级后写入版本号 v0.1.0(而非 installed/latest) - DB 存量 online 主机 agent_version 补填 v0.1.0 - 前端恢复批量卸载 Agent 按钮 --- backend/internal/api/handlers/agent.go | 17 +++++++++-- .../internal/api/handlers/agent_install.go | 8 ++--- .../monitor/dashboard/GrafanaDashboard.tsx | 29 ++++++++++++++++--- 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/backend/internal/api/handlers/agent.go b/backend/internal/api/handlers/agent.go index b376cc2..36d0605 100644 --- a/backend/internal/api/handlers/agent.go +++ b/backend/internal/api/handlers/agent.go @@ -96,19 +96,30 @@ func (h *AgentHandler) ReceiveReport(c *gin.Context) { } // 直接 SQL 更新 asset_servers 监控字段 + // 若 AgentVersion 非空则同步写入,否则保留数据库中已有版本号(防止旧版Agent空字段覆盖已设置的版本) updateSQL := `UPDATE asset_servers SET cpu_usage = ?, memory_usage = ?, disk_usage = ?, cpu = ?, memory = ?, disk = ?, - status = ?, agent_status = ?, agent_version = ?, + status = ?, agent_status = ?` + func() string { + if req.AgentVersion != "" { + return `, agent_version = ?` + } + return `` + }() + `, hostname = CASE WHEN hostname != ? AND ip = ? THEN ? ELSE hostname END, updated_at = NOW()` args := []interface{}{ req.CPU.UsagePercent, req.Memory.UsagePercent, diskAvg, req.CPU.CoreCount, memMB, diskGB, - "running", "online", req.AgentVersion, + "running", "online", + } + if req.AgentVersion != "" { + args = append(args, req.AgentVersion) + } + args = append(args, // CASE WHEN hostname != ? AND ip = ? THEN ? ELSE hostname END req.Hostname, matchIP, req.Hostname, - } + ) if req.System != nil { if req.System.OSVersion != "" { updateSQL += ", os = ?" diff --git a/backend/internal/api/handlers/agent_install.go b/backend/internal/api/handlers/agent_install.go index 762d6db..84eb797 100644 --- a/backend/internal/api/handlers/agent_install.go +++ b/backend/internal/api/handlers/agent_install.go @@ -171,10 +171,10 @@ func (h *AgentInstallHandler) InstallAgent(c *gin.Context) { installed := contains(verifyOut, "active") || contains(verifyOut, "running") if installed { - // 更新服务器 Agent 状态 + // 更新服务器 Agent 状态(版本号:安装成功后由 Agent 下一次上报自动更新) h.db.Model(&server).Updates(map[string]interface{}{ "agent_status": "online", - "agent_version": "installed", + "agent_version": "v0.1.0", "last_check_at": time.Now(), }) } @@ -224,7 +224,7 @@ func (h *AgentInstallHandler) installOne(server models.Server, serverURL, token if installed { h.db.Model(&server).Updates(map[string]interface{}{ "agent_status": "online", - "agent_version": "installed", + "agent_version": "v0.1.0", "last_check_at": time.Now(), }) } @@ -433,7 +433,7 @@ func (h *AgentInstallHandler) BatchUpgradeAgent(c *gin.Context) { // 更新版本号 h.db.Model(&server).Updates(map[string]interface{}{ - "agent_version": "latest", + "agent_version": "v0.1.0", "last_check_at": time.Now(), }) log.Printf("[BatchUpgrade] %s Agent 升级成功", server.Hostname) diff --git a/frontend/src/components/monitor/dashboard/GrafanaDashboard.tsx b/frontend/src/components/monitor/dashboard/GrafanaDashboard.tsx index e206ee5..eedde44 100644 --- a/frontend/src/components/monitor/dashboard/GrafanaDashboard.tsx +++ b/frontend/src/components/monitor/dashboard/GrafanaDashboard.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect, useMemo } from 'react'; import { Monitor, Plus, RefreshCw, Maximize2, Minimize2, BarChart3, Clock, Server, Activity, Database, Wifi, WifiOff, ArrowUpDown, ArrowUp, ArrowDown, - ExternalLink, Download, Rocket, PackageOpen, Loader2, Info + ExternalLink, Download, Rocket, PackageOpen, Loader2, Info, Trash2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -24,6 +24,7 @@ import { http } from '@/services/http'; import { asList } from '@/components/agent/agentUtils'; import { AgentWizard } from '@/components/agent/AgentWizard'; import { Checkbox } from '@/components/ui/checkbox'; +import { useConfirm } from '@/components/ui/confirm-dialog'; import { useNavigate } from 'react-router-dom'; // ═══════════ Tab 配置 ═══════════ @@ -40,6 +41,7 @@ interface AgentHost { server_id: number; hostname: string; ip: string; cpu_usage type SortField = 'hostname' | 'agent_version' | 'agent_status' | 'cpu_usage' | 'mem_usage' | 'disk_usage' | 'region' | 'ip'; const AgentTab: React.FC = () => { + const _confirm = useConfirm(); const navigate = useNavigate(); const [hosts, setHosts] = useState([]); const [loading, setLoading] = useState(true); @@ -57,6 +59,20 @@ const AgentTab: React.FC = () => { const [batchSubmitting, setBatchSubmitting] = useState(false); // 正在执行操作的主机 ID 集合(版本列 loading) const [operating, setOperating] = useState>(new Set()); + // 批量卸载 + const [batchUninstalling, setBatchUninstalling] = useState(false); + const handleBatchUninstall = async () => { + const ok = await _confirm({ title: '批量卸载 Agent', description: `确定卸载选中的 ${selIds.length} 台主机的 AIOps Agent?卸载将在后台异步执行。`, confirmText: '卸载', variant: 'destructive' }); + if (!ok) return; + setBatchUninstalling(true); + try { + await agentApi.batchUninstall(selIds); + toast.success(`已提交:批量卸载 ${selIds.length} 台主机的 Agent,后台执行中,30秒后自动刷新`); + setSelected(new Set()); + setTimeout(() => fetch(), 30000); + } catch { toast.error('提交失败'); } + finally { setBatchUninstalling(false); } + }; const totalPages = Math.max(1, Math.ceil(hosts.length / pageSize)); const fetch = async () => { try { setHosts(asList(await http.get('/agent/latest'))); } catch {} finally { setLoading(false); } }; useEffect(() => { fetch(); const t = setInterval(fetch, 30000); return () => clearInterval(t); }, []); @@ -141,9 +157,14 @@ const AgentTab: React.FC = () => { 安装 Agent {selAny && ( - + <> + + + )}