package feichibridge import ( "encoding/json" "fmt" "strconv" "strings" "time" ) var shanghai = time.FixedZone("Asia/Shanghai", 8*60*60) type Vehicle struct { VehicleID string `json:"vehicleId"` VIN string `json:"vin"` OnlineStatus any `json:"onlineStatus"` UpdateTime string `json:"updateTime"` RuleTypeName string `json:"ruleTypeName"` } type Snapshot struct { VehicleID string `json:"vehicleId"` VIN string `json:"vin"` DataItems map[string]string `json:"dataItems"` } type Record map[string]string type collectionEnvelope[T any] struct { Code any `json:"code"` Data []T `json:"data"` } type objectEnvelope[T any] struct { Code any `json:"code"` Data T `json:"data"` } type historySegment struct { Subdata []Record `json:"subdata"` } func apiCodeOK(code any) bool { switch value := code.(type) { case nil: return true case float64: return value == 0 || value == 200 case string: return value == "" || value == "0" || value == "200" default: return false } } func recordTime(record Record) (time.Time, error) { for _, key := range []string{"2000", "9999", "dataTime", "updateTime", "time"} { if raw := strings.TrimSpace(record[key]); raw != "" { return parseSourceTime(raw) } } return time.Time{}, fmt.Errorf("source record has no timestamp") } func parseSourceTime(raw string) (time.Time, error) { raw = strings.TrimSpace(raw) for _, layout := range []string{ "2006-01-02 15:04:05", "2006-01-02T15:04:05", time.RFC3339Nano, } { if layout == time.RFC3339Nano { if parsed, err := time.Parse(layout, raw); err == nil { return parsed, nil } continue } if parsed, err := time.ParseInLocation(layout, raw, shanghai); err == nil { return parsed, nil } } if unixMS, err := strconv.ParseInt(raw, 10, 64); err == nil { if unixMS < 10_000_000_000 { return time.Unix(unixMS, 0).In(shanghai), nil } return time.UnixMilli(unixMS).In(shanghai), nil } return time.Time{}, fmt.Errorf("unsupported source timestamp %q", raw) } func canonicalHash(record Record) string { encoded, _ := json.Marshal(record) return hashBytes(encoded) }