175 lines
5.1 KiB
Go
175 lines
5.1 KiB
Go
package platform
|
|
|
|
import (
|
|
"context"
|
|
"net/url"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
type Store interface {
|
|
DashboardSummary(context.Context) (DashboardSummary, error)
|
|
Vehicles(context.Context, url.Values) (Page[VehicleRow], error)
|
|
RealtimeLocations(context.Context, url.Values) (Page[RealtimeLocationRow], error)
|
|
HistoryLocations(context.Context, url.Values) (Page[HistoryLocationRow], error)
|
|
HistoryLocationsFromTDengine(context.Context, url.Values) (Page[HistoryLocationRow], error)
|
|
RawFrames(context.Context, RawFrameQuery) (Page[RawFrameRow], error)
|
|
DailyMileage(context.Context, url.Values) (Page[DailyMileageRow], error)
|
|
QualityIssues(context.Context, url.Values) (Page[QualityIssueRow], error)
|
|
OpsHealth(context.Context) (OpsHealth, error)
|
|
}
|
|
|
|
type RawFrameQuery struct {
|
|
Protocol string `json:"protocol"`
|
|
VIN string `json:"vin"`
|
|
DateFrom string `json:"dateFrom"`
|
|
DateTo string `json:"dateTo"`
|
|
Fields []string `json:"fields"`
|
|
IncludeFields bool `json:"includeFields"`
|
|
Limit int `json:"limit"`
|
|
Offset int `json:"offset"`
|
|
}
|
|
|
|
type Service struct {
|
|
store Store
|
|
}
|
|
|
|
func NewService(store Store) *Service {
|
|
return &Service{store: store}
|
|
}
|
|
|
|
func (s *Service) DashboardSummary(ctx context.Context) (DashboardSummary, error) {
|
|
return s.store.DashboardSummary(ctx)
|
|
}
|
|
|
|
func (s *Service) Vehicles(ctx context.Context, query url.Values) (Page[VehicleRow], error) {
|
|
return s.store.Vehicles(ctx, query)
|
|
}
|
|
|
|
func (s *Service) VehicleDetail(ctx context.Context, vin string, protocol string) (VehicleDetail, error) {
|
|
keyword := strings.TrimSpace(vin)
|
|
protocol = strings.TrimSpace(protocol)
|
|
vehicleQuery := url.Values{"keyword": {keyword}, "limit": {"10"}}
|
|
vehicles, err := s.store.Vehicles(ctx, vehicleQuery)
|
|
if err != nil {
|
|
return VehicleDetail{}, err
|
|
}
|
|
identity := resolveVehicleIdentity(keyword, vehicles.Items)
|
|
resolvedVIN := keyword
|
|
if identity != nil && strings.TrimSpace(identity.VIN) != "" {
|
|
resolvedVIN = identity.VIN
|
|
}
|
|
|
|
realtimeQuery := url.Values{"vin": {resolvedVIN}, "limit": {"20"}}
|
|
historyQuery := url.Values{"vin": {resolvedVIN}, "limit": {"20"}}
|
|
rawQuery := RawFrameQuery{VIN: resolvedVIN, IncludeFields: true, Limit: 10}
|
|
mileageQuery := url.Values{"vin": {resolvedVIN}, "limit": {"20"}}
|
|
if protocol != "" {
|
|
realtimeQuery.Set("protocol", protocol)
|
|
historyQuery.Set("protocol", protocol)
|
|
rawQuery.Protocol = protocol
|
|
}
|
|
realtime, err := s.store.RealtimeLocations(ctx, realtimeQuery)
|
|
if err != nil {
|
|
return VehicleDetail{}, err
|
|
}
|
|
history, err := s.store.HistoryLocationsFromTDengine(ctx, historyQuery)
|
|
if err != nil {
|
|
return VehicleDetail{}, err
|
|
}
|
|
raw, err := s.RawFrames(ctx, rawQuery)
|
|
if err != nil {
|
|
return VehicleDetail{}, err
|
|
}
|
|
mileage, err := s.store.DailyMileage(ctx, mileageQuery)
|
|
if err != nil {
|
|
return VehicleDetail{}, err
|
|
}
|
|
return VehicleDetail{
|
|
VIN: resolvedVIN,
|
|
Identity: identity,
|
|
Sources: vehicleSources(vehicles.Items, realtime.Items, raw.Items, mileage.Items),
|
|
Realtime: realtime.Items,
|
|
History: history,
|
|
Raw: raw,
|
|
Mileage: mileage,
|
|
}, nil
|
|
}
|
|
|
|
func resolveVehicleIdentity(keyword string, vehicles []VehicleRow) *VehicleRow {
|
|
keyword = strings.TrimSpace(keyword)
|
|
if keyword == "" {
|
|
return nil
|
|
}
|
|
for index := range vehicles {
|
|
if strings.EqualFold(vehicles[index].VIN, keyword) {
|
|
return &vehicles[index]
|
|
}
|
|
}
|
|
for index := range vehicles {
|
|
if strings.EqualFold(vehicles[index].Plate, keyword) || strings.EqualFold(vehicles[index].Phone, keyword) {
|
|
return &vehicles[index]
|
|
}
|
|
}
|
|
if len(vehicles) > 0 {
|
|
return &vehicles[0]
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *Service) RealtimeLocations(ctx context.Context, query url.Values) (Page[RealtimeLocationRow], error) {
|
|
return s.store.RealtimeLocations(ctx, query)
|
|
}
|
|
|
|
func (s *Service) HistoryLocations(ctx context.Context, query url.Values) (Page[HistoryLocationRow], error) {
|
|
return s.store.HistoryLocationsFromTDengine(ctx, query)
|
|
}
|
|
|
|
func (s *Service) RawFrames(ctx context.Context, query RawFrameQuery) (Page[RawFrameRow], error) {
|
|
if query.Limit <= 0 || query.Limit > 500 {
|
|
query.Limit = 100
|
|
}
|
|
return s.store.RawFrames(ctx, query)
|
|
}
|
|
|
|
func (s *Service) DailyMileage(ctx context.Context, query url.Values) (Page[DailyMileageRow], error) {
|
|
return s.store.DailyMileage(ctx, query)
|
|
}
|
|
|
|
func (s *Service) QualityIssues(ctx context.Context, query url.Values) (Page[QualityIssueRow], error) {
|
|
return s.store.QualityIssues(ctx, query)
|
|
}
|
|
|
|
func (s *Service) OpsHealth(ctx context.Context) (OpsHealth, error) {
|
|
return s.store.OpsHealth(ctx)
|
|
}
|
|
|
|
func vehicleSources(vehicles []VehicleRow, realtime []RealtimeLocationRow, raw []RawFrameRow, mileage []DailyMileageRow) []string {
|
|
seen := map[string]struct{}{}
|
|
add := func(value string) {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return
|
|
}
|
|
seen[value] = struct{}{}
|
|
}
|
|
for _, row := range vehicles {
|
|
add(row.Protocol)
|
|
}
|
|
for _, row := range realtime {
|
|
add(row.Protocol)
|
|
}
|
|
for _, row := range raw {
|
|
add(row.Protocol)
|
|
}
|
|
for _, row := range mileage {
|
|
add(row.Source)
|
|
}
|
|
out := make([]string, 0, len(seen))
|
|
for source := range seen {
|
|
out = append(out, source)
|
|
}
|
|
sort.Strings(out)
|
|
return out
|
|
}
|