feat(operations): support canonical source providers
This commit is contained in:
@@ -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。选举还会综合两分钟新鲜度、漂移冲突保护和连续有效样本。",
|
||||
|
||||
Reference in New Issue
Block a user