Use slim DTOs without audit timestamps; migrate legacy LeftPostcode/RightPostcode on read. Co-authored-by: Cursor <cursoragent@cursor.com>
106 lines
3.2 KiB
C#
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;
|
|
}
|
|
}
|