diff --git a/backend/controller/agent.go b/backend/controller/agent.go index d23e405..eaa94e2 100644 --- a/backend/controller/agent.go +++ b/backend/controller/agent.go @@ -163,43 +163,57 @@ func (h *AgentHandler) ReceiveReport(c *gin.Context) { result = h.db.Exec(updateSQL2, args2...) } - // 如果还是没匹配到 —— 自动注册新服务器(UPSERT 防重复) + // 如果还是没匹配到 —— 按 hostname 显式查重后再注册 // Logan.Li + // asset_servers.hostname 无 UNIQUE 索引,OnConflict 不可靠,故先 First 查重, + // 避免 Agent 每次上报(10s)都新建记录导致资产越来越多。 if result.RowsAffected == 0 { osName := "" if req.System != nil { osName = req.System.OSVersion } - newServer := model.Server{ - Hostname: req.Hostname, - IP: matchIP, - Port: 22, - Username: "root", - AuthType: "password", - OS: osName, - CPU: req.CPU.CoreCount, - Memory: memMB, - Disk: diskGB, - Status: "running", - AgentStatus: "online", - AgentVersion: req.AgentVersion, - CPUUsage: req.CPU.UsagePercent, - MemoryUsage: req.Memory.UsagePercent, - DiskUsage: diskAvg, - } - // ON DUPLICATE KEY UPDATE — 若 hostname 已存在则更新字段(防重复注册) - if err := h.db.Clauses(clause.OnConflict{ - Columns: []clause.Column{{Name: "hostname"}}, - DoUpdates: clause.AssignmentColumns([]string{ - "ip", "os", "cpu", "memory", "disk", - "status", "agent_status", - "cpu_usage", "memory_usage", "disk_usage", - "updated_at", - }), - }).Create(&newServer).Error; err != nil { - fmt.Printf("[agent] 自动注册主机 %q (IP: %s) 失败: %v\n", req.Hostname, matchIP, err) + var existing model.Server + if h.db.Where("hostname = ?", req.Hostname).First(&existing).Error == nil { + // 已自动注册过 → 仅更新实时指标,不重复创建 + h.db.Model(&existing).Updates(map[string]interface{}{ + "ip": matchIP, "os": osName, "cpu": req.CPU.CoreCount, + "memory": memMB, "disk": diskGB, "status": "running", + "agent_status": "online", "agent_version": req.AgentVersion, + "cpu_usage": req.CPU.UsagePercent, "memory_usage": req.Memory.UsagePercent, + "disk_usage": diskAvg, + }) } else { - fmt.Printf("[agent] 自动注册新主机: %q (IP: %s, CPU=%d核, 内存=%dMB, 磁盘=%dGB, OS=%q)\n", - req.Hostname, matchIP, req.CPU.CoreCount, memMB, diskGB, osName) + // 真正的新主机 → 注册(OnConflict 作为并发双保险) + newServer := model.Server{ + Hostname: req.Hostname, + IP: matchIP, + Port: 22, + Username: "root", + AuthType: "password", + OS: osName, + CPU: req.CPU.CoreCount, + Memory: memMB, + Disk: diskGB, + Status: "running", + AgentStatus: "online", + AgentVersion: req.AgentVersion, + CPUUsage: req.CPU.UsagePercent, + MemoryUsage: req.Memory.UsagePercent, + DiskUsage: diskAvg, + } + if err := h.db.Clauses(clause.OnConflict{ + Columns: []clause.Column{{Name: "hostname"}}, + DoUpdates: clause.AssignmentColumns([]string{ + "ip", "os", "cpu", "memory", "disk", + "status", "agent_status", + "cpu_usage", "memory_usage", "disk_usage", + "updated_at", + }), + }).Create(&newServer).Error; err != nil { + fmt.Printf("[agent] 自动注册主机 %q (IP: %s) 失败: %v\n", req.Hostname, matchIP, err) + } else { + fmt.Printf("[agent] 自动注册新主机: %q (IP: %s, CPU=%d核, 内存=%dMB, 磁盘=%dGB, OS=%q)\n", + req.Hostname, matchIP, req.CPU.CoreCount, memMB, diskGB, osName) + } } } diff --git a/backend/controller/server_v2.go b/backend/controller/server_v2.go index 260aaba..af8e883 100644 --- a/backend/controller/server_v2.go +++ b/backend/controller/server_v2.go @@ -169,11 +169,15 @@ func (h *ServerHandlerV2) BatchCreateServers(c *gin.Context) { } func (h *ServerHandlerV2) BatchDeleteServers(c *gin.Context) { - var ids []uint - if err := c.ShouldBindJSON(&ids); err != nil { + // 前端发 { ids: [...] },对齐格式(原期望裸 []uint 导致解析失败 400) // Logan.Li + var req struct { + IDs []uint `json:"ids"` + } + if err := c.ShouldBindJSON(&req); err != nil || len(req.IDs) == 0 { utils.BadRequest(c, "请求参数错误") return } + ids := req.IDs // 清理关联数据 for _, id := range ids { var s model.Server