fix(dashboard): 修复大屏主机列表恒为空 —— 区域表名错误且错误被静默吞掉

现象:/servers 资产里有 31 台,/dashboards 大屏一台都不显示。

根因:GetLatestReports 的 SQL 中 JOIN 了不存在的表 asset_regions,
实际表名是 asset_cloud_regions(见 model/server.go 的 Region.TableName())。
查询直接报 Error 1146,但 Scan() 的错误未校验,函数静默返回空数组,
接口始终 200 + data:[],因此前端无任何报错可循。

- agent.go: JOIN 改为 asset_cloud_regions,并补上错误校验与 500 响应
- agent_report_test.go: 移除手工创建的 asset_regions,
  改由 AutoMigrate(&model.Server{}) 级联建立 asset_cloud_regions,
  避免测试用错误表名掩盖生产问题

验证:/agent/latest 由 0 台恢复为 31 台(含未装 Agent 的 not_installed 主机)。

// Logan.Li
This commit is contained in:
Logan.Li committed 2026-09-14 08:29:56 +00:00
1 parent 4010b4fd70
commit 610402d47f
2 files changed
+13 -6

No files matched your search

+11 -3
View File
@@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"math" "math"
"net/http" "net/http"
"sort" "sort"
@@ -289,7 +290,9 @@ func (h *AgentHandler) GetLatestReports(c *gin.Context) {
threeMinAgo := time.Now().UTC().Add(-3 * time.Minute) threeMinAgo := time.Now().UTC().Add(-3 * time.Minute)
// 以 servers 表为主,LEFT JOIN 最近 3 分钟内的 Agent 上报 // 以 servers 表为主,LEFT JOIN 最近 3 分钟内的 Agent 上报
// 未装 Agent 的主机也会显示(agent_status='not_installed'),打破"不显示→无法安装"的循环 // 未装 Agent 的主机也会显示(agent_status='not_installed'),打破"不显示→无法安装"的循环
h.db.Raw(` // 必须检查错误:此前未校验导致 asset_regions 表名错误被静默吞掉,
// 接口稳定返回空数组,表现为"资产有主机、大屏没主机"却无任何报错。 // Logan.Li
if err := h.db.Raw(`
SELECT SELECT
s.id as server_id, s.id as server_id,
s.hostname, s.hostname,
@@ -330,9 +333,14 @@ func (h *AgentHandler) GetLatestReports(c *gin.Context) {
GROUP BY hostname GROUP BY hostname
) )
) a ON a.hostname = s.hostname ) a ON a.hostname = s.hostname
LEFT JOIN asset_regions r ON r.id = s.region_id LEFT JOIN asset_cloud_regions r ON r.id = s.region_id
ORDER BY a.report_time DESC, s.id ASC ORDER BY a.report_time DESC, s.id ASC
`, threeMinAgo).Scan(&results) `, threeMinAgo).Scan(&results).Error; err != nil {
log.Printf("[GetLatestReports] 查询主机最新上报失败: %v", err)
c.JSON(http.StatusInternalServerError, model.Response{
Code: 500, Message: "查询主机列表失败: " + err.Error()})
return
}
if results == nil { if results == nil {
results = []model.AgentLatestResponse{} results = []model.AgentLatestResponse{}
+2 -3
View File
@@ -135,9 +135,8 @@ func TestGetLatestReportsUsesUTCTimeBoundary(t *testing.T) {
if err := db.AutoMigrate(&model.Server{}, &model.AgentReport{}); err != nil { if err := db.AutoMigrate(&model.Server{}, &model.AgentReport{}); err != nil {
t.Fatalf("迁移测试表失败: %v", err) t.Fatalf("迁移测试表失败: %v", err)
} }
if err := db.Exec("CREATE TABLE asset_regions (id INTEGER PRIMARY KEY, name TEXT)").Error; err != nil { // asset_cloud_regions 由上面的 AutoMigrate(&model.Server{}) 级联建立,
t.Fatalf("创建区域测试表失败: %v", err) // 无需手工建表;此前手工建 asset_regions 反而掩盖了生产侧同名的错误 join。 // Logan.Li
}
server := model.Server{ server := model.Server{
Hostname: "c-redis-cluster-3", Hostname: "c-redis-cluster-3",