Files
lingniu-vehicle-ingest/go/vehicle-gateway/internal/stats/source.go

137 lines
4.3 KiB
Go

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
`