admin/src/Oms.Application/Shared/PriceAreaSnapshotHelper.cs
zzh 0449a4a3f5
All checks were successful
Build and Deploy / deploy (push) Successful in 57s
fix: 发布同步态按 ContentHash 语义对比,且不再 Ensure 写包
sync-status 用 ResolvePackRef;哈希忽略 PackVersion/AreaName。修复卖价页发布后因价格行签名跳过刷新导致徽章卡在待发布。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-07 19:21:17 +08:00

318 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Oms.Application.Dtos.Orders;
using Oms.Application.Services.Orders;
using Oms.Domain.ChannelsAggregate.Entities;
using Oms.Domain.ChannelsAggregate.Repositories;
namespace Oms.Application.Shared;
/// <summary>
/// 卖价发布:解析价区表 Id 并钉分区包引用(不内嵌 Infos)。
/// </summary>
public static class PriceAreaSnapshotHelper
{
/// <summary>
/// 写入 PriceAreaRefs(Ensure 分区包);Infos 仅 LIVE 路径由 PackService.LoadLive 填充。
/// </summary>
public static async Task ApplyPriceAreaToSnapshotAsync(
SalesProductPricePublishSnapshotDto snapshot,
SalesProduct product,
IKpiAreaRepository kpiAreaRepository,
KpiAreaPackService packService,
string? publishUser = null,
CancellationToken cancellationToken = default)
{
var areaIds = await CollectResolvedPriceAreaIdsAsync(
product,
snapshot,
kpiAreaRepository,
cancellationToken);
snapshot.PriceAreaInfos = [];
if (areaIds.Count == 0)
{
snapshot.PriceAreaId = null;
snapshot.PriceAreaName = null;
snapshot.PriceAreaRefs = [];
return;
}
var refs = new List<KpiAreaPackRefDto>();
foreach (var areaId in areaIds)
{
refs.Add(await packService.EnsurePackAsync(
areaId,
KpiAreaPackService.AreaTypePrice,
publishUser,
description: "sales-product-price-publish",
cancellationToken));
}
AssignPriceAreaRefs(snapshot, refs);
}
/// <summary>
/// 同步态:解析价区 ContentHash / 已有包 Version,不 Ensure 新包。
/// </summary>
public static async Task ApplyPriceAreaRefsForSyncAsync(
SalesProductPricePublishSnapshotDto snapshot,
SalesProduct product,
IKpiAreaRepository kpiAreaRepository,
KpiAreaPackService packService,
CancellationToken cancellationToken = default)
{
var areaIds = await CollectResolvedPriceAreaIdsAsync(
product,
snapshot,
kpiAreaRepository,
cancellationToken);
snapshot.PriceAreaInfos = [];
if (areaIds.Count == 0)
{
snapshot.PriceAreaId = null;
snapshot.PriceAreaName = null;
snapshot.PriceAreaRefs = [];
return;
}
var refs = new List<KpiAreaPackRefDto>();
foreach (var areaId in areaIds)
{
refs.Add(await packService.ResolvePackRefForSyncAsync(areaId, cancellationToken));
}
AssignPriceAreaRefs(snapshot, refs);
}
private static void AssignPriceAreaRefs(
SalesProductPricePublishSnapshotDto snapshot,
List<KpiAreaPackRefDto> refs)
{
snapshot.PriceAreaRefs = refs;
if (refs.Count == 1)
{
snapshot.PriceAreaId = refs[0].AreaId;
snapshot.PriceAreaName = refs[0].AreaName;
}
else
{
snapshot.PriceAreaId = null;
snapshot.PriceAreaName = string.Join(
"、",
refs.Select(x => x.AreaName).Where(x => !string.IsNullOrWhiteSpace(x)));
}
}
/// <summary>
/// LIVE/模拟:不钉包,直接从当前库装载 Infos。
/// </summary>
public static async Task ApplyLivePriceAreaInfosAsync(
SalesProductPricePublishSnapshotDto snapshot,
SalesProduct product,
IKpiAreaRepository kpiAreaRepository,
KpiAreaPackService packService,
CancellationToken cancellationToken = default)
{
var areaIds = await CollectResolvedPriceAreaIdsAsync(
product,
snapshot,
kpiAreaRepository,
cancellationToken);
await packService.LoadLiveInfosIntoPriceSnapshotAsync(snapshot, areaIds, cancellationToken);
}
/// <summary>
/// 收集卖价明细(及产品 PriceAreaId)可解析到的全部价格分区表 Id,去重保序。
/// </summary>
internal static async Task<IReadOnlyList<int>> CollectResolvedPriceAreaIdsAsync(
SalesProduct product,
SalesProductPricePublishSnapshotDto snapshot,
IKpiAreaRepository kpiAreaRepository,
CancellationToken cancellationToken = default)
{
var result = new List<int>();
var seen = new HashSet<int>();
void Add(int? areaId)
{
if (areaId is > 0 && seen.Add(areaId.Value))
{
result.Add(areaId.Value);
}
}
if (product.PriceAreaId is > 0)
{
Add(product.PriceAreaId);
}
var zoneKeys = ParcelBillingCalculator.CollectDistinctDetailZoneKeys(snapshot);
foreach (var zoneKey in zoneKeys)
{
Add(await TryResolveZoneKeyToAreaIdAsync(zoneKey, kpiAreaRepository, cancellationToken));
}
return result;
}
internal static async Task<int?> ResolvePriceAreaIdAsync(
SalesProduct product,
SalesProductPricePublishSnapshotDto snapshot,
IKpiAreaRepository kpiAreaRepository,
CancellationToken cancellationToken = default)
{
var ids = await CollectResolvedPriceAreaIdsAsync(
product,
snapshot,
kpiAreaRepository,
cancellationToken);
return ids.Count > 0 ? ids[0] : null;
}
/// <summary>
/// 渠道买价发布:从明细推断价区表并钉包(与产品卖价分区独立)。
/// </summary>
public static async Task ApplyChannelPriceAreaToSnapshotAsync(
ServiceChannelPricePublishSnapshotDto snapshot,
IKpiAreaRepository kpiAreaRepository,
KpiAreaPackService packService,
string? publishUser = null,
CancellationToken cancellationToken = default)
{
var areaIds = await CollectResolvedChannelPriceAreaIdsAsync(
snapshot,
kpiAreaRepository,
cancellationToken);
snapshot.PriceAreaInfos = [];
if (areaIds.Count == 0)
{
snapshot.PriceAreaId = null;
snapshot.PriceAreaName = null;
snapshot.PriceAreaRefs = [];
return;
}
var refs = new List<KpiAreaPackRefDto>();
foreach (var areaId in areaIds)
{
refs.Add(await packService.EnsurePackAsync(
areaId,
KpiAreaPackService.AreaTypePrice,
publishUser,
description: "service-channel-price-publish",
cancellationToken));
}
AssignChannelPriceAreaRefs(snapshot, refs);
}
/// <summary>
/// 渠道买价同步态:不 Ensure 新包。
/// </summary>
public static async Task ApplyChannelPriceAreaRefsForSyncAsync(
ServiceChannelPricePublishSnapshotDto snapshot,
IKpiAreaRepository kpiAreaRepository,
KpiAreaPackService packService,
CancellationToken cancellationToken = default)
{
var areaIds = await CollectResolvedChannelPriceAreaIdsAsync(
snapshot,
kpiAreaRepository,
cancellationToken);
snapshot.PriceAreaInfos = [];
if (areaIds.Count == 0)
{
snapshot.PriceAreaId = null;
snapshot.PriceAreaName = null;
snapshot.PriceAreaRefs = [];
return;
}
var refs = new List<KpiAreaPackRefDto>();
foreach (var areaId in areaIds)
{
refs.Add(await packService.ResolvePackRefForSyncAsync(areaId, cancellationToken));
}
AssignChannelPriceAreaRefs(snapshot, refs);
}
private static void AssignChannelPriceAreaRefs(
ServiceChannelPricePublishSnapshotDto snapshot,
List<KpiAreaPackRefDto> refs)
{
snapshot.PriceAreaRefs = refs;
if (refs.Count == 1)
{
snapshot.PriceAreaId = refs[0].AreaId;
snapshot.PriceAreaName = refs[0].AreaName;
}
else
{
snapshot.PriceAreaId = null;
snapshot.PriceAreaName = string.Join(
"、",
refs.Select(x => x.AreaName).Where(x => !string.IsNullOrWhiteSpace(x)));
}
}
/// <summary>
/// 渠道买价 LIVE:不钉包,从当前库装 Infos。
/// </summary>
public static async Task ApplyLiveChannelPriceAreaInfosAsync(
ServiceChannelPricePublishSnapshotDto snapshot,
IKpiAreaRepository kpiAreaRepository,
KpiAreaPackService packService,
CancellationToken cancellationToken = default)
{
var areaIds = await CollectResolvedChannelPriceAreaIdsAsync(
snapshot,
kpiAreaRepository,
cancellationToken);
await packService.LoadLiveInfosIntoChannelPriceSnapshotAsync(snapshot, areaIds, cancellationToken);
}
internal static async Task<IReadOnlyList<int>> CollectResolvedChannelPriceAreaIdsAsync(
ServiceChannelPricePublishSnapshotDto snapshot,
IKpiAreaRepository kpiAreaRepository,
CancellationToken cancellationToken = default)
{
var result = new List<int>();
var seen = new HashSet<int>();
void Add(int? areaId)
{
if (areaId is > 0 && seen.Add(areaId.Value))
{
result.Add(areaId.Value);
}
}
var zoneKeys = ParcelBillingCalculator.CollectDistinctDetailZoneKeys(snapshot);
foreach (var zoneKey in zoneKeys)
{
Add(await TryResolveZoneKeyToAreaIdAsync(zoneKey, kpiAreaRepository, cancellationToken));
}
return result;
}
private static async Task<int?> TryResolveZoneKeyToAreaIdAsync(
string zoneKey,
IKpiAreaRepository kpiAreaRepository,
CancellationToken cancellationToken)
{
if (!int.TryParse(zoneKey.Trim(), out var zoneId) || zoneId <= 0)
{
return null;
}
var area = await kpiAreaRepository.FirstOrDefaultAsync(x => x.Id == zoneId, cancellationToken);
if (area != null)
{
return zoneId;
}
var info = await kpiAreaRepository.GetInfoByIdAsync(zoneId, cancellationToken);
return info?.AreaId;
}
}