feat: expand vehicle data platform capabilities

This commit is contained in:
lingniu
2026-07-27 16:46:15 +08:00
parent e3a1f80f86
commit 3c4bece72c
650 changed files with 62155 additions and 2552 deletions

View File

@@ -4,6 +4,7 @@ import (
"encoding/json"
"net/http"
"net/url"
"strconv"
"strings"
"time"
@@ -31,6 +32,7 @@ func (h *Handler) routes() {
h.mux.HandleFunc("GET /api/vehicles/resolve", h.handleVehicleResolve)
h.mux.HandleFunc("GET /api/vehicles/coverage", h.handleVehicleCoverage)
h.mux.HandleFunc("GET /api/vehicles/coverage/summary", h.handleVehicleCoverageSummary)
h.mux.HandleFunc("GET /api/vehicles/business-filters", h.handleVehicleBusinessFilters)
h.mux.HandleFunc("GET /api/vehicle-service", h.handleVehicleDetail)
h.mux.HandleFunc("GET /api/vehicle-service/summary", h.handleVehicleServiceSummary)
h.mux.HandleFunc("GET /api/vehicle-service/overview", h.handleVehicleServiceOverview)
@@ -43,7 +45,9 @@ func (h *Handler) routes() {
h.mux.HandleFunc("POST /api/history/raw-frames/query", h.handleRawFramesPost)
h.mux.HandleFunc("GET /api/mileage/summary", h.handleMileageSummary)
h.mux.HandleFunc("GET /api/mileage/daily", h.handleDailyMileage)
h.mux.HandleFunc("POST /api/mileage/daily", h.handleDailyMileagePost)
h.mux.HandleFunc("GET /api/v2/statistics/mileage", h.handleMileageStatistics)
h.mux.HandleFunc("POST /api/v2/statistics/mileage", h.handleMileageStatisticsPost)
h.mux.HandleFunc("GET /api/statistics/online-summary", h.handleOnlineStatisticsSummary)
h.mux.HandleFunc("GET /api/statistics/online-vehicles", h.handleOnlineVehicleStatuses)
h.mux.HandleFunc("GET /api/quality/summary", h.handleQualitySummary)
@@ -64,21 +68,48 @@ func (h *Handler) routes() {
h.mux.HandleFunc("GET /api/v2/operations/vehicles/{vin}/sources", h.handleVehicleSourceDiagnostic)
h.mux.HandleFunc("PUT /api/v2/operations/vehicles/{vin}/sources/{sourceRef}", h.handleUpdateVehicleSourcePolicy)
h.mux.HandleFunc("GET /api/v2/reconciliation/summary", h.handleReconciliationSummary)
h.mux.HandleFunc("GET /api/v2/reconciliation/assignees", h.handleReconciliationAssignees)
h.mux.HandleFunc("POST /api/v2/reconciliation/issues", h.handleReconciliationIssues)
h.mux.HandleFunc("POST /api/v2/reconciliation/issues/export", h.handleReconciliationIssuesExport)
h.mux.HandleFunc("POST /api/v2/reconciliation/issues/batch-actions", h.handleReconciliationBatchAction)
h.mux.HandleFunc("POST /api/v2/reconciliation/issues/batch-assignments", h.handleReconciliationBatchAssignment)
h.mux.HandleFunc("POST /api/v2/reconciliation/issues/batch-archive", h.handleReconciliationBatchArchive)
h.mux.HandleFunc("POST /api/v2/reconciliation/issues/batch-restore", h.handleReconciliationBatchRestore)
h.mux.HandleFunc("GET /api/v2/reconciliation/issues/{id}", h.handleReconciliationIssue)
h.mux.HandleFunc("POST /api/v2/reconciliation/issues/{id}/actions", h.handleReconciliationAction)
h.mux.HandleFunc("POST /api/v2/reconciliation/issues/{id}/assignment", h.handleReconciliationAssignment)
h.mux.HandleFunc("POST /api/v2/reconciliation/issues/{id}/archive", h.handleReconciliationArchive)
h.mux.HandleFunc("POST /api/v2/reconciliation/issues/{id}/restore", h.handleReconciliationRestore)
h.mux.HandleFunc("POST /api/v2/vehicle-profiles/sync", h.handleSyncVehicleProfiles)
h.mux.HandleFunc("GET /api/v2/tracks", h.handleTrackPlayback)
h.mux.HandleFunc("GET /api/v2/metrics", h.handleMetricCatalog)
h.mux.HandleFunc("GET /api/v2/history/metrics", h.handleHistoryMetricCatalog)
h.mux.HandleFunc("GET /api/v2/history/query", h.handleHistoryData)
h.mux.HandleFunc("GET /api/v2/history/series", h.handleHistorySeries)
h.mux.HandleFunc("GET /api/v2/history/preferences", h.handleHistoryPreferences)
h.mux.HandleFunc("PUT /api/v2/history/preferences", h.handleUpdateHistoryPreferences)
h.mux.HandleFunc("POST /api/v2/exports", h.handleCreateHistoryExport)
h.mux.HandleFunc("GET /api/v2/exports", h.handleListHistoryExports)
h.mux.HandleFunc("GET /api/v2/exports/page", h.handleListHistoryExportsPage)
h.mux.HandleFunc("GET /api/v2/exports/cleanup/preview", h.handlePreviewHistoryExportCleanup)
h.mux.HandleFunc("GET /api/v2/exports/cleanup/audit", h.handleHistoryExportCleanupAudit)
h.mux.HandleFunc("POST /api/v2/exports/cleanup", h.handleCleanupHistoryExports)
h.mux.HandleFunc("GET /api/v2/exports/cleanup/automation", h.handleHistoryExportCleanupAutomation)
h.mux.HandleFunc("PUT /api/v2/exports/cleanup/automation", h.handleUpdateHistoryExportCleanupAutomation)
h.mux.HandleFunc("POST /api/v2/exports/cleanup/automation/{id}/approve", h.handleApproveHistoryExportCleanupAutomation)
h.mux.HandleFunc("POST /api/v2/exports/cleanup/automation/{id}/reject", h.handleRejectHistoryExportCleanupAutomation)
h.mux.HandleFunc("POST /api/v2/exports/cleanup/automation/{id}/retry", h.handleRetryHistoryExportCleanupAutomation)
h.mux.HandleFunc("POST /api/v2/exports/batch", h.handleBatchHistoryExports)
h.mux.HandleFunc("POST /api/v2/exports/{id}/cancel", h.handleCancelHistoryExport)
h.mux.HandleFunc("POST /api/v2/exports/{id}/rebuild", h.handleRebuildHistoryExport)
h.mux.HandleFunc("POST /api/v2/exports/{id}/archive", h.handleArchiveHistoryExport)
h.mux.HandleFunc("POST /api/v2/exports/{id}/restore", h.handleRestoreHistoryExport)
h.mux.HandleFunc("PUT /api/v2/exports/{id}/cleanup-protection", h.handleHistoryExportCleanupProtection)
h.mux.HandleFunc("GET /api/v2/exports/{id}/download", h.handleDownloadHistoryExport)
h.mux.HandleFunc("POST /api/v2/access/summary", h.handleAccessSummary)
h.mux.HandleFunc("POST /api/v2/access/vehicles", h.handleAccessVehicles)
h.mux.HandleFunc("POST /api/v2/access/unresolved-identities", h.handleAccessUnresolvedIdentities)
h.mux.HandleFunc("POST /api/v2/access/unresolved-identities/{id}/claim", h.handleClaimAccessIdentity)
h.mux.HandleFunc("GET /api/v2/access/thresholds", h.handleAccessThresholds)
h.mux.HandleFunc("PUT /api/v2/access/thresholds", h.handleUpdateAccessThresholds)
h.mux.HandleFunc("POST /api/v2/alerts/summary", h.handleAlertSummary)
@@ -86,11 +117,30 @@ func (h *Handler) routes() {
h.mux.HandleFunc("GET /api/v2/alerts/events/{id}", h.handleAlertEvent)
h.mux.HandleFunc("POST /api/v2/alerts/events/{id}/actions", h.handleAlertAction)
h.mux.HandleFunc("GET /api/v2/alerts/rules", h.handleAlertRules)
h.mux.HandleFunc("GET /api/v2/alerts/rules/library", h.handleAlertRuleLibrary)
h.mux.HandleFunc("POST /api/v2/alerts/rules", h.handleSaveAlertRule)
h.mux.HandleFunc("PUT /api/v2/alerts/rules/{id}", h.handleSaveAlertRule)
h.mux.HandleFunc("PUT /api/v2/alerts/rules/{id}/enabled", h.handleAlertRuleEnabled)
h.mux.HandleFunc("GET /api/v2/alerts/rules/{id}/revisions", h.handleAlertRuleRevisions)
h.mux.HandleFunc("POST /api/v2/alerts/rules/{id}/rollback", h.handleAlertRuleRollback)
h.mux.HandleFunc("POST /api/v2/alerts/rules/{id}/archive", h.handleArchiveAlertRule)
h.mux.HandleFunc("POST /api/v2/alerts/rules/{id}/restore", h.handleRestoreAlertRule)
h.mux.HandleFunc("GET /api/v2/alerts/notification-config", h.handleAlertNotificationConfig)
h.mux.HandleFunc("GET /api/v2/alerts/notifications/health", h.handleAlertNotificationDeliveryHealth)
h.mux.HandleFunc("GET /api/v2/alerts/notifications", h.handleAlertNotifications)
h.mux.HandleFunc("POST /api/v2/alerts/notifications/read", h.handleAlertNotificationsRead)
h.mux.HandleFunc("POST /api/v2/alerts/notifications/{id}/retry", h.handleAlertNotificationRetry)
h.mux.HandleFunc("GET /api/v2/alerts/notifications/{id}/retry-audit", h.handleAlertNotificationRetryAudit)
}
func (h *Handler) handleAlertNotificationConfig(w http.ResponseWriter, r *http.Request) {
data, err := h.service.AlertNotificationConfig(r.Context())
h.write(w, r, data, err)
}
func (h *Handler) handleAlertNotificationDeliveryHealth(w http.ResponseWriter, r *http.Request) {
data, err := h.service.AlertNotificationDeliveryHealth(r.Context())
h.write(w, r, data, err)
}
func (h *Handler) handleReconciliationSummary(w http.ResponseWriter, r *http.Request) {
@@ -107,6 +157,29 @@ func (h *Handler) handleReconciliationIssues(w http.ResponseWriter, r *http.Requ
h.write(w, r, data, err)
}
func (h *Handler) handleReconciliationAssignees(w http.ResponseWriter, r *http.Request) {
data, err := h.service.ReconciliationAssignees(r.Context(), r.URL.Query().Get("search"))
h.write(w, r, data, err)
}
func (h *Handler) handleReconciliationIssuesExport(w http.ResponseWriter, r *http.Request) {
var query ReconciliationQuery
if !decodeJSONBody(w, r, &query) {
return
}
file, err := h.service.ExportReconciliationIssues(r.Context(), query)
if err != nil {
h.write(w, r, nil, err)
return
}
w.Header().Set("Content-Type", "text/csv; charset=utf-8")
w.Header().Set("Content-Disposition", `attachment; filename="quality-issues.csv"; filename*=UTF-8''`+url.PathEscape(file.Name))
w.Header().Set("X-Export-Name", url.PathEscape(file.Name))
w.Header().Set("X-Export-Count", strconv.Itoa(file.RowCount))
w.WriteHeader(http.StatusOK)
_, _ = w.Write(file.Content)
}
func (h *Handler) handleReconciliationIssue(w http.ResponseWriter, r *http.Request) {
data, err := h.service.ReconciliationIssue(r.Context(), r.PathValue("id"))
h.write(w, r, data, err)
@@ -121,6 +194,67 @@ func (h *Handler) handleReconciliationAction(w http.ResponseWriter, r *http.Requ
h.write(w, r, data, err)
}
func (h *Handler) handleReconciliationAssignment(w http.ResponseWriter, r *http.Request) {
var request ReconciliationAssignmentRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.AssignReconciliationIssue(r.Context(), r.PathValue("id"), request)
h.write(w, r, data, err)
}
func (h *Handler) handleReconciliationBatchAssignment(w http.ResponseWriter, r *http.Request) {
var request ReconciliationBatchAssignmentRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.BatchAssignReconciliationIssues(r.Context(), request)
h.write(w, r, data, err)
}
func (h *Handler) handleReconciliationBatchAction(w http.ResponseWriter, r *http.Request) {
var request ReconciliationBatchActionRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.BatchUpdateReconciliationIssues(r.Context(), request)
h.write(w, r, data, err)
}
func (h *Handler) handleReconciliationArchive(w http.ResponseWriter, r *http.Request) {
h.handleReconciliationLifecycle(w, r, true)
}
func (h *Handler) handleReconciliationRestore(w http.ResponseWriter, r *http.Request) {
h.handleReconciliationLifecycle(w, r, false)
}
func (h *Handler) handleReconciliationLifecycle(w http.ResponseWriter, r *http.Request, archived bool) {
var request ReconciliationLifecycleRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.SetReconciliationIssueArchived(r.Context(), r.PathValue("id"), archived, request)
h.write(w, r, data, err)
}
func (h *Handler) handleReconciliationBatchArchive(w http.ResponseWriter, r *http.Request) {
h.handleReconciliationBatchLifecycle(w, r, true)
}
func (h *Handler) handleReconciliationBatchRestore(w http.ResponseWriter, r *http.Request) {
h.handleReconciliationBatchLifecycle(w, r, false)
}
func (h *Handler) handleReconciliationBatchLifecycle(w http.ResponseWriter, r *http.Request, archived bool) {
var request ReconciliationBatchLifecycleRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.BatchSetReconciliationIssuesArchived(r.Context(), archived, request)
h.write(w, r, data, err)
}
func (h *Handler) handleAccessUnresolvedIdentities(w http.ResponseWriter, r *http.Request) {
var query AccessUnresolvedIdentityQuery
if !decodeJSONBody(w, r, &query) {
@@ -130,6 +264,16 @@ func (h *Handler) handleAccessUnresolvedIdentities(w http.ResponseWriter, r *htt
h.write(w, r, data, err)
}
func (h *Handler) handleClaimAccessIdentity(w http.ResponseWriter, r *http.Request) {
var input AccessIdentityClaimInput
if !decodeJSONBody(w, r, &input) {
return
}
input.Actor = ActorFromContext(r.Context())
data, err := h.service.ClaimAccessIdentity(r.Context(), r.PathValue("id"), input)
h.write(w, r, data, err)
}
func (h *Handler) handleVehicleProfile(w http.ResponseWriter, r *http.Request) {
data, err := h.service.VehicleProfile(r.Context(), r.PathValue("vin"))
h.write(w, r, data, err)
@@ -224,6 +368,16 @@ func (h *Handler) handleAlertRules(w http.ResponseWriter, r *http.Request) {
h.write(w, r, data, err)
}
func (h *Handler) handleAlertRuleLibrary(w http.ResponseWriter, r *http.Request) {
query := AlertRuleQuery{
Keyword: r.URL.Query().Get("keyword"), Status: r.URL.Query().Get("status"),
Protocol: r.URL.Query().Get("protocol"), Lifecycle: r.URL.Query().Get("lifecycle"),
Limit: parsePositive(r.URL.Query().Get("limit"), 10), Offset: parsePositive(r.URL.Query().Get("offset"), 0),
}
data, err := h.service.AlertRulePage(r.Context(), query)
h.write(w, r, data, err)
}
func (h *Handler) handleSaveAlertRule(w http.ResponseWriter, r *http.Request) {
var input AlertRuleInput
if !decodeJSONBody(w, r, &input) {
@@ -247,11 +401,52 @@ func (h *Handler) handleAlertRuleEnabled(w http.ResponseWriter, r *http.Request)
h.write(w, r, data, err)
}
func (h *Handler) handleAlertRuleRevisions(w http.ResponseWriter, r *http.Request) {
data, err := h.service.AlertRuleRevisions(r.Context(), r.PathValue("id"))
h.write(w, r, data, err)
}
func (h *Handler) handleAlertRuleRollback(w http.ResponseWriter, r *http.Request) {
var request AlertRuleRollbackRequest
if !decodeJSONBody(w, r, &request) {
return
}
request.Actor = ActorFromContext(r.Context())
data, err := h.service.RollbackAlertRule(r.Context(), r.PathValue("id"), request)
h.write(w, r, data, err)
}
func (h *Handler) handleArchiveAlertRule(w http.ResponseWriter, r *http.Request) {
var request AlertRuleLifecycleRequest
if !decodeJSONBody(w, r, &request) {
return
}
request.Actor = ActorFromContext(r.Context())
data, err := h.service.SetAlertRuleArchived(r.Context(), r.PathValue("id"), true, request)
h.write(w, r, data, err)
}
func (h *Handler) handleRestoreAlertRule(w http.ResponseWriter, r *http.Request) {
var request AlertRuleLifecycleRequest
if !decodeJSONBody(w, r, &request) {
return
}
request.Actor = ActorFromContext(r.Context())
data, err := h.service.SetAlertRuleArchived(r.Context(), r.PathValue("id"), false, request)
h.write(w, r, data, err)
}
func (h *Handler) handleAlertNotifications(w http.ResponseWriter, r *http.Request) {
limit := parsePositive(r.URL.Query().Get("limit"), 20)
offset := parsePositive(r.URL.Query().Get("offset"), 0)
unread := strings.EqualFold(r.URL.Query().Get("unreadOnly"), "true") || r.URL.Query().Get("unreadOnly") == "1"
data, err := h.service.AlertNotifications(r.Context(), AlertNotificationQuery{UnreadOnly: unread, Limit: limit, Offset: offset})
data, err := h.service.AlertNotifications(r.Context(), AlertNotificationQuery{
UnreadOnly: unread,
Search: strings.TrimSpace(r.URL.Query().Get("search")),
DeliveryStatus: strings.TrimSpace(r.URL.Query().Get("deliveryStatus")),
Limit: limit,
Offset: offset,
})
h.write(w, r, data, err)
}
@@ -265,6 +460,30 @@ func (h *Handler) handleAlertNotificationsRead(w http.ResponseWriter, r *http.Re
h.write(w, r, map[string]int{"updated": count}, err)
}
func (h *Handler) handleAlertNotificationRetry(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(strings.TrimSpace(r.PathValue("id")), 10, 64)
if err != nil || id <= 0 {
h.write(w, r, nil, clientError{Code: "ALERT_NOTIFICATION_ID_INVALID", Message: "通知记录 ID 无效"})
return
}
var request AlertNotificationRetryRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.RetryAlertNotification(r.Context(), id, request)
h.write(w, r, data, err)
}
func (h *Handler) handleAlertNotificationRetryAudit(w http.ResponseWriter, r *http.Request) {
id, err := strconv.ParseInt(strings.TrimSpace(r.PathValue("id")), 10, 64)
if err != nil || id <= 0 {
h.write(w, r, nil, clientError{Code: "ALERT_NOTIFICATION_ID_INVALID", Message: "通知记录 ID 无效"})
return
}
data, err := h.service.AlertNotificationRetryAudits(r.Context(), id)
h.write(w, r, data, err)
}
func (h *Handler) handleAccessSummary(w http.ResponseWriter, r *http.Request) {
var query AccessQuery
if !decodeJSONBody(w, r, &query) {
@@ -324,6 +543,129 @@ func (h *Handler) handleListHistoryExports(w http.ResponseWriter, r *http.Reques
h.write(w, r, h.service.ListHistoryExports(r.Context()), nil)
}
func (h *Handler) handleListHistoryExportsPage(w http.ResponseWriter, r *http.Request) {
limit := parsePositive(r.URL.Query().Get("limit"), 10)
offset := parsePositive(r.URL.Query().Get("offset"), 0)
h.write(w, r, h.service.ListHistoryExportsPage(r.Context(), HistoryExportQuery{
Search: r.URL.Query().Get("search"), Status: r.URL.Query().Get("status"), Scope: r.URL.Query().Get("scope"), OwnerScope: r.URL.Query().Get("ownerScope"), Sort: r.URL.Query().Get("sort"), Limit: limit, Offset: offset,
}), nil)
}
func (h *Handler) handlePreviewHistoryExportCleanup(w http.ResponseWriter, r *http.Request) {
data, err := h.service.PreviewHistoryExportCleanup(r.Context(), HistoryExportCleanupQuery{
OlderThanDays: parsePositive(r.URL.Query().Get("olderThanDays"), 180),
OwnerScope: r.URL.Query().Get("ownerScope"),
})
h.write(w, r, data, err)
}
func (h *Handler) handleHistoryExportCleanupAudit(w http.ResponseWriter, r *http.Request) {
data, err := h.service.HistoryExportCleanupAudit(r.Context(), parsePositive(r.URL.Query().Get("limit"), 20))
h.write(w, r, data, err)
}
func (h *Handler) handleCleanupHistoryExports(w http.ResponseWriter, r *http.Request) {
var request HistoryExportCleanupRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.CleanupHistoryExports(r.Context(), request)
h.write(w, r, data, err)
}
func (h *Handler) handleHistoryExportCleanupAutomation(w http.ResponseWriter, r *http.Request) {
data, err := h.service.HistoryExportCleanupAutomation(r.Context())
h.write(w, r, data, err)
}
func (h *Handler) handleUpdateHistoryExportCleanupAutomation(w http.ResponseWriter, r *http.Request) {
var request HistoryExportCleanupAutomationPolicyRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.UpdateHistoryExportCleanupAutomation(r.Context(), request)
h.write(w, r, data, err)
}
func (h *Handler) handleApproveHistoryExportCleanupAutomation(w http.ResponseWriter, r *http.Request) {
var request HistoryExportCleanupAutomationActionRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.ApproveHistoryExportCleanupAutomation(r.Context(), r.PathValue("id"), request)
h.write(w, r, data, err)
}
func (h *Handler) handleRejectHistoryExportCleanupAutomation(w http.ResponseWriter, r *http.Request) {
var request HistoryExportCleanupAutomationActionRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.RejectHistoryExportCleanupAutomation(r.Context(), r.PathValue("id"), request)
h.write(w, r, data, err)
}
func (h *Handler) handleRetryHistoryExportCleanupAutomation(w http.ResponseWriter, r *http.Request) {
var request HistoryExportCleanupAutomationActionRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.RetryHistoryExportCleanupAutomation(r.Context(), r.PathValue("id"), request)
h.write(w, r, data, err)
}
func (h *Handler) handleHistoryPreferences(w http.ResponseWriter, r *http.Request) {
data, err := h.service.HistoryPreferences(r.Context())
h.write(w, r, data, err)
}
func (h *Handler) handleUpdateHistoryPreferences(w http.ResponseWriter, r *http.Request) {
var input HistoryPreferences
if !decodeJSONBody(w, r, &input) {
return
}
data, err := h.service.UpdateHistoryPreferences(r.Context(), input)
h.write(w, r, data, err)
}
func (h *Handler) handleCancelHistoryExport(w http.ResponseWriter, r *http.Request) {
data, err := h.service.CancelHistoryExport(r.Context(), r.PathValue("id"))
h.write(w, r, data, err)
}
func (h *Handler) handleRebuildHistoryExport(w http.ResponseWriter, r *http.Request) {
data, err := h.service.RebuildHistoryExport(r.Context(), r.PathValue("id"))
h.write(w, r, data, err)
}
func (h *Handler) handleArchiveHistoryExport(w http.ResponseWriter, r *http.Request) {
data, err := h.service.SetHistoryExportArchived(r.Context(), r.PathValue("id"), true)
h.write(w, r, data, err)
}
func (h *Handler) handleRestoreHistoryExport(w http.ResponseWriter, r *http.Request) {
data, err := h.service.SetHistoryExportArchived(r.Context(), r.PathValue("id"), false)
h.write(w, r, data, err)
}
func (h *Handler) handleHistoryExportCleanupProtection(w http.ResponseWriter, r *http.Request) {
var request HistoryExportCleanupProtectionRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.SetHistoryExportCleanupProtection(r.Context(), r.PathValue("id"), request)
h.write(w, r, data, err)
}
func (h *Handler) handleBatchHistoryExports(w http.ResponseWriter, r *http.Request) {
var request HistoryExportBatchRequest
if !decodeJSONBody(w, r, &request) {
return
}
data, err := h.service.BatchHistoryExports(r.Context(), request)
h.write(w, r, data, err)
}
func (h *Handler) handleDownloadHistoryExport(w http.ResponseWriter, r *http.Request) {
path, name, err := h.service.HistoryExportFile(r.Context(), r.PathValue("id"))
if err != nil {
@@ -401,6 +743,11 @@ func (h *Handler) handleVehicleCoverageSummary(w http.ResponseWriter, r *http.Re
h.write(w, r, data, err)
}
func (h *Handler) handleVehicleBusinessFilters(w http.ResponseWriter, r *http.Request) {
data, err := h.service.VehicleBusinessFilters(r.Context())
h.write(w, r, data, err)
}
func (h *Handler) handleVehicleServiceSummary(w http.ResponseWriter, r *http.Request) {
data, err := h.service.VehicleServiceSummary(r.Context())
h.write(w, r, data, err)
@@ -487,6 +834,15 @@ func (h *Handler) handleDailyMileage(w http.ResponseWriter, r *http.Request) {
h.write(w, r, data, err)
}
func (h *Handler) handleDailyMileagePost(w http.ResponseWriter, r *http.Request) {
query, ok := decodeMileageQuery(w, r)
if !ok {
return
}
data, err := h.service.DailyMileage(r.Context(), mileageQueryValues(query))
h.write(w, r, data, err)
}
func (h *Handler) handleMileageSummary(w http.ResponseWriter, r *http.Request) {
data, err := h.service.MileageSummary(r.Context(), r.URL.Query())
h.write(w, r, data, err)
@@ -497,6 +853,62 @@ func (h *Handler) handleMileageStatistics(w http.ResponseWriter, r *http.Request
h.write(w, r, data, err)
}
func (h *Handler) handleMileageStatisticsPost(w http.ResponseWriter, r *http.Request) {
query, ok := decodeMileageQuery(w, r)
if !ok {
return
}
data, err := h.service.MileageStatistics(r.Context(), mileageQueryValues(query))
h.write(w, r, data, err)
}
func decodeMileageQuery(w http.ResponseWriter, r *http.Request) (MileageQuery, bool) {
defer r.Body.Close()
var query MileageQuery
decoder := json.NewDecoder(http.MaxBytesReader(w, r.Body, 16<<20))
decoder.DisallowUnknownFields()
if err := decoder.Decode(&query); err != nil {
httpx.WriteError(w, http.StatusBadRequest, "BAD_JSON", "里程查询 JSON 解析失败", err.Error(), traceID(r))
return MileageQuery{}, false
}
return query, true
}
func mileageQueryValues(query MileageQuery) url.Values {
values := url.Values{}
if query.DateFrom != "" {
values.Set("dateFrom", query.DateFrom)
}
if query.DateTo != "" {
values.Set("dateTo", query.DateTo)
}
if query.Keyword != "" {
values.Set("keyword", query.Keyword)
}
if len(query.VINs) > 0 {
values.Set("vins", strings.Join(query.VINs, ","))
}
if query.VehicleScope != "" {
values.Set("vehicleScope", query.VehicleScope)
}
if query.Protocol != "" {
values.Set("protocol", query.Protocol)
}
if len(query.Protocols) > 0 {
values.Set("protocols", strings.Join(query.Protocols, ","))
}
if query.Deduplicate {
values.Set("deduplicate", "1")
}
if query.Limit > 0 {
values.Set("limit", strconv.Itoa(query.Limit))
}
if query.Offset > 0 {
values.Set("offset", strconv.Itoa(query.Offset))
}
return values
}
func (h *Handler) handleOnlineStatisticsSummary(w http.ResponseWriter, r *http.Request) {
data, err := h.service.OnlineStatisticsSummary(r.Context(), r.URL.Query())
h.write(w, r, data, err)