diff --git a/internal/service/resource_management.go b/internal/service/resource_management.go index c82b141..2efcad5 100644 --- a/internal/service/resource_management.go +++ b/internal/service/resource_management.go @@ -7,6 +7,8 @@ import ( "path/filepath" "sort" "strings" + "sync" + "time" "3588AdminBackend/internal/models" "3588AdminBackend/internal/storage" @@ -220,3 +222,57 @@ func BuildResourceStatusBoard(standardResources []storage.StandardResourceRecord }) return board } + +// FetchAndBuildResourceBoard 并发查询所有在线设备的资源状态并构建面板,总超时 5 秒。 +func FetchAndBuildResourceBoard(agent *AgentClient, devices []*models.Device, standardResources []storage.StandardResourceRecord) ResourceStatusBoard { + board := ResourceStatusBoard{ + Summary: ResourceStatusSummary{ + StandardResources: len(standardResources), + Devices: len(devices), + }, + Rows: make([]ResourceStatusRow, 0, len(devices)), + } + if agent == nil || len(standardResources) == 0 { + return board + } + + installed := make(map[string][]InstalledResourceStatus) + var mu sync.Mutex + var wg sync.WaitGroup + + // 总超时:最多等 5 秒 + done := make(chan struct{}) + go func() { + time.Sleep(5 * time.Second) + close(done) + }() + + for _, device := range devices { + if device == nil || !device.Online { + continue + } + wg.Add(1) + go func(dev *models.Device) { + defer wg.Done() + items, err := FetchInstalledResourceStatuses(agent, dev) + if err == nil { + mu.Lock() + installed[dev.DeviceID] = items + mu.Unlock() + } + }(device) + } + + // Wait with timeout + waitCh := make(chan struct{}) + go func() { + wg.Wait() + close(waitCh) + }() + select { + case <-waitCh: + case <-done: + } + + return BuildResourceStatusBoard(standardResources, devices, installed) +} diff --git a/internal/web/ui.go b/internal/web/ui.go index a896621..d475882 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -610,7 +610,6 @@ func (u *UI) Routes() (chi.Router, error) { r.Get("/system", u.pageSystem) r.Get("/system/db-backup", u.pageSystemDBBackup) r.Get("/resources", u.pageResources) - r.Get("/api/resources/device-status", u.apiResourceDeviceStatus) r.Post("/system/db-restore", u.actionSystemDBRestore) r.Get("/api/graph-node-types", u.apiGraphNodeTypes) r.Get("/device-config", u.pageDeviceConfig) @@ -1419,48 +1418,21 @@ func (u *UI) pageResources(w http.ResponseWriter, r *http.Request) { data.OnlineCount++ } } + board := service.ResourceStatusBoard{} if strings.TrimSpace(u.dbPath) != "" { if store, err := storage.OpenSQLite(u.dbPath); err == nil { resourcesRepo := storage.NewResourcesRepo(store.DB()) if items, err := resourcesRepo.List(); err == nil { data.StandardResources = items + board = service.FetchAndBuildResourceBoard(u.agent, data.Devices, items) } _ = store.Close() } } + data.ResourceStatusBoard = &board u.render(w, r, "resources", data) } -func (u *UI) apiResourceDeviceStatus(w http.ResponseWriter, r *http.Request) { - u.ensureDevicesLoaded() - devices := u.registry.GetDevices() - board := service.ResourceStatusBoard{ - Summary: service.ResourceStatusSummary{Devices: len(devices)}, - } - if strings.TrimSpace(u.dbPath) != "" { - if store, err := storage.OpenSQLite(u.dbPath); err == nil { - resourcesRepo := storage.NewResourcesRepo(store.DB()) - if items, err := resourcesRepo.List(); err == nil { - board.Summary.StandardResources = len(items) - installed := map[string][]service.InstalledResourceStatus{} - for _, device := range devices { - if device == nil || !device.Online { - continue - } - items, err := service.FetchInstalledResourceStatuses(u.agent, device) - if err == nil { - installed[device.DeviceID] = items - } - } - board = service.BuildResourceStatusBoard(items, devices, installed) - } - _ = store.Close() - } - } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(board) -} - func (u *UI) actionResourceSync(w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) diff --git a/internal/web/ui/templates/resources.html b/internal/web/ui/templates/resources.html index a3772b7..81258f0 100644 --- a/internal/web/ui/templates/resources.html +++ b/internal/web/ui/templates/resources.html @@ -1,4 +1,5 @@ {{define "resources"}} +{{$board := .ResourceStatusBoard}}
@@ -6,7 +7,7 @@
统一维护标准资源,设备侧通过任务同步。当前支持人脸库等资源类型。
-
+ {{range .Devices}}{{if .Online}}{{end}}{{end}} @@ -16,7 +17,7 @@
{{icon "template"}}标准资源总数
-
{{len .StandardResources}}
+
{{if $board}}{{$board.Summary.StandardResources}}{{else}}0{{end}}
{{icon "devices"}}在线设备数
@@ -24,15 +25,15 @@
{{icon "assets"}}完整设备数
-
-
+
{{if $board}}{{$board.Summary.CompleteDevices}}{{else}}0{{end}}
{{icon "warn"}}缺失设备数
-
-
+
{{if $board}}{{$board.Summary.MissingDevices}}{{else}}0{{end}}
{{icon "service"}}不一致设备数
-
-
+
{{if $board}}{{$board.Summary.MismatchDevices}}{{else}}0{{end}}
@@ -66,15 +67,15 @@
-
+

{{icon "devices"}}设备资源状态

-
按标准资源逐项比对设备已安装状态,缺失和不一致会直接标出。
+
按标准资源逐项比对设备已安装状态,缺失和不一致会直接标出。
- +
@@ -82,69 +83,42 @@ - - + + {{if and $board (gt (len $board.Rows) 0)}} + {{range $board.Rows}} + + + {{range .Cells}} + + {{end}} + + + {{end}} + {{else}} + + {{end}}
设备非标资源
正在加载设备资源状态…
+
{{.DeviceName}}
+
{{.DeviceID}}
+
+ {{if eq .Status "ok"}}一致 + {{else if eq .Status "mismatch"}}不一致 + {{else}}缺失{{end}} + + {{if gt .ExtraCount 0}} +
+ {{.ExtraCount}} 个 · 更多 +
+ {{range .ExtraResources}} +
{{.Name}}
+ {{end}} +
+
+ {{else}} + 0 + {{end}} +
暂无设备资源状态。请先确保设备在线且 agent 实现了 GET /v1/resources/status 端点。
- - {{end}}