| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- using System.Text.Json;
- using Admin.NET.Plugin.AiDOP.Entity.DataPlatform;
- namespace Admin.NET.Plugin.AiDOP.DataPlatform.Inbound;
- /// <summary>
- /// mdp_field_map 两层映射:精确 access_key 覆盖 * 兜底;无映射行则恒等。
- /// 与 PULL 的 mdp_field_mapping 不是同一张表。
- /// </summary>
- public sealed class MdpInboundFieldMapper : ITransient
- {
- private static readonly IReadOnlyDictionary<string, MdpInboundSchemaField[]> IdentityFields =
- new Dictionary<string, MdpInboundSchemaField[]>(StringComparer.OrdinalIgnoreCase)
- {
- ["S1_SALES_ORDER_ENTRY"] =
- [
- Req("bill_no"), Req("entry_seq"), Req("seorder_id"), Req("qty"),
- Opt("item_number"), Opt("item_name"), Opt("specification"), Opt("unit"),
- Opt("map_number"), Opt("map_name"), Opt("bom_number"),
- Opt("deliver_notice_count"), Opt("deliver_count"),
- Opt("price"), Opt("tax_price"), Opt("amount"), Opt("total_amount"),
- Opt("plan_date"), Opt("date"), Opt("sys_capacity_date"), Opt("sys_material_date"),
- Opt("planner_no"), Opt("planner_name"), Opt("custom_order_bill_no"),
- Opt("progress"), Opt("urgent")
- ],
- ["MDM_ITEM"] =
- [
- Req("ItemNum"), Req("Descr"),
- Opt("Drawing"), Opt("UM"), Opt("ItemType"), Opt("Status"), Opt("Domain")
- ],
- ["S5_INVENTORY_TXN"] =
- [
- Req("Id"), Opt("idid"), Opt("code"), Opt("sl"), Opt("slzx"),
- Opt("shyn"), Opt("shtime"), Opt("date0"), Opt("lynoid"),
- Opt("gdyn"), Opt("gdtime"), Opt("rwnoid"), Opt("jhdate"), Opt("addtime")
- ],
- ["S5_INVENTORY_OPENING_BALANCE"] =
- [
- Req("Id"), Opt("idid"), Opt("code"), Opt("sl"), Opt("slzx"),
- Opt("shyn"), Opt("shtime"), Opt("date0")
- ],
- ["S7_FINISHED_ONHAND"] =
- [
- Req("Id"), Opt("code"), Opt("sl"), Opt("date0"), Opt("location")
- ],
- ["S7_FINISHED_OPENING_BALANCE"] =
- [
- Req("Id"), Opt("code"), Opt("sl"), Opt("date0"), Opt("location")
- ],
- ["MDM_EMPLOYEE_HEADCOUNT"] =
- [
- Req("Domain"), Req("Employee"),
- Opt("Name"), Opt("Department")
- ]
- };
- private static readonly MdpInboundSchemaField[] CommonContractFields =
- [
- Req("sourceUpdatedAt"),
- Opt("sourceVersion"),
- Opt("op")
- ];
- private readonly ISqlSugarClient _db;
- public MdpInboundFieldMapper(ISqlSugarClient db) => _db = db;
- public async Task<IReadOnlyList<MdpFieldMap>> LoadAsync(
- long tenantId, string entityCode, string accessKey, CancellationToken ct)
- {
- var code = (entityCode ?? string.Empty).Trim().ToUpperInvariant();
- var rows = await _db.Queryable<MdpFieldMap>()
- .Where(m => m.TenantId == tenantId && m.Status == 1)
- .Where(m => m.AccessKey == accessKey || m.AccessKey == "*")
- .Where("UPPER(entity_code) = @code", new SugarParameter("@code", code))
- .ToListAsync(ct);
- var merged = new Dictionary<string, MdpFieldMap>(StringComparer.OrdinalIgnoreCase);
- foreach (var row in rows.Where(r => r.AccessKey == "*"))
- merged[row.ContractField] = row;
- foreach (var row in rows.Where(r => r.AccessKey != "*"))
- merged[row.ContractField] = row;
- return merged.Values.ToList();
- }
- /// <summary>把对外规范字段改写成 raw 键;未登记字段原样保留。输出键名以映射表 raw_key 大小写为准。</summary>
- public Dictionary<string, object?> MapRow(
- IDictionary<string, object?> row, IReadOnlyList<MdpFieldMap> maps)
- {
- var byContract = maps.ToDictionary(m => m.ContractField, StringComparer.OrdinalIgnoreCase);
- var output = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
- foreach (var kv in row)
- {
- if (byContract.TryGetValue(kv.Key, out var map) && !string.IsNullOrWhiteSpace(map.RawKey))
- output[map.RawKey] = kv.Value;
- else
- output[kv.Key] = kv.Value;
- }
- return output;
- }
- public List<string> MissingRequired(IDictionary<string, object?> mapped, IReadOnlyList<MdpFieldMap> maps)
- {
- var missing = new List<string>();
- foreach (var map in maps.Where(m => m.IsRequired == 1))
- {
- var key = string.IsNullOrWhiteSpace(map.RawKey) ? map.ContractField : map.RawKey;
- if (!TryGetNonEmpty(mapped, key))
- missing.Add(map.ContractField);
- }
- return missing;
- }
- public MdpInboundSchemaData BuildSchema(
- MdpEntity entity, IReadOnlyList<MdpFieldMap> maps)
- {
- var fields = new List<MdpInboundSchemaField>();
- var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
- void Add(MdpInboundSchemaField f)
- {
- if (seen.Add(f.Name))
- fields.Add(f);
- }
- if (maps.Count > 0)
- {
- foreach (var map in maps)
- {
- Add(new MdpInboundSchemaField
- {
- Name = map.ContractField,
- RawKey = string.IsNullOrWhiteSpace(map.RawKey) ? map.ContractField : map.RawKey,
- Required = map.IsRequired == 1
- });
- }
- }
- else if (IdentityFields.TryGetValue(entity.EntityCode, out var identity))
- {
- foreach (var f in identity)
- Add(f);
- }
- else if (!string.IsNullOrWhiteSpace(entity.BizKeyExpr))
- {
- foreach (var part in entity.BizKeyExpr.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
- Add(Req(part));
- }
- foreach (var f in CommonContractFields)
- Add(f);
- return new MdpInboundSchemaData
- {
- EntityCode = entity.EntityCode,
- BizKeyExpr = entity.BizKeyExpr,
- Fields = fields
- };
- }
- public static Dictionary<string, object?> ElementToDict(JsonElement el)
- {
- var dict = new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase);
- if (el.ValueKind != JsonValueKind.Object)
- return dict;
- foreach (var prop in el.EnumerateObject())
- {
- dict[prop.Name] = prop.Value.ValueKind switch
- {
- JsonValueKind.Null => null,
- JsonValueKind.String => prop.Value.GetString(),
- JsonValueKind.Number => prop.Value.TryGetInt64(out var l) ? l
- : prop.Value.TryGetDecimal(out var d) ? d
- : prop.Value.GetDouble(),
- JsonValueKind.True => true,
- JsonValueKind.False => false,
- _ => prop.Value.GetRawText()
- };
- }
- return dict;
- }
- private static bool TryGetNonEmpty(IDictionary<string, object?> row, string key)
- {
- foreach (var kv in row)
- {
- if (!string.Equals(kv.Key, key, StringComparison.OrdinalIgnoreCase))
- continue;
- if (kv.Value == null || kv.Value == DBNull.Value)
- return false;
- return !string.IsNullOrWhiteSpace(kv.Value.ToString());
- }
- return false;
- }
- private static MdpInboundSchemaField Req(string name) =>
- new() { Name = name, RawKey = name, Required = true };
- private static MdpInboundSchemaField Opt(string name) =>
- new() { Name = name, RawKey = name, Required = false };
- }
|