admin/src/Oms.Application/Shared/KpiAreaSnapshotMapper.cs
zzh 7827b4803e feat: slim rules/price publish snapshots with PostcodeRange (left,right]
Use slim DTOs without audit timestamps; migrate legacy LeftPostcode/RightPostcode on read.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-26 14:10:34 +08:00

106 lines
3.2 KiB
C#

using System.Text.RegularExpressions;
using Oms.Application.Dtos.Orders;
using Oms.Domain.ChannelsAggregate.Entities;
namespace Oms.Application.Shared;
/// <summary>
/// KPI 分区快照映射与左开右闭邮编匹配。
/// </summary>
public static class KpiAreaSnapshotMapper
{
private static readonly Regex PostcodeRangePattern = new(
@"^\(\s*([^,)\]]*)\s*,\s*([^,)\]]*)\s*\]$",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
public static KpiAreaSnapshotRuleDto MapToSnapshotRule(KpiAreaInfo entity) =>
new()
{
Id = entity.Id,
CountryCode = entity.CountryCode,
Province = entity.Province,
City = entity.City,
PostcodeRange = BuildPostcodeRange(entity.LeftPostcode, entity.RightPostcode)
};
public static string? BuildPostcodeRange(string? left, string? right)
{
if (string.IsNullOrWhiteSpace(left) && string.IsNullOrWhiteSpace(right))
{
return null;
}
return $"({left?.Trim()},{right?.Trim()}]";
}
public static string? ResolvePostcodeRange(KpiAreaSnapshotRuleDto rule)
{
if (!string.IsNullOrWhiteSpace(rule.PostcodeRange))
{
return rule.PostcodeRange.Trim();
}
return BuildPostcodeRange(rule.LeftPostcode, rule.RightPostcode);
}
public static bool IsRuleMatched(
KpiAreaSnapshotRuleDto rule,
string countryCode,
string? province,
string? city,
string? zipCode)
{
if (!string.IsNullOrWhiteSpace(rule.CountryCode)
&& !string.Equals(rule.CountryCode, countryCode, StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (!string.IsNullOrWhiteSpace(rule.Province)
&& !string.Equals(rule.Province, province, StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (!string.IsNullOrWhiteSpace(rule.City)
&& !string.Equals(rule.City, city, StringComparison.OrdinalIgnoreCase))
{
return false;
}
var postcodeRange = ResolvePostcodeRange(rule);
if (string.IsNullOrWhiteSpace(postcodeRange))
{
return true;
}
if (string.IsNullOrWhiteSpace(zipCode))
{
return false;
}
return IsZipInLeftOpenRightClosedRange(zipCode.Trim(), postcodeRange);
}
public static bool IsZipInLeftOpenRightClosedRange(string zipCode, string postcodeRange)
{
var match = PostcodeRangePattern.Match(postcodeRange.Trim());
if (!match.Success)
{
return false;
}
var left = match.Groups[1].Value.Trim();
var right = match.Groups[2].Value.Trim();
if (long.TryParse(zipCode, out var zip)
&& long.TryParse(left, out var leftValue)
&& long.TryParse(right, out var rightValue))
{
return zip > leftValue && zip <= rightValue;
}
return string.Compare(zipCode, left, StringComparison.OrdinalIgnoreCase) > 0
&& string.Compare(zipCode, right, StringComparison.OrdinalIgnoreCase) <= 0;
}
}