152 lines
4.6 KiB
Go
152 lines
4.6 KiB
Go
package stats
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"time"
|
|
|
|
"lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope"
|
|
)
|
|
|
|
const (
|
|
QualityOK = "OK"
|
|
QualityNoPreviousBaseline = "NO_PREVIOUS_BASELINE"
|
|
QualityInvalidDelta = "INVALID_DELTA"
|
|
)
|
|
|
|
type SourceMileageSample struct {
|
|
VIN string
|
|
StatDate string
|
|
Protocol envelope.Protocol
|
|
SourceKey string
|
|
SourceIP string
|
|
SourceEndpoint string
|
|
Phone string
|
|
DeviceID string
|
|
PlatformName string
|
|
FirstTotalKM float64
|
|
LatestTotalKM float64
|
|
DailyKM float64
|
|
SampleCount int64
|
|
FirstEventTime time.Time
|
|
LatestEventTime time.Time
|
|
QualityStatus string
|
|
QualityReason string
|
|
}
|
|
|
|
func SourceKey(protocol envelope.Protocol, phone string, deviceID string, sourceIP string) string {
|
|
identity := strings.TrimSpace(phone)
|
|
if identity == "" {
|
|
identity = strings.TrimSpace(deviceID)
|
|
}
|
|
if identity == "" {
|
|
identity = "unknown"
|
|
}
|
|
return string(protocol) + ":" + identity + "@" + strings.TrimSpace(sourceIP)
|
|
}
|
|
|
|
func SourceMileageSampleFromMetric(sample MetricSample, identity SourceIdentity) SourceMileageSample {
|
|
eventTime := sample.EventTime
|
|
return SourceMileageSample{
|
|
VIN: sample.VIN,
|
|
StatDate: sample.StatDate,
|
|
Protocol: sample.Protocol,
|
|
SourceKey: SourceKey(sample.Protocol, sample.Phone, sample.DeviceID, identity.SourceIP),
|
|
SourceIP: identity.SourceIP,
|
|
SourceEndpoint: identity.SourceEndpoint,
|
|
Phone: sample.Phone,
|
|
DeviceID: sample.DeviceID,
|
|
FirstTotalKM: sample.TotalMileageKM,
|
|
LatestTotalKM: sample.TotalMileageKM,
|
|
DailyKM: 0,
|
|
SampleCount: 1,
|
|
FirstEventTime: eventTime,
|
|
LatestEventTime: eventTime,
|
|
QualityStatus: QualityOK,
|
|
QualityReason: "realtime_sample",
|
|
}
|
|
}
|
|
|
|
func UpsertSourceMileage(ctx context.Context, exec Execer, sample SourceMileageSample) error {
|
|
if exec == nil {
|
|
panic("stats execer must not be nil")
|
|
}
|
|
if sample.VIN == "" || sample.SourceKey == "" {
|
|
return nil
|
|
}
|
|
if sample.QualityStatus == "" {
|
|
sample.QualityStatus = QualityOK
|
|
}
|
|
_, err := exec.ExecContext(ctx, upsertSourceMileageSQL,
|
|
sample.VIN,
|
|
sample.StatDate,
|
|
string(sample.Protocol),
|
|
sample.SourceKey,
|
|
sample.SourceIP,
|
|
sample.SourceEndpoint,
|
|
sample.Phone,
|
|
sample.DeviceID,
|
|
sample.PlatformName,
|
|
sample.FirstTotalKM,
|
|
sample.LatestTotalKM,
|
|
sample.DailyKM,
|
|
sample.SampleCount,
|
|
sample.FirstEventTime,
|
|
sample.LatestEventTime,
|
|
sample.QualityStatus,
|
|
sample.QualityReason,
|
|
)
|
|
return err
|
|
}
|
|
|
|
const upsertSourceMileageSQL = `
|
|
INSERT INTO vehicle_daily_mileage_source
|
|
(vin, stat_date, protocol, source_key, source_ip, source_endpoint, phone, device_id, platform_name,
|
|
first_total_mileage_km, latest_total_mileage_km, daily_mileage_km, sample_count,
|
|
first_event_time, latest_event_time, quality_status, quality_reason)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
ON DUPLICATE KEY UPDATE
|
|
source_ip = VALUES(source_ip),
|
|
source_endpoint = VALUES(source_endpoint),
|
|
phone = VALUES(phone),
|
|
device_id = VALUES(device_id),
|
|
platform_name = COALESCE(VALUES(platform_name), platform_name),
|
|
first_total_mileage_km = CASE
|
|
WHEN first_total_mileage_km IS NULL OR first_total_mileage_km <= 0
|
|
THEN VALUES(first_total_mileage_km)
|
|
ELSE LEAST(first_total_mileage_km, VALUES(first_total_mileage_km))
|
|
END,
|
|
latest_total_mileage_km = CASE
|
|
WHEN latest_total_mileage_km IS NULL OR latest_total_mileage_km <= 0
|
|
THEN VALUES(latest_total_mileage_km)
|
|
ELSE GREATEST(latest_total_mileage_km, VALUES(latest_total_mileage_km))
|
|
END,
|
|
/* daily_mileage_km = VALUES(daily_mileage_km) */
|
|
daily_mileage_km = GREATEST(
|
|
CASE
|
|
WHEN latest_total_mileage_km IS NULL OR latest_total_mileage_km <= 0
|
|
THEN VALUES(latest_total_mileage_km)
|
|
ELSE latest_total_mileage_km
|
|
END,
|
|
VALUES(latest_total_mileage_km)
|
|
) - CASE
|
|
WHEN first_total_mileage_km IS NULL OR first_total_mileage_km <= 0
|
|
THEN VALUES(first_total_mileage_km)
|
|
ELSE LEAST(first_total_mileage_km, VALUES(first_total_mileage_km))
|
|
END,
|
|
sample_count = sample_count + VALUES(sample_count),
|
|
first_event_time = CASE
|
|
WHEN first_event_time IS NULL OR VALUES(first_event_time) < first_event_time
|
|
THEN VALUES(first_event_time)
|
|
ELSE first_event_time
|
|
END,
|
|
latest_event_time = CASE
|
|
WHEN latest_event_time IS NULL OR VALUES(latest_event_time) > latest_event_time
|
|
THEN VALUES(latest_event_time)
|
|
ELSE latest_event_time
|
|
END,
|
|
quality_status = VALUES(quality_status),
|
|
quality_reason = VALUES(quality_reason),
|
|
updated_at = CURRENT_TIMESTAMP
|
|
`
|