20261008-ide1.0发布 #1
@ -297,6 +297,29 @@ public partial class SalesProductService
|
|||||||
return rulesSnapshot;
|
return rulesSnapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同步态规则快照:分区不 Ensure,其余与发布组装一致。
|
||||||
|
/// </summary>
|
||||||
|
private async Task<SalesProductRulesPublishSnapshotDto> BuildRulesPublishSnapshotForSyncAsync(int salesProductId)
|
||||||
|
{
|
||||||
|
var product = await GetProductOrThrowAsync(salesProductId);
|
||||||
|
var rulesSnapshot = new SalesProductRulesPublishSnapshot
|
||||||
|
{
|
||||||
|
Product = MapProductToRulesSaveInput(product),
|
||||||
|
Providers = (await GetAllocationCoreAsync(salesProductId)).Providers
|
||||||
|
.Select(PublishSnapshotSlimmer.SlimAllocationProvider)
|
||||||
|
.ToList()
|
||||||
|
};
|
||||||
|
await RulesSnapshotHelper.ApplyRulesToSnapshotForSyncAsync(
|
||||||
|
rulesSnapshot,
|
||||||
|
product,
|
||||||
|
WeightRuleRepository,
|
||||||
|
WeightRoundingRepository,
|
||||||
|
KpiAreaPackService);
|
||||||
|
await AllocationProviderSnapshotEnricher.EnrichAsync(rulesSnapshot.Providers);
|
||||||
|
return rulesSnapshot;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 组装价格发布快照(不落库);价区钉分区包 Refs。
|
/// 组装价格发布快照(不落库);价区钉分区包 Refs。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@ -410,7 +410,7 @@ public partial class SalesProductService : ApplicationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发布同步态(轻量):对比待发布与最新生效快照,不回传正文。
|
/// 发布同步态:当前配置 vs 最新生效发布 Content。前端只展示 pending,不算差异。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("/channel/sales-product/publish/sync-status")]
|
[HttpGet("/channel/sales-product/publish/sync-status")]
|
||||||
public async Task<SalesProductPublishSyncStatusOutput> GetPublishSyncStatusAsync(
|
public async Task<SalesProductPublishSyncStatusOutput> GetPublishSyncStatusAsync(
|
||||||
@ -420,48 +420,45 @@ public partial class SalesProductService : ApplicationService
|
|||||||
var normalized = NormalizeOperatorType(operatorType);
|
var normalized = NormalizeOperatorType(operatorType);
|
||||||
await GetProductOrThrowAsync(salesProductId);
|
await GetProductOrThrowAsync(salesProductId);
|
||||||
|
|
||||||
// 待发布:同步态不 Ensure 分区包;对比时忽略 PackVersion/AreaName,只比价格项 + 分区 ContentHash
|
// 当前 = 与发布同源的快照组装(分区只解析哈希,不 Ensure 写库)
|
||||||
object pendingSnapshot = normalized == PriceOperatorType
|
object currentSnapshot = normalized == PriceOperatorType
|
||||||
? await BuildPricePublishSnapshotForSyncAsync(salesProductId)
|
? await BuildPricePublishSnapshotForSyncAsync(salesProductId)
|
||||||
: await BuildRulesPublishSnapshotAsync(salesProductId);
|
: await BuildRulesPublishSnapshotForSyncAsync(salesProductId);
|
||||||
var pendingCanon = normalized == PriceOperatorType
|
var currentHash = ComputeContentHash(
|
||||||
? OmsDocHelper.HashSalesProductPriceSnapshotForSync(pendingSnapshot)
|
normalized == PriceOperatorType
|
||||||
: OmsDocHelper.HashSalesProductRulesSnapshotForSync(pendingSnapshot);
|
? OmsDocHelper.HashSalesProductPriceSnapshotForSync(currentSnapshot)
|
||||||
var pendingHash = ComputeContentHash(pendingCanon);
|
: OmsDocHelper.HashSalesProductRulesSnapshotForSync(currentSnapshot));
|
||||||
|
|
||||||
var headers = await SalesProductRepository.GetAllPublishRecordHeadersAsync(salesProductId, normalized);
|
// 生效 = 最新 Active 发布记录 Content
|
||||||
var onlineHeader = headers
|
var onlineRecord = await SalesProductRepository.GetEffectivePublishRecordAsync(
|
||||||
.Where(x => x.Status == Domain.Shared.Enums.EsSnapshotStatus.Active)
|
salesProductId,
|
||||||
.OrderByDescending(x => x.PublishTime)
|
normalized,
|
||||||
.FirstOrDefault();
|
DateTime.Now);
|
||||||
|
if (onlineRecord == null || string.IsNullOrWhiteSpace(onlineRecord.Content))
|
||||||
if (onlineHeader == null)
|
|
||||||
{
|
{
|
||||||
return new SalesProductPublishSyncStatusOutput
|
return new SalesProductPublishSyncStatusOutput
|
||||||
{
|
{
|
||||||
Pending = true,
|
Pending = true,
|
||||||
OnlineVersion = null,
|
OnlineVersion = null,
|
||||||
OnlinePublishTime = null,
|
OnlinePublishTime = null,
|
||||||
PendingContentHash = pendingHash,
|
PendingContentHash = currentHash,
|
||||||
OnlineContentHash = null
|
OnlineContentHash = null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var onlineRecord = await SalesProductRepository.GetPublishRecordByIdAsync(onlineHeader.Id);
|
var onlineHash = ComputeContentHash(
|
||||||
var onlineJson = onlineRecord?.Content ?? string.Empty;
|
normalized == PriceOperatorType
|
||||||
var onlineCanon = normalized == PriceOperatorType
|
|
||||||
? OmsDocHelper.HashSalesProductPriceSnapshotForSync(
|
? OmsDocHelper.HashSalesProductPriceSnapshotForSync(
|
||||||
OmsDocHelper.DeserializePriceSnapshot(onlineJson))
|
OmsDocHelper.DeserializePriceSnapshot(onlineRecord.Content))
|
||||||
: OmsDocHelper.HashSalesProductRulesSnapshotForSync(
|
: OmsDocHelper.HashSalesProductRulesSnapshotForSync(
|
||||||
OmsDocHelper.DeserializeRulesSnapshot(onlineJson));
|
OmsDocHelper.DeserializeRulesSnapshot(onlineRecord.Content)));
|
||||||
var onlineHash = ComputeContentHash(onlineCanon);
|
|
||||||
|
|
||||||
return new SalesProductPublishSyncStatusOutput
|
return new SalesProductPublishSyncStatusOutput
|
||||||
{
|
{
|
||||||
Pending = !string.Equals(pendingHash, onlineHash, StringComparison.Ordinal),
|
Pending = !string.Equals(currentHash, onlineHash, StringComparison.Ordinal),
|
||||||
OnlineVersion = onlineHeader.Version,
|
OnlineVersion = onlineRecord.Version,
|
||||||
OnlinePublishTime = onlineHeader.PublishTime,
|
OnlinePublishTime = onlineRecord.PublishTime,
|
||||||
PendingContentHash = pendingHash,
|
PendingContentHash = currentHash,
|
||||||
OnlineContentHash = onlineHash
|
OnlineContentHash = onlineHash
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -325,6 +325,46 @@ public partial class ServiceChannelService
|
|||||||
return snapshot;
|
return snapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 渠道规则同步态:偏远分区不 Ensure。
|
||||||
|
/// </summary>
|
||||||
|
private async Task<ServiceChannelRulesPublishSnapshotDto> BuildRulesPublishSnapshotForSyncAsync(int serviceChannelId)
|
||||||
|
{
|
||||||
|
var entity = await GetEntityOrThrowAsync(serviceChannelId);
|
||||||
|
ServiceChannelDownstreamSnapshotDto? downstream = null;
|
||||||
|
if (entity.GroupId is > 0)
|
||||||
|
{
|
||||||
|
var group = await ServiceChannelGroupRepository.FirstOrDefaultAsync(x => x.Id == entity.GroupId.Value);
|
||||||
|
if (group != null)
|
||||||
|
{
|
||||||
|
downstream = await AllocationProviderSnapshotEnricher.BuildDownstreamFromGroupAsync(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var snapshot = new ServiceChannelRulesPublishSnapshotDto
|
||||||
|
{
|
||||||
|
Channel = MapChannelToSaveInput(entity),
|
||||||
|
Downstream = downstream,
|
||||||
|
RemoteAreaInfos = []
|
||||||
|
};
|
||||||
|
|
||||||
|
if (entity.RemoteAreaId is > 0)
|
||||||
|
{
|
||||||
|
var packRef = await KpiAreaPackService.ResolvePackRefForSyncAsync(entity.RemoteAreaId.Value);
|
||||||
|
snapshot.RemoteAreaId = entity.RemoteAreaId;
|
||||||
|
snapshot.RemoteAreaName = packRef.AreaName;
|
||||||
|
snapshot.RemoteAreaRef = packRef;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snapshot.RemoteAreaId = null;
|
||||||
|
snapshot.RemoteAreaName = null;
|
||||||
|
snapshot.RemoteAreaRef = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return snapshot;
|
||||||
|
}
|
||||||
|
|
||||||
private string ResolvePublishUser() =>
|
private string ResolvePublishUser() =>
|
||||||
string.IsNullOrWhiteSpace(CurrentUser.UserName)
|
string.IsNullOrWhiteSpace(CurrentUser.UserName)
|
||||||
? CurrentUser.GetId<int>().ToString()
|
? CurrentUser.GetId<int>().ToString()
|
||||||
|
|||||||
@ -403,7 +403,7 @@ public partial class ServiceChannelService : ApplicationService
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 发布同步态(轻量):对比待发布与最新生效快照,不回传正文。与产品卖价对称。
|
/// 发布同步态:当前配置 vs 最新生效发布 Content。前端只展示 pending。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[HttpGet("/channel/service-channel/publish/sync-status")]
|
[HttpGet("/channel/service-channel/publish/sync-status")]
|
||||||
public async Task<ServiceChannelPublishSyncStatusOutput> GetPublishSyncStatusAsync(
|
public async Task<ServiceChannelPublishSyncStatusOutput> GetPublishSyncStatusAsync(
|
||||||
@ -413,49 +413,70 @@ public partial class ServiceChannelService : ApplicationService
|
|||||||
var normalized = NormalizeOperatorType(operatorType);
|
var normalized = NormalizeOperatorType(operatorType);
|
||||||
await GetEntityOrThrowAsync(serviceChannelId);
|
await GetEntityOrThrowAsync(serviceChannelId);
|
||||||
|
|
||||||
object pendingSnapshot = normalized == PriceOperatorType
|
string currentCanon;
|
||||||
? await BuildPricePublishSnapshotForSyncAsync(serviceChannelId)
|
if (normalized == PriceOperatorType)
|
||||||
: await BuildRulesPublishSnapshotAsync(serviceChannelId);
|
{
|
||||||
var pendingCanon = normalized == PriceOperatorType
|
var current = await BuildPricePublishSnapshotForSyncAsync(serviceChannelId);
|
||||||
? OmsDocHelper.HashChannelPriceSnapshotForSync(pendingSnapshot)
|
currentCanon = OmsDocHelper.HashChannelPriceSnapshotForSync(current);
|
||||||
: OmsDocHelper.SerializeSnapshotJson(pendingSnapshot) ?? string.Empty;
|
}
|
||||||
var pendingHash = ComputeContentHash(pendingCanon);
|
else
|
||||||
|
{
|
||||||
|
var current = await BuildRulesPublishSnapshotForSyncAsync(serviceChannelId);
|
||||||
|
currentCanon = HashChannelRulesSnapshotForSync(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentHash = ComputeContentHash(currentCanon);
|
||||||
|
|
||||||
var onlineRecord = await ServiceChannelRepository.GetEffectivePublishRecordAsync(
|
var onlineRecord = await ServiceChannelRepository.GetEffectivePublishRecordAsync(
|
||||||
serviceChannelId,
|
serviceChannelId,
|
||||||
normalized,
|
normalized,
|
||||||
DateTime.Now);
|
DateTime.Now);
|
||||||
if (onlineRecord == null)
|
if (onlineRecord == null || string.IsNullOrWhiteSpace(onlineRecord.Content))
|
||||||
{
|
{
|
||||||
return new ServiceChannelPublishSyncStatusOutput
|
return new ServiceChannelPublishSyncStatusOutput
|
||||||
{
|
{
|
||||||
Pending = true,
|
Pending = true,
|
||||||
OnlineVersion = null,
|
OnlineVersion = null,
|
||||||
OnlinePublishTime = null,
|
OnlinePublishTime = null,
|
||||||
PendingContentHash = pendingHash,
|
PendingContentHash = currentHash,
|
||||||
OnlineContentHash = null
|
OnlineContentHash = null
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var onlineJson = onlineRecord.Content ?? string.Empty;
|
|
||||||
var onlineCanon = normalized == PriceOperatorType
|
var onlineCanon = normalized == PriceOperatorType
|
||||||
? OmsDocHelper.HashChannelPriceSnapshotForSync(
|
? OmsDocHelper.HashChannelPriceSnapshotForSync(
|
||||||
OmsDocHelper.DeserializeChannelPriceSnapshot(onlineJson))
|
OmsDocHelper.DeserializeChannelPriceSnapshot(onlineRecord.Content))
|
||||||
: OmsDocHelper.SerializeSnapshotJson(
|
: HashChannelRulesSnapshotForSync(
|
||||||
OmsDocHelper.DeserializeJson<ServiceChannelRulesPublishSnapshotDto>(onlineJson))
|
OmsDocHelper.DeserializeJson<ServiceChannelRulesPublishSnapshotDto>(onlineRecord.Content));
|
||||||
?? string.Empty;
|
|
||||||
var onlineHash = ComputeContentHash(onlineCanon);
|
var onlineHash = ComputeContentHash(onlineCanon);
|
||||||
|
|
||||||
return new ServiceChannelPublishSyncStatusOutput
|
return new ServiceChannelPublishSyncStatusOutput
|
||||||
{
|
{
|
||||||
Pending = !string.Equals(pendingHash, onlineHash, StringComparison.Ordinal),
|
Pending = !string.Equals(currentHash, onlineHash, StringComparison.Ordinal),
|
||||||
OnlineVersion = onlineRecord.Version,
|
OnlineVersion = onlineRecord.Version,
|
||||||
OnlinePublishTime = onlineRecord.PublishTime,
|
OnlinePublishTime = onlineRecord.PublishTime,
|
||||||
PendingContentHash = pendingHash,
|
PendingContentHash = currentHash,
|
||||||
OnlineContentHash = onlineHash
|
OnlineContentHash = onlineHash
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string HashChannelRulesSnapshotForSync(ServiceChannelRulesPublishSnapshotDto? snapshot)
|
||||||
|
{
|
||||||
|
if (snapshot == null)
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot.RemoteAreaName = null;
|
||||||
|
if (snapshot.RemoteAreaRef != null)
|
||||||
|
{
|
||||||
|
snapshot.RemoteAreaRef.PackVersion = string.Empty;
|
||||||
|
snapshot.RemoteAreaRef.AreaName = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OmsDocHelper.SerializeSnapshotJson(snapshot) ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
private static string ComputeContentHash(string content)
|
private static string ComputeContentHash(string content)
|
||||||
{
|
{
|
||||||
var hash = global::System.Security.Cryptography.SHA256.HashData(
|
var hash = global::System.Security.Cryptography.SHA256.HashData(
|
||||||
|
|||||||
@ -28,6 +28,7 @@ public static class RulesSnapshotHelper
|
|||||||
KpiAreaPackService.AreaTypeBlacklist,
|
KpiAreaPackService.AreaTypeBlacklist,
|
||||||
packService,
|
packService,
|
||||||
publishUser,
|
publishUser,
|
||||||
|
ensurePack: true,
|
||||||
(s, areaId, areaName, packRef) =>
|
(s, areaId, areaName, packRef) =>
|
||||||
{
|
{
|
||||||
s.BlacklistAreaId = areaId;
|
s.BlacklistAreaId = areaId;
|
||||||
@ -43,6 +44,54 @@ public static class RulesSnapshotHelper
|
|||||||
KpiAreaPackService.AreaTypeRemote,
|
KpiAreaPackService.AreaTypeRemote,
|
||||||
packService,
|
packService,
|
||||||
publishUser,
|
publishUser,
|
||||||
|
ensurePack: true,
|
||||||
|
(s, areaId, areaName, packRef) =>
|
||||||
|
{
|
||||||
|
s.RemoteAreaId = areaId;
|
||||||
|
s.RemoteAreaName = areaName;
|
||||||
|
s.RemoteAreaRef = packRef;
|
||||||
|
s.RemoteAreaInfos = [];
|
||||||
|
},
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
await ApplyWeightRuleToSnapshotAsync(snapshot, product, weightRuleRepository, cancellationToken);
|
||||||
|
await ApplyWeightRoundingToSnapshotAsync(snapshot, product, weightRoundingRepository, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 同步态:黑名单/偏远只解析哈希/已有包,不 Ensure 写库。
|
||||||
|
/// </summary>
|
||||||
|
public static async Task ApplyRulesToSnapshotForSyncAsync(
|
||||||
|
SalesProductRulesPublishSnapshotDto snapshot,
|
||||||
|
SalesProduct product,
|
||||||
|
IWeightRuleRepository weightRuleRepository,
|
||||||
|
IWeightRoundingRepository weightRoundingRepository,
|
||||||
|
KpiAreaPackService packService,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
await ApplyKpiAreaPackRefAsync(
|
||||||
|
snapshot,
|
||||||
|
product.BlacklistAreaId,
|
||||||
|
KpiAreaPackService.AreaTypeBlacklist,
|
||||||
|
packService,
|
||||||
|
publishUser: null,
|
||||||
|
ensurePack: false,
|
||||||
|
(s, areaId, areaName, packRef) =>
|
||||||
|
{
|
||||||
|
s.BlacklistAreaId = areaId;
|
||||||
|
s.BlacklistAreaName = areaName;
|
||||||
|
s.BlacklistAreaRef = packRef;
|
||||||
|
s.BlacklistAreaInfos = [];
|
||||||
|
},
|
||||||
|
cancellationToken);
|
||||||
|
|
||||||
|
await ApplyKpiAreaPackRefAsync(
|
||||||
|
snapshot,
|
||||||
|
product.RemoteAreaId,
|
||||||
|
KpiAreaPackService.AreaTypeRemote,
|
||||||
|
packService,
|
||||||
|
publishUser: null,
|
||||||
|
ensurePack: false,
|
||||||
(s, areaId, areaName, packRef) =>
|
(s, areaId, areaName, packRef) =>
|
||||||
{
|
{
|
||||||
s.RemoteAreaId = areaId;
|
s.RemoteAreaId = areaId;
|
||||||
@ -107,6 +156,7 @@ public static class RulesSnapshotHelper
|
|||||||
string areaType,
|
string areaType,
|
||||||
KpiAreaPackService packService,
|
KpiAreaPackService packService,
|
||||||
string? publishUser,
|
string? publishUser,
|
||||||
|
bool ensurePack,
|
||||||
Action<SalesProductRulesPublishSnapshotDto, int?, string?, KpiAreaPackRefDto?> assign,
|
Action<SalesProductRulesPublishSnapshotDto, int?, string?, KpiAreaPackRefDto?> assign,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
@ -116,12 +166,14 @@ public static class RulesSnapshotHelper
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var packRef = await packService.EnsurePackAsync(
|
var packRef = ensurePack
|
||||||
|
? await packService.EnsurePackAsync(
|
||||||
areaId.Value,
|
areaId.Value,
|
||||||
areaType,
|
areaType,
|
||||||
publishUser,
|
publishUser,
|
||||||
description: "sales-product-rules-publish",
|
description: "sales-product-rules-publish",
|
||||||
cancellationToken);
|
cancellationToken)
|
||||||
|
: await packService.ResolvePackRefForSyncAsync(areaId.Value, cancellationToken);
|
||||||
assign(snapshot, areaId, packRef.AreaName, packRef);
|
assign(snapshot, areaId, packRef.AreaName, packRef);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user