diff --git a/internal/service/config_assets.go b/internal/service/config_assets.go index e2d21f1..be112f7 100644 --- a/internal/service/config_assets.go +++ b/internal/service/config_assets.go @@ -1830,3 +1830,22 @@ func intValue(v any) int { return 0 } } + +// ResolveChannelToSource maps an alarm channel (e.g. "src_52ef4499_alarm_violation") +// to its video source name (e.g. "5跨主通道"). +func (s *ConfigPreviewService) ResolveChannelToSource(channel string) string { + if s == nil { + return channel + } + unitName := channel + if idx := strings.LastIndex(channel, "_alarm_"); idx > 0 { + unitName = channel[:idx] + } + units, _ := s.ListRecognitionUnits() + for _, u := range units { + if u.Name == unitName && u.VideoSourceRef != "" { + return u.VideoSourceRef + } + } + return channel +} diff --git a/internal/web/ui.go b/internal/web/ui.go index f564f20..8f12269 100644 --- a/internal/web/ui.go +++ b/internal/web/ui.go @@ -595,7 +595,12 @@ func NewUI(discovery *service.DiscoveryService, registry *service.RegistryServic return "" } }, - "alarmChannelSource": alarmChannelSource, + "alarmChannelSource": func(channel string) string { + if len(preview) > 0 && preview[0] != nil { + return preview[0].ResolveChannelToSource(channel) + } + return channel + }, "statusLabel": func(v string) string { switch strings.TrimSpace(v) { case "unacknowledged": @@ -2580,24 +2585,12 @@ func (u *UI) pageAlarms(w http.ResponseWriter, r *http.Request) { // Build device name/IP maps and channel→source map for template data.AlarmDeviceNames = map[string]string{} data.AlarmDeviceIPs = map[string]string{} - data.AlarmChannelSources = map[string]string{} for _, dev := range data.Devices { if dev != nil { data.AlarmDeviceNames[dev.DeviceID] = dev.DisplayName() data.AlarmDeviceIPs[dev.DeviceID] = dev.IP } } - if u.preview != nil { - if assignments, err := u.preview.ListDeviceAssignments(); err == nil { - for _, a := range assignments { - for _, ref := range a.RecognitionUnits { - if unit, err := u.preview.GetRecognitionUnit(ref); err == nil && unit != nil && unit.VideoSourceRef != "" { - data.AlarmChannelSources[unit.Name] = unit.VideoSourceRef - } - } - } - } - } // Calculate total pages and build page list: 1, ..., cur-2, cur-1, cur, cur+1, cur+2, ..., N if data.TotalAlarmCount > 0 { data.TotalAlarmPages = (data.TotalAlarmCount + perPage - 1) / perPage @@ -2742,19 +2735,6 @@ func (u *UI) apiAlarmExport(w http.ResponseWriter, r *http.Request) { deviceMap[d.DeviceID] = d } } - // Channel→source lookup (channel = recognition unit name) - channelSources := map[string]string{} - if u.preview != nil { - if assignments, _ := u.preview.ListDeviceAssignments(); len(assignments) > 0 { - for _, a := range assignments { - for _, ref := range a.RecognitionUnits { - if unit, err := u.preview.GetRecognitionUnit(ref); err == nil && unit != nil && unit.VideoSourceRef != "" { - channelSources[unit.Name] = unit.VideoSourceRef - } - } - } - } - } w.Header().Set("Content-Type", "text/csv; charset=utf-8") w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="alarms_%s_%s.csv"`, from, to)) @@ -2768,7 +2748,7 @@ func (u *UI) apiAlarmExport(w http.ResponseWriter, r *http.Request) { devName = dev.DisplayName() devIP = dev.IP } - sourceName := alarmChannelSource(a.Channel, channelSources) + sourceName := u.preview.ResolveChannelToSource(a.Channel) fmt.Fprintf(w, "%s,%s,%s,%s,%s,%s,%s\n", escapeCSV(formatAlarmTime(a.Timestamp)), escapeCSV(devName), escapeCSV(devIP), escapeCSV(sourceName), escapeCSV(alarmRuleLabel(a.RuleName)), escapeCSV(a.ObjectLabel), @@ -2815,30 +2795,14 @@ func alarmStatusLabel(v string) string { } } -// alarmChannelSource resolves an alarm channel (e.g. "cam1_alarm_violation") to its video source name. -// The channel format is {instanceName}_alarm_{type}, where instanceName matches the recognition unit. -func alarmChannelSource(channel string, sources map[string]string) string { - // Try exact match first - if src, ok := sources[channel]; ok { - return src - } - // Strip suffix: cam1_alarm_violation → cam1 - if idx := strings.LastIndex(channel, "_alarm_"); idx > 0 { - if src, ok := sources[channel[:idx]]; ok { - return src - } - } - return channel -} - func formatAlarmTime(t string) string { // 2026-07-22T13:03:20+08:00 → 2026-07-22 13:03:20 s := strings.TrimSpace(t) s = strings.Replace(s, "T", " ", 1) - if idx := strings.Index(s, "+"); idx > 0 { - s = s[:idx] + if idx := strings.LastIndex(s, "+"); idx > 10 { + s = strings.TrimSpace(s[:idx]) } - return strings.TrimSpace(s) + return s } func (u *UI) pageResources(w http.ResponseWriter, r *http.Request) { diff --git a/internal/web/ui/templates/alarms.html b/internal/web/ui/templates/alarms.html index c3838e0..68ccf1d 100644 --- a/internal/web/ui/templates/alarms.html +++ b/internal/web/ui/templates/alarms.html @@ -62,7 +62,7 @@ {{index $.AlarmDeviceNames .DeviceID}}