fix(agent): 修复资产重复注册(每10s新建) + 批量删除400
agent 自动注册前按 hostname 显式查重(asset_servers.hostname 无 UNIQUE 索引,OnConflict 失效导致 Agent 每次上报都 INSERT 新记录,资产越来越多)。
批量删除 BatchDeleteServers 对齐前端 {ids} 格式(原期望裸 []uint 导致 ShouldBindJSON 解析失败 400)。
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
f820f0271f
commit
b7780c5a5a
2 files changed
+51
-33
No files matched your search
+45
-31
@@ -163,43 +163,57 @@ func (h *AgentHandler) ReceiveReport(c *gin.Context) {
|
|||||||
result = h.db.Exec(updateSQL2, args2...)
|
result = h.db.Exec(updateSQL2, args2...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果还是没匹配到 —— 自动注册新服务器(UPSERT 防重复)
|
// 如果还是没匹配到 —— 按 hostname 显式查重后再注册 // Logan.Li
|
||||||
|
// asset_servers.hostname 无 UNIQUE 索引,OnConflict 不可靠,故先 First 查重,
|
||||||
|
// 避免 Agent 每次上报(10s)都新建记录导致资产越来越多。
|
||||||
if result.RowsAffected == 0 {
|
if result.RowsAffected == 0 {
|
||||||
osName := ""
|
osName := ""
|
||||||
if req.System != nil {
|
if req.System != nil {
|
||||||
osName = req.System.OSVersion
|
osName = req.System.OSVersion
|
||||||
}
|
}
|
||||||
newServer := model.Server{
|
var existing model.Server
|
||||||
Hostname: req.Hostname,
|
if h.db.Where("hostname = ?", req.Hostname).First(&existing).Error == nil {
|
||||||
IP: matchIP,
|
// 已自动注册过 → 仅更新实时指标,不重复创建
|
||||||
Port: 22,
|
h.db.Model(&existing).Updates(map[string]interface{}{
|
||||||
Username: "root",
|
"ip": matchIP, "os": osName, "cpu": req.CPU.CoreCount,
|
||||||
AuthType: "password",
|
"memory": memMB, "disk": diskGB, "status": "running",
|
||||||
OS: osName,
|
"agent_status": "online", "agent_version": req.AgentVersion,
|
||||||
CPU: req.CPU.CoreCount,
|
"cpu_usage": req.CPU.UsagePercent, "memory_usage": req.Memory.UsagePercent,
|
||||||
Memory: memMB,
|
"disk_usage": diskAvg,
|
||||||
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)
|
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("[agent] 自动注册新主机: %q (IP: %s, CPU=%d核, 内存=%dMB, 磁盘=%dGB, OS=%q)\n",
|
// 真正的新主机 → 注册(OnConflict 作为并发双保险)
|
||||||
req.Hostname, matchIP, req.CPU.CoreCount, memMB, diskGB, osName)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -169,11 +169,15 @@ func (h *ServerHandlerV2) BatchCreateServers(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *ServerHandlerV2) BatchDeleteServers(c *gin.Context) {
|
func (h *ServerHandlerV2) BatchDeleteServers(c *gin.Context) {
|
||||||
var ids []uint
|
// 前端发 { ids: [...] },对齐格式(原期望裸 []uint 导致解析失败 400) // Logan.Li
|
||||||
if err := c.ShouldBindJSON(&ids); err != nil {
|
var req struct {
|
||||||
|
IDs []uint `json:"ids"`
|
||||||
|
}
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil || len(req.IDs) == 0 {
|
||||||
utils.BadRequest(c, "请求参数错误")
|
utils.BadRequest(c, "请求参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
ids := req.IDs
|
||||||
// 清理关联数据
|
// 清理关联数据
|
||||||
for _, id := range ids {
|
for _, id := range ids {
|
||||||
var s model.Server
|
var s model.Server
|
||||||
|
|||||||
Reference in new issue
Block a user