fix(mileage): recover sparse source daily distance

This commit is contained in:
lingniu
2026-07-19 22:57:31 +08:00
parent e45daff528
commit 4c64c4c738
11 changed files with 364 additions and 80 deletions

View File

@@ -99,13 +99,20 @@ func CalculateGPSMileageSegment(previous, current GPSMileagePoint) GPSMileageSeg
}
func GPSMileagePointFromEnvelope(env envelope.FrameEnvelope, identity SourceIdentity, loc *time.Location) (GPSMileagePoint, bool) {
if env.Protocol != envelope.ProtocolJT808 || strings.TrimSpace(env.VIN) == "" {
if !supportsGPSMileageFallback(env.Protocol) || strings.TrimSpace(env.VIN) == "" {
return GPSMileagePoint{}, false
}
location, ok := telemetry.LocationProjectionForProtocol(env.Protocol, env.Fields)
if !ok || !validGPSCoordinate(location.Longitude, location.Latitude) {
return GPSMileagePoint{}, false
}
// JT808 terminals often omit speed together with odometer data, so keep the
// established coordinate-only fallback. GB32960 and Yutong report much more
// frequently; require positive device speed to prevent stationary GPS jitter
// from becoming mileage and to keep the MySQL state path inexpensive.
if env.Protocol != envelope.ProtocolJT808 && (location.SpeedKMH == nil || *location.SpeedKMH <= 0) {
return GPSMileagePoint{}, false
}
eventMS, _, ok := envelope.NormalizedEventTimeMSWithReason(env)
if !ok {
return GPSMileagePoint{}, false
@@ -131,7 +138,16 @@ func GPSMileagePointFromEnvelope(env envelope.FrameEnvelope, identity SourceIden
}, true
}
func AccumulateJT808GPSMileage(ctx context.Context, exec Execer, point GPSMileagePoint) (GPSMileageState, bool, error) {
func supportsGPSMileageFallback(protocol envelope.Protocol) bool {
switch protocol {
case envelope.ProtocolGB32960, envelope.ProtocolJT808, envelope.ProtocolYutongMQTT:
return true
default:
return false
}
}
func AccumulateGPSMileage(ctx context.Context, exec Execer, point GPSMileagePoint) (GPSMileageState, bool, error) {
beginner, ok := exec.(txBeginner)
if !ok || strings.TrimSpace(point.VIN) == "" || strings.TrimSpace(point.SourceKey) == "" || point.EventTime.IsZero() {
return GPSMileageState{}, false, nil
@@ -242,6 +258,12 @@ func AccumulateJT808GPSMileage(ctx context.Context, exec Execer, point GPSMileag
return state, true, nil
}
// AccumulateJT808GPSMileage remains as a compatibility wrapper for callers and
// operational tooling compiled against the original JT808-only fallback.
func AccumulateJT808GPSMileage(ctx context.Context, exec Execer, point GPSMileagePoint) (GPSMileageState, bool, error) {
return AccumulateGPSMileage(ctx, exec, point)
}
func selectExistingGPSMileageBaseline(ctx context.Context, tx *sql.Tx, point GPSMileagePoint) (float64, time.Time, int64, int64, error) {
var mileage sql.NullFloat64
var firstEvent sql.NullTime
@@ -293,7 +315,7 @@ func upsertGPSMileageCandidate(ctx context.Context, exec Execer, point GPSMileag
point.SourceEndpoint,
point.Phone,
point.DeviceID,
"JT808 GPS轨迹估算",
gpsMileagePlatformName(point.Protocol),
0,
state.DailyMileageKM,
state.DailyMileageKM,
@@ -306,6 +328,17 @@ func upsertGPSMileageCandidate(ctx context.Context, exec Execer, point GPSMileag
return err
}
func gpsMileagePlatformName(protocol envelope.Protocol) string {
switch protocol {
case envelope.ProtocolGB32960:
return "GB32960 GPS轨迹估算"
case envelope.ProtocolYutongMQTT:
return "宇通 GPS轨迹估算"
default:
return "JT808 GPS轨迹估算"
}
}
func gpsMileageCandidateSourceKey(point GPSMileagePoint) string {
identity := strings.TrimSpace(point.Phone)
if identity == "" {