SeOrderAuditHelper.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. using System.Text;
  2. using System.Text.Json;
  3. namespace Admin.NET.Plugin.AiDOP.Order;
  4. /// <summary>
  5. /// 销售订单保存前后快照与差异文本(用于操作日志)
  6. /// </summary>
  7. internal static class SeOrderAuditHelper
  8. {
  9. private static readonly JsonSerializerOptions JsonOpt = new()
  10. {
  11. WriteIndented = false,
  12. Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping
  13. };
  14. public static string SnapshotOrderJson(SeOrder o)
  15. {
  16. var dict = OrderToFlat(o);
  17. return JsonSerializer.Serialize(dict, JsonOpt);
  18. }
  19. public static string SnapshotEntriesJson(IEnumerable<SeOrderEntry> entries)
  20. {
  21. var list = entries
  22. .OrderBy(e => e.EntrySeq ?? 0)
  23. .Select(EntryToFlat)
  24. .ToList();
  25. return JsonSerializer.Serialize(list, JsonOpt);
  26. }
  27. public static string BuildCombinedSnapshotJson(SeOrder? order, List<SeOrderEntry> entries)
  28. {
  29. var root = new
  30. {
  31. header = order == null ? null : OrderToFlat(order),
  32. entries = entries.OrderBy(e => e.EntrySeq ?? 0).Select(EntryToFlat).ToList()
  33. };
  34. return JsonSerializer.Serialize(root, JsonOpt);
  35. }
  36. /// <summary>生成「变动前 / 变动后」可读文本 + 结构化 JSON</summary>
  37. public static (string Content, string? BeforeJson, string? AfterJson) BuildUpdateDiff(
  38. SeOrder beforeOrder,
  39. List<SeOrderEntry> beforeEntries,
  40. SeOrder afterOrder,
  41. List<SeOrderEntry> afterEntries)
  42. {
  43. var beforeCombined = BuildCombinedSnapshotJson(beforeOrder, beforeEntries);
  44. var afterCombined = BuildCombinedSnapshotJson(afterOrder, afterEntries);
  45. var sb = new StringBuilder();
  46. sb.Append("修改订单,订单编号:").Append(afterOrder.BillNo ?? "—").Append('。').AppendLine();
  47. var hBefore = OrderToFlat(beforeOrder);
  48. var hAfter = OrderToFlat(afterOrder);
  49. foreach (var key in hBefore.Keys.Union(hAfter.Keys))
  50. {
  51. hBefore.TryGetValue(key, out var ov);
  52. hAfter.TryGetValue(key, out var nv);
  53. if (string.Equals(ov, nv, StringComparison.Ordinal)) continue;
  54. sb.Append(" 【").Append(key).Append("】变动前:「").Append(ov ?? "—").Append("」→ 变动后:「").Append(nv ?? "—").AppendLine("」");
  55. }
  56. var beforeById = beforeEntries.ToDictionary(e => e.Id);
  57. var afterById = afterEntries.ToDictionary(e => e.Id);
  58. foreach (var id in beforeById.Keys.Union(afterById.Keys))
  59. {
  60. beforeById.TryGetValue(id, out var eb);
  61. afterById.TryGetValue(id, out var ea);
  62. if (eb == null && ea != null)
  63. {
  64. sb.Append(" 【新增明细】行").Append(ea.EntrySeq).Append(" 物料:").AppendLine(ea.ItemNumber ?? "—");
  65. continue;
  66. }
  67. if (eb != null && ea == null)
  68. {
  69. sb.Append(" 【删除明细】行").Append(eb.EntrySeq).Append(" 物料:").AppendLine(eb.ItemNumber ?? "—");
  70. continue;
  71. }
  72. if (eb == null || ea == null) continue;
  73. var fb = EntryToFlat(eb);
  74. var fa = EntryToFlat(ea);
  75. var lineChanged = false;
  76. foreach (var key in fb.Keys.Union(fa.Keys))
  77. {
  78. fb.TryGetValue(key, out var ov);
  79. fa.TryGetValue(key, out var nv);
  80. if (string.Equals(ov, nv, StringComparison.Ordinal)) continue;
  81. if (!lineChanged)
  82. {
  83. sb.Append(" 【明细行").Append(ea.EntrySeq).Append("】物料 ").Append(ea.ItemNumber ?? "—").AppendLine();
  84. lineChanged = true;
  85. }
  86. sb.Append(" ").Append(key).Append(":变动前「").Append(ov ?? "—").Append("」→ 变动后「").Append(nv ?? "—").AppendLine("」");
  87. }
  88. }
  89. var content = sb.ToString().TrimEnd();
  90. if (content.IndexOf('→') < 0 && content.IndexOf("【新增明细】", StringComparison.Ordinal) < 0 && content.IndexOf("【删除明细】", StringComparison.Ordinal) < 0)
  91. content = "修改订单,订单编号:" + (afterOrder.BillNo ?? "—") + "。(主表与明细字段无变化)";
  92. return (content, beforeCombined, afterCombined);
  93. }
  94. private static Dictionary<string, string?> OrderToFlat(SeOrder o) => new()
  95. {
  96. ["订单编号"] = o.BillNo,
  97. ["订单类型"] = o.OrderType switch { 1 => "销售", 2 => "计划", _ => o.OrderType?.ToString() },
  98. ["客户编码"] = o.CustomNo,
  99. ["客户名称"] = o.CustomName,
  100. ["签订日期"] = o.Date?.ToString("yyyy-MM-dd"),
  101. ["客户级别"] = o.CustomLevel?.ToString(),
  102. ["加急"] = o.Urgent == 1 ? "加急" : "普通",
  103. ["客户订单号"] = o.BillFrom,
  104. ["国家"] = o.Country,
  105. ["客户下单日期"] = o.RDate?.ToString("yyyy-MM-dd"),
  106. ["流程状态"] = o.FlowState,
  107. };
  108. private static Dictionary<string, string?> EntryToFlat(SeOrderEntry e) => new()
  109. {
  110. ["明细Id"] = e.Id.ToString(),
  111. ["行号"] = e.EntrySeq?.ToString(),
  112. ["物料编码"] = e.ItemNumber,
  113. ["物料名称"] = e.ItemName,
  114. ["规格"] = e.Specification,
  115. ["单位"] = e.Unit,
  116. ["数量"] = e.Qty?.ToString("0.######"),
  117. ["客户要求交期"] = e.PlanDate?.ToString("yyyy-MM-dd"),
  118. ["最终交货日期"] = e.Date?.ToString("yyyy-MM-dd"),
  119. ["系统建议交期"] = e.SysCapacityDate?.ToString("yyyy-MM-dd"),
  120. ["备注"] = e.Remark,
  121. ["进度"] = e.Progress switch { 0 => "再评审", 1 => "新建", 2 => "评审", 3 => "确认", _ => e.Progress?.ToString() },
  122. ["交付数量"] = e.DeliverCount?.ToString("0.######"),
  123. };
  124. }