Files
iotlcm/server/internal/logic/warningnotify/aggregate.go
T
2026-10-29 14:36:49 +08:00

254 lines
6.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package warningnotify
import (
"sort"
"strings"
)
type groupKey struct {
Business string
TenantID string
DeptID int64
CreatedBy int64
Method string
Recipients string
Category string
}
func BuildBatches(events []Event) []Batch {
if len(events) == 0 {
return nil
}
grouped := make(map[groupKey]*Batch)
for _, event := range events {
event = normalizeEvent(event)
for _, method := range methodsForEvent(event) {
for _, recipients := range recipientGroupsForMethod(method, event) {
if methodRequiresRecipient(method) && len(recipients) == 0 {
continue
}
key := groupKeyForEvent(method, recipients, event)
batch := grouped[key]
if batch == nil {
batch = &Batch{
Business: event.Business,
Method: method,
TenantID: event.TenantID,
DeptID: event.DeptID,
DeptName: event.DeptName,
CreatedBy: event.CreatedBy,
Category: event.Category,
Recipients: recipients,
Items: make([]Event, 0),
}
grouped[key] = batch
} else if strings.TrimSpace(batch.DeptName) == "" {
batch.DeptName = event.DeptName
}
batch.Items = append(batch.Items, event)
}
}
}
keys := make([]groupKey, 0, len(grouped))
for key := range grouped {
keys = append(keys, key)
}
sort.Slice(keys, func(i, j int) bool {
return compareGroupKey(keys[i], keys[j]) < 0
})
batches := make([]Batch, 0, len(keys))
for _, key := range keys {
batch := grouped[key]
sort.SliceStable(batch.Items, func(i, j int) bool {
if batch.Items[i].WarnStage != batch.Items[j].WarnStage {
return batch.Items[i].WarnStage > batch.Items[j].WarnStage
}
if batch.Items[i].ItemCode != batch.Items[j].ItemCode {
return batch.Items[i].ItemCode < batch.Items[j].ItemCode
}
return batch.Items[i].EventID < batch.Items[j].EventID
})
batch.WarnStage = highestStage(batch.Items)
applyContent(batch)
batches = append(batches, *batch)
}
return batches
}
func groupKeyForEvent(method string, recipients []string, event Event) groupKey {
key := groupKey{
Business: event.Business,
TenantID: event.TenantID,
DeptID: event.DeptID,
CreatedBy: event.CreatedBy,
Method: method,
Recipients: strings.Join(recipients, ","),
Category: event.Category,
}
if method == MethodSMS || method == MethodEmail {
key.DeptID = 0
key.CreatedBy = 0
}
if method == MethodWechat {
key.DeptID = 0
key.CreatedBy = 0
key.Recipients = ""
}
return key
}
func compareGroupKey(left, right groupKey) int {
leftParts := []string{left.TenantID, left.Business, left.Method, left.Recipients, left.Category}
rightParts := []string{right.TenantID, right.Business, right.Method, right.Recipients, right.Category}
for i := range leftParts {
if leftParts[i] < rightParts[i] {
return -1
}
if leftParts[i] > rightParts[i] {
return 1
}
}
if left.DeptID != right.DeptID {
if left.DeptID < right.DeptID {
return -1
}
return 1
}
if left.CreatedBy != right.CreatedBy {
if left.CreatedBy < right.CreatedBy {
return -1
}
return 1
}
return 0
}
func normalizeEvent(event Event) Event {
event.Business = strings.TrimSpace(event.Business)
if event.Business == "" {
event.Business = BusinessSimWarning
}
event.TenantID = strings.TrimSpace(event.TenantID)
event.DeptName = strings.TrimSpace(event.DeptName)
event.Category = strings.TrimSpace(event.Category)
if event.Category == "" {
event.Category = "预警"
}
event.Kind = strings.TrimSpace(event.Kind)
event.ItemCode = strings.TrimSpace(event.ItemCode)
event.MSISDN = strings.TrimSpace(event.MSISDN)
event.WarnMethods = strings.TrimSpace(event.WarnMethods)
event.WarnPhones = strings.TrimSpace(event.WarnPhones)
event.WarnEmails = strings.TrimSpace(event.WarnEmails)
event.UnitText = strings.TrimSpace(event.UnitText)
event.Remark = strings.TrimSpace(event.Remark)
event.OperationText = strings.TrimSpace(event.OperationText)
event.BatchNo = strings.TrimSpace(event.BatchNo)
return event
}
func methodsForEvent(event Event) []string {
values := splitList(event.WarnMethods)
seen := map[string]struct{}{MethodPlatform: {}}
result := []string{MethodPlatform}
for _, value := range values {
switch value {
case MethodSMS, MethodEmail, MethodPhone, MethodPlatform:
default:
continue
}
if _, exists := seen[value]; exists {
continue
}
seen[value] = struct{}{}
result = append(result, value)
}
sort.SliceStable(result, func(i, j int) bool {
return methodOrder(result[i]) < methodOrder(result[j])
})
return result
}
func methodOrder(method string) int {
switch method {
case MethodPlatform:
return 1
case MethodEmail:
return 2
case MethodSMS:
return 3
case MethodPhone:
return 4
case MethodWechat:
return 5
default:
return 99
}
}
func recipientGroupsForMethod(method string, event Event) [][]string {
switch method {
case MethodSMS:
return singleRecipientGroups(splitList(event.WarnPhones))
case MethodEmail:
return singleRecipientGroups(splitList(event.WarnEmails))
default:
return [][]string{{}}
}
}
func methodRequiresRecipient(method string) bool {
return method == MethodSMS || method == MethodEmail
}
func singleRecipientGroups(recipients []string) [][]string {
groups := make([][]string, 0, len(recipients))
for _, recipient := range recipients {
recipient = strings.TrimSpace(recipient)
if recipient == "" {
continue
}
groups = append(groups, []string{recipient})
}
return groups
}
func splitList(value string) []string {
parts := strings.FieldsFunc(value, func(r rune) bool {
switch r {
case ',', ';', ',', ';', '|', '\n', '\r', '\t', ' ':
return true
default:
return false
}
})
seen := make(map[string]struct{}, len(parts))
result := make([]string, 0, len(parts))
for _, part := range parts {
part = strings.TrimSpace(part)
if part == "" {
continue
}
if _, exists := seen[part]; exists {
continue
}
seen[part] = struct{}{}
result = append(result, part)
}
sort.Strings(result)
return result
}
func highestStage(items []Event) int {
stage := 0
for _, item := range items {
if item.WarnStage > stage {
stage = item.WarnStage
}
}
return stage
}