package envelope import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "strings" ) type Protocol string const ( ProtocolGB32960 Protocol = "GB32960" ProtocolJT808 Protocol = "JT808" ProtocolYutongMQTT Protocol = "YUTONG_MQTT" ) type ParseStatus string const ( ParseOK ParseStatus = "OK" ParsePartial ParseStatus = "PARTIAL" ParseBadFrame ParseStatus = "BAD_FRAME" ) type EventKind string const ( EventKindRaw EventKind = "RAW" EventKindFields EventKind = "FIELDS" EventKindUnified EventKind = "UNIFIED" ) const ( FieldSpeedKMH = "speed_kmh" FieldTotalMileageKM = "total_mileage_km" FieldLongitude = "longitude" FieldLatitude = "latitude" FieldSOCPercent = "soc_percent" ) type FrameEnvelope struct { EventID string `json:"event_id"` TraceID string `json:"trace_id"` EventKind EventKind `json:"event_kind,omitempty"` SourceEventID string `json:"source_event_id,omitempty"` FieldMapping string `json:"field_mapping,omitempty"` Protocol Protocol `json:"protocol"` MessageID string `json:"message_id"` Sequence uint16 `json:"sequence"` VIN string `json:"vin,omitempty"` VehicleKeyHint string `json:"vehicle_key,omitempty"` Phone string `json:"phone,omitempty"` DeviceID string `json:"device_id,omitempty"` Plate string `json:"plate,omitempty"` SourceEndpoint string `json:"source_endpoint,omitempty"` SourceCode string `json:"source_code,omitempty"` PlatformName string `json:"platform_name,omitempty"` SourceKind string `json:"source_kind,omitempty"` AuthenticationMode string `json:"authentication_mode,omitempty"` AuthenticationStatus string `json:"authentication_status,omitempty"` AuthenticationEnforced bool `json:"authentication_enforced,omitempty"` EventTimeMS int64 `json:"event_time_ms"` ReceivedAtMS int64 `json:"received_at_ms"` RawHex string `json:"raw_hex,omitempty"` RawText string `json:"raw_text,omitempty"` Parsed map[string]any `json:"parsed,omitempty"` ParsedFields map[string]any `json:"parsed_fields,omitempty"` ParsedFieldTypes map[string]string `json:"parsed_field_types,omitempty"` Fields map[string]any `json:"fields,omitempty"` ParseStatus ParseStatus `json:"parse_status"` ParseError string `json:"parse_error,omitempty"` } func (e FrameEnvelope) VehicleKey() string { if key := strings.TrimSpace(e.VIN); key != "" { return key } if key := strings.TrimSpace(e.Phone); key != "" { return string(e.Protocol) + ":" + key } if key := strings.TrimSpace(e.VehicleKeyHint); key != "" { return key } if key := strings.TrimSpace(e.DeviceID); key != "" { return string(e.Protocol) + ":" + key } return string(e.Protocol) + ":unknown" } func (e FrameEnvelope) StableEventID() string { if strings.TrimSpace(e.EventID) != "" { return e.EventID } input := fmt.Sprintf("%s|%s|%s|%d|%d|%s", e.Protocol, e.MessageID, e.VehicleKey(), e.Sequence, e.EventTimeMS, e.RawHex) sum := sha256.Sum256([]byte(input)) return hex.EncodeToString(sum[:16]) } func (e FrameEnvelope) KafkaKey() []byte { return []byte(e.VehicleKey()) } func (e FrameEnvelope) MarshalJSONBytes() ([]byte, error) { if e.EventID == "" { e.EventID = e.StableEventID() } if e.ParseStatus == "" { e.ParseStatus = ParseOK } if e.EventKind == "" { e.EventKind = EventKindRaw } return json.Marshal(e) }