package stats import ( "context" "strings" "time" "lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope" ) type SourceIdentity struct { Protocol envelope.Protocol SourceIP string SourceEndpoint string SourceCode string PlatformName string SourceKind string } func NewSourceIdentity(protocol envelope.Protocol, endpoint string) (SourceIdentity, bool) { sourceIP := NormalizeSourceIP(endpoint) if sourceIP == "" { return SourceIdentity{}, false } return SourceIdentity{ Protocol: protocol, SourceIP: sourceIP, SourceEndpoint: strings.TrimSpace(endpoint), }, true } func NewSourceIdentityFromEnvelope(env envelope.FrameEnvelope) (SourceIdentity, bool) { identity, ok := NewSourceIdentity(env.Protocol, env.SourceEndpoint) if !ok { return SourceIdentity{}, false } identity.SourceCode = strings.TrimSpace(env.SourceCode) identity.PlatformName = strings.TrimSpace(env.PlatformName) identity.SourceKind = normalizeSourceKindForRead(env.SourceKind) return identity, true } func ShouldManageDataSource(identity SourceIdentity) bool { if strings.TrimSpace(identity.SourceIP) == "" { return false } if strings.TrimSpace(identity.SourceCode) != "" || strings.TrimSpace(identity.PlatformName) != "" { return true } return normalizeSourceKindForRead(identity.SourceKind) == "PLATFORM" } func NormalizeSourceIP(endpoint string) string { return envelope.NormalizeSourceEndpointKey(endpoint) } func UpsertDataSource(ctx context.Context, exec Execer, identity SourceIdentity, now time.Time) error { if exec == nil { panic("stats execer must not be nil") } if identity.SourceIP == "" { return nil } if now.IsZero() { now = time.Now() } _, err := exec.ExecContext(ctx, upsertDataSourceSQL, string(identity.Protocol), identity.SourceIP, identity.SourceEndpoint, nullableTrimmedString(identity.PlatformName), nullableTrimmedString(identity.SourceCode), sourceKindForDataSourceWrite(identity), now, now, ) return err } func sourceKindForDataSourceWrite(identity SourceIdentity) string { kind := normalizeSourceKindForWrite(identity.SourceKind) if kind != "UNKNOWN" { return kind } if strings.TrimSpace(identity.SourceCode) != "" || strings.TrimSpace(identity.PlatformName) != "" { return "PLATFORM" } return "UNKNOWN" } const upsertDataSourceSQL = ` INSERT INTO vehicle_data_source (protocol, source_ip, latest_source_endpoint, platform_name, source_code, source_kind, first_seen_at, latest_seen_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE latest_source_endpoint = VALUES(latest_source_endpoint), platform_name = CASE WHEN vehicle_data_source.platform_name IS NULL OR TRIM(vehicle_data_source.platform_name) = '' THEN VALUES(platform_name) ELSE vehicle_data_source.platform_name END, source_code = CASE WHEN vehicle_data_source.source_code IS NULL OR TRIM(vehicle_data_source.source_code) = '' THEN VALUES(source_code) ELSE vehicle_data_source.source_code END, source_kind = CASE WHEN vehicle_data_source.source_kind IS NULL OR TRIM(vehicle_data_source.source_kind) = '' OR vehicle_data_source.source_kind = 'UNKNOWN' THEN VALUES(source_kind) ELSE vehicle_data_source.source_kind END, enabled = CASE WHEN vehicle_data_source.enabled = 0 AND (vehicle_data_source.remark LIKE 'auto-retired:%' OR vehicle_data_source.remark = 'auto-reenabled: source evidence restored') AND ( (VALUES(platform_name) IS NOT NULL AND TRIM(VALUES(platform_name)) <> '') OR (VALUES(source_code) IS NOT NULL AND TRIM(VALUES(source_code)) <> '') OR VALUES(source_kind) <> 'UNKNOWN' ) THEN 1 ELSE vehicle_data_source.enabled END, remark = CASE WHEN vehicle_data_source.enabled = 0 AND (vehicle_data_source.remark LIKE 'auto-retired:%' OR vehicle_data_source.remark = 'auto-reenabled: source evidence restored') AND ( (VALUES(platform_name) IS NOT NULL AND TRIM(VALUES(platform_name)) <> '') OR (VALUES(source_code) IS NOT NULL AND TRIM(VALUES(source_code)) <> '') OR VALUES(source_kind) <> 'UNKNOWN' ) THEN 'auto-reenabled: source evidence restored' ELSE vehicle_data_source.remark END, latest_seen_at = GREATEST(COALESCE(latest_seen_at, VALUES(latest_seen_at)), VALUES(latest_seen_at)), updated_at = CURRENT_TIMESTAMP `