SmartDiagnosisEvidenceRegistry.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. namespace Admin.NET.Plugin.AiDOP.SmartOps;
  2. public enum SmartDiagnosisEvidenceDomain
  3. {
  4. Delivery,
  5. Supply,
  6. Manufacturing,
  7. Quality,
  8. Exception
  9. }
  10. public sealed record SmartDiagnosisEvidenceFilterColumn(string QueryKey, string SqlColumn);
  11. public sealed record SmartDiagnosisEvidenceSource(
  12. string Key,
  13. SmartDiagnosisEvidenceDomain Domain,
  14. IReadOnlyList<string> Modules,
  15. string ObjectType,
  16. string DrillPath,
  17. string CountSql,
  18. string ItemSql,
  19. IReadOnlyList<SmartDiagnosisEvidenceFilterColumn> Filters);
  20. public static class SmartDiagnosisEvidenceRegistry
  21. {
  22. public const int DefaultLimit = 20;
  23. public const int MaxLimit = 50;
  24. private static readonly SmartDiagnosisEvidenceSource[] Sources =
  25. {
  26. new(
  27. "order_schedule",
  28. SmartDiagnosisEvidenceDomain.Manufacturing,
  29. new[] { "S1", "S2", "S6" },
  30. "work_order",
  31. "/aidop/s2/work-order-scheduling",
  32. """
  33. SELECT COUNT(*) FROM dwd_order_schedule_trans
  34. WHERE tenant_id=@tenantId
  35. AND (factory_id IS NULL OR factory_id=0 OR factory_id=1 OR factory_id=@factoryId)
  36. AND (factory_id IS NULL OR factory_id<>@tenantId)
  37. AND IFNULL(work_order,'')<>''
  38. """,
  39. """
  40. SELECT work_order AS ObjectCode,
  41. CONCAT(IFNULL(sales_order_no,''),' / ',IFNULL(work_order,''),' / ',IFNULL(item_code,'')) AS Title,
  42. GREATEST(IFNULL(qty_ordered,0)-IFNULL(qty_completed,0),0) AS Contribution,
  43. CASE
  44. WHEN IFNULL(schedule_satisfaction_flag,1)=0 OR (due_date IS NOT NULL AND due_date<CURDATE()) THEN 'red'
  45. WHEN IFNULL(wip_qty,0)>0 THEN 'yellow'
  46. ELSE 'green'
  47. END AS Status,
  48. COALESCE(due_date, stat_date) AS OccurredAt
  49. FROM dwd_order_schedule_trans
  50. WHERE tenant_id=@tenantId
  51. AND (factory_id IS NULL OR factory_id=0 OR factory_id=1 OR factory_id=@factoryId)
  52. AND (factory_id IS NULL OR factory_id<>@tenantId)
  53. AND IFNULL(work_order,'')<>''
  54. ORDER BY Contribution DESC, OccurredAt
  55. LIMIT @limit
  56. """,
  57. new[]
  58. {
  59. new SmartDiagnosisEvidenceFilterColumn("workOrder", "work_order"),
  60. new SmartDiagnosisEvidenceFilterColumn("orderNo", "sales_order_no"),
  61. new SmartDiagnosisEvidenceFilterColumn("material", "item_code"),
  62. new SmartDiagnosisEvidenceFilterColumn("productionLine", "prod_line"),
  63. }),
  64. new(
  65. "supplier_delivery",
  66. SmartDiagnosisEvidenceDomain.Supply,
  67. new[] { "S3", "S4" },
  68. "purchase_order",
  69. "/aidop/s4/purchase-execution",
  70. """
  71. SELECT COUNT(*) FROM dwd_supplier_delivery
  72. WHERE tenant_id=@tenantId
  73. AND (factory_id IS NULL OR factory_id=0 OR factory_id=1 OR factory_id=@factoryId)
  74. AND (factory_id IS NULL OR factory_id<>@tenantId)
  75. AND IFNULL(po_no,'')<>''
  76. """,
  77. """
  78. SELECT po_no AS ObjectCode,
  79. CONCAT(IFNULL(supplier_name,supplier_code),' / ',IFNULL(item_code,''),' / ',IFNULL(po_no,'')) AS Title,
  80. GREATEST(IFNULL(remaining_qty,0),0) AS Contribution,
  81. CASE
  82. WHEN UPPER(IFNULL(risk_level,'')) IN ('HIGH','RED')
  83. OR UPPER(IFNULL(delivery_status,'')) IN ('OVERDUE','DELAYED','LATE') THEN 'red'
  84. WHEN IFNULL(remaining_qty,0)>0 THEN 'yellow'
  85. ELSE 'green'
  86. END AS Status,
  87. COALESCE(due_date, stat_date) AS OccurredAt
  88. FROM dwd_supplier_delivery
  89. WHERE tenant_id=@tenantId
  90. AND (factory_id IS NULL OR factory_id=0 OR factory_id=1 OR factory_id=@factoryId)
  91. AND (factory_id IS NULL OR factory_id<>@tenantId)
  92. AND IFNULL(po_no,'')<>''
  93. ORDER BY Contribution DESC, OccurredAt
  94. LIMIT @limit
  95. """,
  96. new[]
  97. {
  98. new SmartDiagnosisEvidenceFilterColumn("poNo", "po_no"),
  99. new SmartDiagnosisEvidenceFilterColumn("supplier", "supplier_code"),
  100. new SmartDiagnosisEvidenceFilterColumn("material", "item_code"),
  101. }),
  102. new(
  103. "material_readiness",
  104. SmartDiagnosisEvidenceDomain.Supply,
  105. new[] { "S3" },
  106. "work_order",
  107. "/aidop/s3/material-readiness",
  108. """
  109. SELECT COUNT(*) FROM dwd_material_readiness
  110. WHERE tenant_id=@tenantId
  111. AND (factory_id IS NULL OR factory_id=0 OR factory_id=1 OR factory_id=@factoryId)
  112. AND (factory_id IS NULL OR factory_id<>@tenantId)
  113. AND IFNULL(shortage_qty,0)>0
  114. """,
  115. """
  116. SELECT work_order AS ObjectCode,
  117. CONCAT('缺料 ',IFNULL(work_order,''),' / ',IFNULL(supplier_code,'')) AS Title,
  118. IFNULL(shortage_qty,0) AS Contribution,
  119. 'red' AS Status,
  120. stat_date AS OccurredAt
  121. FROM dwd_material_readiness
  122. WHERE tenant_id=@tenantId
  123. AND (factory_id IS NULL OR factory_id=0 OR factory_id=1 OR factory_id=@factoryId)
  124. AND (factory_id IS NULL OR factory_id<>@tenantId)
  125. AND IFNULL(shortage_qty,0)>0
  126. ORDER BY Contribution DESC, OccurredAt
  127. LIMIT @limit
  128. """,
  129. new[]
  130. {
  131. new SmartDiagnosisEvidenceFilterColumn("workOrder", "work_order"),
  132. new SmartDiagnosisEvidenceFilterColumn("supplier", "supplier_code"),
  133. }),
  134. new(
  135. "s6_report",
  136. SmartDiagnosisEvidenceDomain.Manufacturing,
  137. new[] { "S6" },
  138. "work_order",
  139. "/aidop/s6/report-work",
  140. """
  141. SELECT COUNT(*) FROM mdp_std_s6_report
  142. WHERE tenant_id=@tenantId
  143. AND (factory_id IS NULL OR factory_id=0 OR factory_id=1 OR factory_id=@factoryId)
  144. AND (factory_id IS NULL OR factory_id<>@tenantId)
  145. AND IFNULL(work_order_no,'')<>''
  146. """,
  147. """
  148. SELECT work_order_no AS ObjectCode,
  149. CONCAT('报工 ',IFNULL(work_order_no,'')) AS Title,
  150. IFNULL(report_qty,0) AS Contribution,
  151. 'yellow' AS Status,
  152. report_date AS OccurredAt
  153. FROM mdp_std_s6_report
  154. WHERE tenant_id=@tenantId
  155. AND (factory_id IS NULL OR factory_id=0 OR factory_id=1 OR factory_id=@factoryId)
  156. AND (factory_id IS NULL OR factory_id<>@tenantId)
  157. AND IFNULL(work_order_no,'')<>''
  158. ORDER BY Contribution DESC, OccurredAt
  159. LIMIT @limit
  160. """,
  161. new[]
  162. {
  163. new SmartDiagnosisEvidenceFilterColumn("workOrder", "work_order_no"),
  164. }),
  165. new(
  166. "iqc_bill",
  167. SmartDiagnosisEvidenceDomain.Quality,
  168. new[] { "S5" },
  169. "inspection_bill",
  170. "/aidop/s5/iqc",
  171. """
  172. SELECT COUNT(*) FROM qms_qcp_inspbill
  173. WHERE tenant_id=@tenantId AND IFNULL(FBILLNO,'')<>''
  174. """,
  175. """
  176. SELECT FBILLNO AS ObjectCode,
  177. CONCAT('IQC ',IFNULL(FBILLNO,''),' / ',IFNULL(FBILLSTATUS,'')) AS Title,
  178. 1 AS Contribution,
  179. CASE WHEN UPPER(IFNULL(FBILLSTATUS,'')) IN ('NG','REJECT','UNQUALIFIED') THEN 'red' ELSE 'yellow' END AS Status,
  180. NULL AS OccurredAt
  181. FROM qms_qcp_inspbill
  182. WHERE tenant_id=@tenantId AND IFNULL(FBILLNO,'')<>''
  183. ORDER BY id DESC
  184. LIMIT @limit
  185. """,
  186. Array.Empty<SmartDiagnosisEvidenceFilterColumn>()),
  187. new(
  188. "fqc_result",
  189. SmartDiagnosisEvidenceDomain.Quality,
  190. new[] { "S7" },
  191. "inspection_bill",
  192. "/aidop/s7/fqc",
  193. """
  194. SELECT COUNT(*) FROM mdp_std_fqc_result
  195. WHERE tenant_id=@tenantId AND IFNULL(bill_no,'')<>''
  196. """,
  197. """
  198. SELECT bill_no AS ObjectCode,
  199. CONCAT('FQC ',IFNULL(bill_no,''),' / ',IFNULL(material_code,'')) AS Title,
  200. GREATEST(IFNULL(unqualified_qty,0),0) AS Contribution,
  201. CASE WHEN IFNULL(unqualified_qty,0)>0 OR UPPER(IFNULL(judgment,'')) IN ('NG','FAIL','UNQUALIFIED') THEN 'red' ELSE 'green' END AS Status,
  202. inspect_time AS OccurredAt
  203. FROM mdp_std_fqc_result
  204. WHERE tenant_id=@tenantId AND IFNULL(bill_no,'')<>''
  205. ORDER BY Contribution DESC, OccurredAt
  206. LIMIT @limit
  207. """,
  208. new[]
  209. {
  210. new SmartDiagnosisEvidenceFilterColumn("material", "material_code"),
  211. new SmartDiagnosisEvidenceFilterColumn("workOrder", "sap_work_order_no"),
  212. }),
  213. new(
  214. "s8_exception",
  215. SmartDiagnosisEvidenceDomain.Exception,
  216. new[] { "S8" },
  217. "s8_exception",
  218. "/aidop/s8/exceptions",
  219. """
  220. SELECT COUNT(*) FROM ado_s8_exception
  221. WHERE tenant_id=@tenantId AND IFNULL(is_deleted,0)=0
  222. AND IFNULL(exception_code,'')<>''
  223. AND (factory_id IS NULL OR factory_id=0 OR factory_id=1 OR factory_id=@factoryId)
  224. AND (factory_id IS NULL OR factory_id<>@tenantId)
  225. """,
  226. """
  227. SELECT exception_code AS ObjectCode,
  228. CONCAT(IFNULL(exception_code,''),' / ',IFNULL(title,'')) AS Title,
  229. GREATEST(IFNULL(priority_score,0),0) AS Contribution,
  230. CASE
  231. WHEN UPPER(IFNULL(status,'')) IN ('CLOSED','CANCELLED','CANCELED') THEN 'green'
  232. WHEN UPPER(IFNULL(severity,'')) IN ('SERIOUS','CRITICAL','HIGH') THEN 'red'
  233. ELSE 'yellow'
  234. END AS Status,
  235. created_at AS OccurredAt
  236. FROM ado_s8_exception
  237. WHERE tenant_id=@tenantId AND IFNULL(is_deleted,0)=0
  238. AND IFNULL(exception_code,'')<>''
  239. AND (factory_id IS NULL OR factory_id=0 OR factory_id=1 OR factory_id=@factoryId)
  240. AND (factory_id IS NULL OR factory_id<>@tenantId)
  241. ORDER BY Contribution DESC, OccurredAt
  242. LIMIT @limit
  243. """,
  244. new[]
  245. {
  246. new SmartDiagnosisEvidenceFilterColumn("orderNo", "related_object_code"),
  247. }),
  248. };
  249. public static IReadOnlyList<SmartDiagnosisEvidenceSource> All => Sources;
  250. public static IReadOnlyList<SmartDiagnosisEvidenceSource> Resolve(string moduleCode)
  251. {
  252. var mc = (moduleCode ?? "").Trim().ToUpperInvariant();
  253. if (mc == "S9")
  254. return Sources.Where(x => x.Key != "s8_exception").ToList();
  255. return Sources.Where(x => x.Modules.Contains(mc, StringComparer.OrdinalIgnoreCase)).ToList();
  256. }
  257. public static SmartDiagnosisEvidenceSource? Get(string key) =>
  258. Sources.FirstOrDefault(x => string.Equals(x.Key, key, StringComparison.OrdinalIgnoreCase));
  259. }
  260. public sealed class SmartDiagnosisEvidenceItem
  261. {
  262. public string ObjectType { get; set; } = "";
  263. public string ObjectCode { get; set; } = "";
  264. public string Title { get; set; } = "";
  265. public decimal? Contribution { get; set; }
  266. public string ContributionUnit { get; set; } = "";
  267. public string Status { get; set; } = "green";
  268. public string? OccurredAt { get; set; }
  269. public string? DrillPath { get; set; }
  270. }
  271. public sealed class SmartDiagnosisEvidenceResult
  272. {
  273. public string Scope { get; set; } = "no_data";
  274. public string? AsOf { get; set; }
  275. public string Domain { get; set; } = "";
  276. public int Total { get; set; }
  277. public int Returned { get; set; }
  278. public bool Truncated { get; set; }
  279. public List<string> Sources { get; set; } = new();
  280. public List<SmartDiagnosisEvidenceItem> Items { get; set; } = new();
  281. }
  282. internal sealed class SmartDiagnosisEvidenceSqlRow
  283. {
  284. public string? ObjectCode { get; set; }
  285. public string? Title { get; set; }
  286. public decimal? Contribution { get; set; }
  287. public string? Status { get; set; }
  288. public DateTime? OccurredAt { get; set; }
  289. }