| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- using System.Text.RegularExpressions;
- using SqlSugar;
- namespace Admin.NET.Plugin.AiDOP.DataPlatform.Wms;
- /// <summary>
- /// 领料保管员解析。移植基准:旧 DOP ResourceExamineAppService.GenerateNbr
- /// (按 EmpWorkDutyMaster.ItemNum1..ItemNum2 字符串区间匹配 Employee)。
- /// 比较口径见 WP7 §3.6(B5):Trim + ToUpperInvariant 后 Ordinal;最窄区间优先。
- /// </summary>
- public sealed class KeeperResolveService : ITransient
- {
- private static readonly Regex SafeItemNum = new(@"^[0-9A-Z]+$", RegexOptions.Compiled);
- private readonly MdpSourceScopeFactory _scopeFactory;
- public KeeperResolveService(MdpSourceScopeFactory scopeFactory)
- {
- _scopeFactory = scopeFactory;
- }
- public sealed class KeeperHit
- {
- public string ItemNum { get; set; } = "";
- public int DutyRecID { get; set; }
- public string Employee { get; set; } = "";
- public string? Location { get; set; }
- }
- public sealed class KeeperResolveResult
- {
- public IReadOnlyList<KeeperHit> Hits { get; init; } = Array.Empty<KeeperHit>();
- /// <summary>未匹配到职责的物料号</summary>
- public IReadOnlyList<string> MissingItems { get; init; } = Array.Empty<string>();
- /// <summary>命中但 Location 为空的物料号</summary>
- public IReadOnlyList<string> EmptyLocationItems { get; init; } = Array.Empty<string>();
- /// <summary>字符集越界(物料号或职责区间)</summary>
- public IReadOnlyList<string> UnsafeItems { get; init; } = Array.Empty<string>();
- public bool Ok => MissingItems.Count == 0 && EmptyLocationItems.Count == 0 && UnsafeItems.Count == 0;
- }
- /// <summary>
- /// 按物料号匹配职责;返回命中与 WP7 §3.2 硬校验 / §3.6 字符集校验结果。
- /// </summary>
- public async Task<KeeperResolveResult> ResolveByItemsAsync(
- string domain, IEnumerable<string> itemNums, CancellationToken ct = default)
- {
- var items = itemNums.Where(x => !string.IsNullOrWhiteSpace(x)).Distinct(StringComparer.OrdinalIgnoreCase).ToList();
- if (items.Count == 0)
- return new KeeperResolveResult();
- var db = await _scopeFactory.GetScopeAsync("DOPDEMORQ_SQLSERVER", ct);
- var duties = await db.Ado.SqlQueryAsync<DutyRow>(
- @"SELECT RecID, Employee, ItemNum1, ItemNum2, Location
- FROM EmpWorkDutyMaster
- WHERE Domain=@d
- ORDER BY ItemNum1, ItemNum2, RecID",
- new SugarParameter("@d", domain));
- var hits = new List<KeeperHit>();
- var missing = new List<string>();
- var emptyLoc = new List<string>();
- var unsafeItems = new List<string>();
- var hasUnsafeDuty = duties.Any(s => !IsSafe(Norm(s.ItemNum1)) || !IsSafe(Norm(s.ItemNum2)));
- foreach (var raw in items)
- {
- var key = Norm(raw);
- if (!IsSafe(key))
- {
- unsafeItems.Add(raw);
- continue;
- }
- // 最窄区间优先:下界最大、上界最小、同宽 RecID 小者
- var match = duties
- .Select(d => (Row: d, A: Norm(d.ItemNum1), B: Norm(d.ItemNum2)))
- .Where(d => IsSafe(d.A) && IsSafe(d.B)
- && string.Compare(d.A, key, StringComparison.Ordinal) <= 0
- && string.Compare(key, d.B, StringComparison.Ordinal) <= 0)
- .OrderByDescending(d => d.A, StringComparer.Ordinal)
- .ThenBy(d => d.B, StringComparer.Ordinal)
- .ThenBy(d => d.Row.RecID)
- .Select(d => d.Row)
- .FirstOrDefault();
- if (match == null)
- {
- if (hasUnsafeDuty) unsafeItems.Add(raw);
- else missing.Add(raw);
- continue;
- }
- if (string.IsNullOrWhiteSpace(match.Employee))
- {
- missing.Add(raw);
- continue;
- }
- if (string.IsNullOrWhiteSpace(match.Location))
- {
- emptyLoc.Add(raw);
- continue;
- }
- hits.Add(new KeeperHit
- {
- ItemNum = raw,
- DutyRecID = match.RecID,
- Employee = match.Employee!,
- Location = match.Location
- });
- }
- return new KeeperResolveResult
- {
- Hits = hits,
- MissingItems = missing,
- EmptyLocationItems = emptyLoc,
- UnsafeItems = unsafeItems
- };
- }
- /// <summary>拼 NbrMaster.User1(逗号)与 User2(JSON 数组字符串),与旧 DOP 一致。</summary>
- public static (string user1, string user2) FormatKeeperUsers(IEnumerable<KeeperHit> hits)
- {
- var emps = hits.Select(x => x.Employee).Where(x => !string.IsNullOrWhiteSpace(x)).Distinct().ToList();
- if (emps.Count == 0) return ("", "[]");
- var user1 = string.Join(",", emps);
- var user2 = System.Text.Json.JsonSerializer.Serialize(emps);
- return (user1, user2);
- }
- internal static string Norm(string? s) => (s ?? string.Empty).Trim().ToUpperInvariant();
- private static bool IsSafe(string s) => SafeItemNum.IsMatch(s);
- private sealed class DutyRow
- {
- public int RecID { get; set; }
- public string? Employee { get; set; }
- public string? ItemNum1 { get; set; }
- public string? ItemNum2 { get; set; }
- public string? Location { get; set; }
- }
- }
|