feat(operations): support canonical source providers

This commit is contained in:
lingniu
2026-07-16 22:50:55 +08:00
parent 9a5e6e0c4f
commit 65b4e4f055
17 changed files with 1050 additions and 52 deletions

View File

@@ -13,6 +13,7 @@ type vehicleSourcePolicyStore interface {
VehicleSourcePolicy(context.Context, string) (VehicleSourcePolicyConfig, error)
SaveVehicleSourcePolicy(context.Context, vehicleSourcePolicyStoreUpdate) (VehicleSourcePolicyConfig, error)
VehicleLocationSourceHistory(context.Context, string) ([]vehicleLocationSourceHistory, error)
VehicleSourceProvider(context.Context, string, string, string) (string, error)
}
func sourceReference(vin, protocol, sourceKey string) string {
@@ -57,9 +58,48 @@ func (s *Service) VehicleSourceDiagnostic(ctx context.Context, vin string) (Vehi
protocolStatus[status.Protocol] = status
}
}
representedProtocols := make(map[string]bool, len(evidence.LocationSources))
for _, source := range evidence.LocationSources {
representedProtocols[source.Protocol] = true
}
if access != nil {
for _, status := range access.ProtocolStatuses {
if !status.Connected || representedProtocols[status.Protocol] {
continue
}
sourceKey := status.Protocol + ":canonical"
providerOverride, providerErr := store.VehicleSourceProvider(ctx, evidence.VIN, status.Protocol, sourceKey)
if providerErr != nil {
return VehicleSourceDiagnostic{}, providerErr
}
source := VehicleLocationSourceEvidence{
Protocol: status.Protocol,
SourceLabel: firstNonEmpty(providerOverride, status.Provider, status.Protocol),
ProviderOverride: providerOverride,
SourceKind: "CANONICAL",
SelectedWithinProtocol: true,
Recommended: evidence.RecommendedLocationProtocol == status.Protocol,
Enabled: true,
Priority: 100,
Online: status.OnlineState == "online",
QualityStatus: "NO_LOCATION",
QualityReason: "协议已形成接入快照,但当前没有可展开的独立位置来源",
EventTime: status.LatestEventAt,
ReceivedAt: status.LatestReceivedAt,
FirstSeenAt: status.FirstSeenAt,
ReportIntervalSec: status.ReportIntervalSec,
sourceKey: sourceKey,
}
evidence.LocationSources = append(evidence.LocationSources, source)
representedProtocols[status.Protocol] = true
if source.Recommended {
evidence.RecommendedLocationLabel = source.SourceLabel
}
}
}
for index := range evidence.LocationSources {
source := &evidence.LocationSources[index]
if !strings.EqualFold(source.SourceKind, "CANONICAL") {
if source.sourceKey != "" {
source.SourceRef = sourceReference(evidence.VIN, source.Protocol, source.sourceKey)
}
if item, exists := historyBySource[source.Protocol+"\x00"+source.sourceKey]; exists {
@@ -135,7 +175,7 @@ func (s *Service) UpdateVehicleSourcePolicy(ctx context.Context, vin string, upd
var selected *VehicleLocationSourceEvidence
for index := range evidence.LocationSources {
source := &evidence.LocationSources[index]
if !strings.EqualFold(source.SourceKind, "CANONICAL") && sourceReference(vin, source.Protocol, source.sourceKey) == update.SourceRef {
if source.sourceKey != "" && sourceReference(vin, source.Protocol, source.sourceKey) == update.SourceRef {
selected = source
break
}
@@ -143,6 +183,15 @@ func (s *Service) UpdateVehicleSourcePolicy(ctx context.Context, vin string, upd
if selected == nil || selected.sourceKey == "" {
return VehicleSourceDiagnostic{}, clientError{Code: "SOURCE_NOT_FOUND", Message: "当前车辆不存在该来源,请刷新后重试"}
}
policyConfigurable := !strings.EqualFold(selected.SourceKind, "CANONICAL")
if !policyConfigurable && (selected.Enabled != update.Enabled ||
selected.Priority != update.Priority ||
strings.TrimSpace(selected.PolicyRemark) != update.Remark) {
return VehicleSourceDiagnostic{}, clientError{
Code: "SOURCE_POLICY_CANONICAL_READ_ONLY",
Message: "协议融合快照只能维护提供方,不能调整启停、优先级或策略备注",
}
}
providerChanged := strings.TrimSpace(selected.ProviderOverride) != update.ProviderName
if providerChanged && update.ProviderEvidence == "" {
return VehicleSourceDiagnostic{}, clientError{Code: "SOURCE_PROVIDER_REASON_REQUIRED", Message: "维护提供方时必须填写权威资料来源或核验说明"}
@@ -161,6 +210,7 @@ func (s *Service) UpdateVehicleSourcePolicy(ctx context.Context, vin string, upd
CurrentPriority: selected.Priority,
CurrentRemark: selected.PolicyRemark,
CurrentProvider: selected.ProviderOverride,
PolicyConfigurable: policyConfigurable,
}); err != nil {
return VehicleSourceDiagnostic{}, err
}
@@ -183,7 +233,7 @@ func authorizeInternalOperations(ctx context.Context, adminOnly bool) error {
func sourceSelectionReason(source VehicleLocationSourceEvidence) string {
if strings.EqualFold(source.SourceKind, "CANONICAL") {
return "这是协议融合结果快照,不是独立终端候选;需要展开实际来源后调整策略。"
return "这是协议融合结果快照,不是独立终端候选;可维护经权威资料确认的提供方,但不能从该快照调整终端启停或优先级。"
}
if !source.Enabled {
return "已由运维策略停用,不参与协议内选举。"
@@ -207,7 +257,7 @@ func recommendationReason(evidence VehicleSourceEvidence) string {
for _, source := range evidence.LocationSources {
if source.Recommended {
if strings.EqualFold(source.SourceKind, "CANONICAL") {
return fmt.Sprintf("当前推荐 %s 的协议融合结果;本次证据未取得可配置的独立终端候选,不能从该快照反推人工优先级。", source.Protocol)
return fmt.Sprintf("当前推荐 %s 的协议融合结果;本次证据未取得独立终端候选,只能维护权威提供方,不能从该快照反推人工优先级。", source.Protocol)
}
return fmt.Sprintf(
"当前推荐 %s / %s来源已启用、质量为 %s、协议内选中策略优先级为 %d。选举还会综合两分钟新鲜度、漂移冲突保护和连续有效样本。",