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" ) 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"` 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"` 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"` 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.VehicleKeyHint); key != "" { return key } if key := strings.TrimSpace(e.Phone); key != "" { return string(e.Protocol) + ":" + 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 } return json.Marshal(e) }