fix: alarmChannelSource统一处理通道后缀,CSV和列表共用一个映射

This commit is contained in:
tian 2026-07-26 11:52:21 +08:00
parent 64f2e88c26
commit b4ef331be8
3 changed files with 33 additions and 19 deletions

View File

@ -595,6 +595,7 @@ func NewUI(discovery *service.DiscoveryService, registry *service.RegistryServic
return ""
}
},
"alarmChannelSource": alarmChannelSource,
"statusLabel": func(v string) string {
switch strings.TrimSpace(v) {
case "unacknowledged":
@ -2734,13 +2735,26 @@ func (u *UI) apiAlarmExport(w http.ResponseWriter, r *http.Request) {
}
records := u.alarmCollector.ExportFiltered(from, to, deviceID, ruleType)
// Lookup device info for names and IPs
// Device name/IP lookup
deviceMap := map[string]*models.Device{}
for _, d := range u.registry.GetDevices() {
if d != nil {
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))
@ -2750,27 +2764,11 @@ func (u *UI) apiAlarmExport(w http.ResponseWriter, r *http.Request) {
for _, a := range records {
devName := a.DeviceID
devIP := ""
sourceName := a.Channel
if dev, ok := deviceMap[a.DeviceID]; ok {
devName = dev.DisplayName()
devIP = dev.IP
}
// Resolve channel to video source name
if u.preview != nil {
if assignment, err := u.preview.GetDeviceAssignment(a.DeviceID); err == nil && assignment != nil {
for _, ref := range assignment.RecognitionUnits {
if unit, err := u.preview.GetRecognitionUnit(ref); err == nil && unit != nil {
if unit.Name == a.Channel && unit.VideoSourceRef != "" {
sourceName = unit.VideoSourceRef
break
}
}
}
}
}
if sourceName == "" {
sourceName = a.Channel
}
sourceName := alarmChannelSource(a.Channel, channelSources)
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),
@ -2817,6 +2815,22 @@ 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)

View File

@ -62,7 +62,7 @@
<a href="/devices/{{.DeviceID}}">{{index $.AlarmDeviceNames .DeviceID}}</a>
<div class="muted small mono">{{index $.AlarmDeviceIPs .DeviceID}}</div>
</td>
<td>{{if index $.AlarmChannelSources .Channel}}{{index $.AlarmChannelSources .Channel}}{{else}}{{.Channel}}{{end}}</td>
<td>{{alarmChannelSource .Channel $.AlarmChannelSources}}</td>
<td>
<span class="pill warn">{{ruleLabel .RuleName}}</span>
</td>

Binary file not shown.