fix: restore country+weight price-check estimates for postcode zones.

When rules require postal codes, still quote by same-country snapshot zones as estimated; keep blocking countries not configured at all.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zzh 2026-07-28 11:14:12 +08:00
parent c8d35a8ef5
commit e2443ed6f8
3 changed files with 121 additions and 7 deletions

View File

@ -672,21 +672,68 @@ public partial class OrderService
await TryAddAsync(null, null, false);
// 如果按「仅目的国」都无法匹配到任何价格分区,则不要再用卖价快照的全量分区键兜底。
// 否则会导致未配置国家也能返回参考价格(estimated),与预期不符。
if (attempts.Count == 0)
// 精确命中(如无邮编区间的国家级分区)后,仍可用快照内其它分区键作参考兜底
if (attempts.Count > 0)
{
foreach (var zoneKey in ParcelBillingCalculator.CollectDistinctDetailZoneKeys(snapshotBinding.Price))
{
if (attempts.Any(x => x.ZoneKeys.Any(k => string.Equals(k, zoneKey, StringComparison.OrdinalIgnoreCase))))
{
continue;
}
attempts.Add(new QuoteZoneAttempt([zoneKey], true, zoneKey));
}
return attempts;
}
foreach (var zoneKey in ParcelBillingCalculator.CollectDistinctDetailZoneKeys(snapshotBinding.Price))
// 仅国家+重量:分区规则带邮编时无法精确命中,按「同国家」分区给出估算价;
// 快照中完全没有该国家时不跨国家兜底。
if (snapshotBinding.Price?.PriceAreaInfos is { Count: > 0 })
{
if (attempts.Any(x => x.ZoneKeys.Any(k => string.Equals(k, zoneKey, StringComparison.OrdinalIgnoreCase))))
foreach (var zoneKey in ParcelBillingCalculator.CollectCountryEstimateZoneKeys(
snapshotBinding.Price,
context.CountryCode))
{
continue;
attempts.Add(new QuoteZoneAttempt([zoneKey], true, zoneKey));
}
attempts.Add(new QuoteZoneAttempt([zoneKey], true, zoneKey));
return attempts;
}
// 无快照分区明细时,退回库表:同国家且不要求邮编的规则
var priceAreaId = ResolveEffectivePriceAreaId(product, snapshotBinding.Rules, snapshotBinding.Price);
if (priceAreaId is > 0)
{
var area = await KpiAreaRepository.FirstOrDefaultAsync(x => x.Id == priceAreaId.Value);
var rules = await KpiAreaRepository.GetAllInfosByAreaIdAsync(priceAreaId.Value);
foreach (var rule in rules)
{
if (!string.IsNullOrWhiteSpace(rule.CountryCode)
&& !string.Equals(rule.CountryCode, context.CountryCode, StringComparison.OrdinalIgnoreCase))
{
continue;
}
if (!string.IsNullOrWhiteSpace(rule.Province) || !string.IsNullOrWhiteSpace(rule.City))
{
continue;
}
var keys = ParcelBillingCalculator.BuildPriceZoneKeys(rule, area);
if (keys.Count == 0)
{
continue;
}
if (attempts.Any(x => x.ZoneKeys.SequenceEqual(keys, StringComparer.OrdinalIgnoreCase)))
{
continue;
}
attempts.Add(new QuoteZoneAttempt(keys.ToList(), true, keys[0]));
}
}
return attempts;

View File

@ -156,6 +156,32 @@ internal static class ParcelBillingCalculator
return keys.Count > 0 ? keys : null;
}
/// <summary>
/// 查价仅填国家时:若快照已配置该目的国分区(即使规则带邮编区间),
/// 则返回卖价明细中的分区键供估算报价;未配置该国家时返回空(不跨国家兜底)。
/// </summary>
public static IReadOnlyList<string> CollectCountryEstimateZoneKeys(
SalesProductPricePublishSnapshotDto? priceSnapshot,
string countryCode)
{
if (priceSnapshot?.PriceAreaInfos == null
|| priceSnapshot.PriceAreaInfos.Count == 0
|| string.IsNullOrWhiteSpace(countryCode))
{
return [];
}
var countryConfigured = priceSnapshot.PriceAreaInfos.Any(rule =>
string.IsNullOrWhiteSpace(rule.CountryCode)
|| string.Equals(rule.CountryCode, countryCode, StringComparison.OrdinalIgnoreCase));
if (!countryConfigured)
{
return [];
}
return CollectDistinctDetailZoneKeys(priceSnapshot);
}
public static string? ResolveZoneName(KpiAreaInfo? matched, KpiArea? area)
{
if (matched == null)

View File

@ -303,4 +303,45 @@ public class PriceCheckSnapshotVerificationTests
fees.Should().BeEmpty();
}
[Fact]
public void CountryEstimate_WhenCountryConfiguredWithPostcodeRules_ShouldReturnDetailZones()
{
var snapshot = BuildUserSnapshot();
snapshot.PriceAreaInfos =
[
new KpiAreaSnapshotRuleDto
{
Id = 11,
CountryCode = "US",
PostcodeRange = "(90000,99999]"
}
];
// 精确匹配(无邮编)应失败
ParcelBillingCalculator.TryResolveZoneKeysFromPriceSnapshot(snapshot, "US", null, null, null)
.Should().BeNull();
// 国家估算应返回卖价明细分区键
ParcelBillingCalculator.CollectCountryEstimateZoneKeys(snapshot, "US")
.Should().Contain(["3"]);
}
[Fact]
public void CountryEstimate_WhenCountryNotConfigured_ShouldReturnEmpty()
{
var snapshot = BuildUserSnapshot();
snapshot.PriceAreaInfos =
[
new KpiAreaSnapshotRuleDto
{
Id = 11,
CountryCode = "US",
PostcodeRange = "(90000,99999]"
}
];
ParcelBillingCalculator.CollectCountryEstimateZoneKeys(snapshot, "DE")
.Should().BeEmpty();
}
}