S2MdpSyncTransformService.cs 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. namespace Admin.NET.Plugin.AiDOP.Production;
  2. /// <summary>
  3. /// S2 生产排程 MDP 同步、标准化、DWD 与 KPI 计算服务。
  4. /// </summary>
  5. public class S2MdpSyncTransformService : ITransient
  6. {
  7. private const string JobCode = "S2_MDP_SYNC_TRANSFORM";
  8. private readonly ISqlSugarClient _db;
  9. public S2MdpSyncTransformService(ISqlSugarClient db)
  10. {
  11. _db = db;
  12. }
  13. public async Task<S2MdpSyncTransformResult> RunFullAsync(CancellationToken cancellationToken = default, string triggerType = "AUTO")
  14. {
  15. cancellationToken.ThrowIfCancellationRequested();
  16. var now = DateTime.Now;
  17. var batchId = $"S2_MDP_FULL_{now:yyyyMMddHHmmss}";
  18. var runLogId = await InsertTransformRunLogAsync(batchId, now, triggerType);
  19. var result = new S2MdpSyncTransformResult { BatchId = batchId, RunLogId = runLogId };
  20. try
  21. {
  22. await EnsureS2RuntimeObjectsAsync();
  23. result.StageRows = await SyncStagingAsync(batchId, now, cancellationToken);
  24. result.StandardRows = await TransformStandardAsync(batchId, now, cancellationToken);
  25. result.DwdRows = await BuildDwdAsync(batchId, now, cancellationToken);
  26. result.KpiRows = await BuildS2KpiValuesAsync(batchId, now, cancellationToken);
  27. await MarkTransformRunSuccessAsync(runLogId, now, result);
  28. return result;
  29. }
  30. catch (Exception ex)
  31. {
  32. await MarkTransformRunFailedAsync(runLogId, now, ex.Message);
  33. throw;
  34. }
  35. }
  36. private async Task EnsureS2RuntimeObjectsAsync()
  37. {
  38. foreach (var sql in S2MdpDdl.SqlBlocks)
  39. {
  40. await _db.Ado.ExecuteCommandAsync(sql);
  41. }
  42. }
  43. private async Task<int> SyncStagingAsync(string batchId, DateTime now, CancellationToken cancellationToken)
  44. {
  45. var total = 0;
  46. foreach (var entity in S2MdpEntityConfig.All)
  47. {
  48. cancellationToken.ThrowIfCancellationRequested();
  49. total += await SyncOneEntityAsync(entity, batchId, now);
  50. }
  51. return total;
  52. }
  53. private async Task<int> SyncOneEntityAsync(S2MdpEntityConfig entity, string batchId, DateTime now)
  54. {
  55. var entityRow = await _db.Ado.SqlQuerySingleAsync<S2MdpEntityRow>(
  56. "SELECT id AS Id, entity_name AS EntityName FROM mdp_entity WHERE tenant_id=0 AND entity_code=@EntityCode LIMIT 1",
  57. new SugarParameter("@EntityCode", entity.EntityCode));
  58. if (entityRow == null) throw Oops.Oh($"未找到 MDP 实体配置:{entity.EntityCode}");
  59. var columns = await _db.Ado.SqlQueryAsync<S2ColumnRow>(
  60. """
  61. SELECT COLUMN_NAME AS ColumnName
  62. FROM information_schema.COLUMNS
  63. WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=@TableName
  64. ORDER BY ORDINAL_POSITION
  65. """,
  66. new SugarParameter("@TableName", entity.SourceTable));
  67. if (columns.Count == 0) throw Oops.Oh($"未找到源表:{entity.SourceTable}");
  68. var names = columns.Select(u => u.ColumnName).ToList();
  69. var tenantExpr = names.Any(u => string.Equals(u, "tenant_id", StringComparison.OrdinalIgnoreCase))
  70. ? $"IFNULL(s.`{FindColumn(names, "tenant_id")}`,0)"
  71. : "0";
  72. var factoryExpr = names.Any(u => string.Equals(u, "factory_id", StringComparison.OrdinalIgnoreCase))
  73. ? $"s.`{FindColumn(names, "factory_id")}`"
  74. : "NULL";
  75. var domainExpr = names.Any(u => string.Equals(u, "Domain", StringComparison.OrdinalIgnoreCase))
  76. ? $"s.`{FindColumn(names, "Domain")}`"
  77. : "NULL";
  78. var sourceRowExpr = names.Any(u => string.Equals(u, entity.SourceRowIdExpression, StringComparison.OrdinalIgnoreCase))
  79. ? $"s.`{FindColumn(names, entity.SourceRowIdExpression)}`"
  80. : entity.SourceRowIdExpression;
  81. var rawDataExpr = BuildJsonObjectExpression(names);
  82. var rowsRead = await _db.Ado.GetIntAsync($"SELECT COUNT(1) FROM `{entity.SourceTable}`");
  83. var logId = await InsertSyncLogAsync(entityRow.Id, entityRow.EntityName, batchId, rowsRead);
  84. var started = DateTime.Now;
  85. try
  86. {
  87. var affected = await _db.Ado.ExecuteCommandAsync(
  88. $"""
  89. INSERT INTO `{entity.TargetTable}`
  90. (tenant_id, factory_id, source_system, source_table, source_row_id, source_biz_key, sync_batch_id, sync_time, process_status, raw_data)
  91. SELECT
  92. {tenantExpr},
  93. COALESCE({factoryExpr}, {domainExpr}),
  94. 'AIDOP',
  95. @SourceTable,
  96. CAST({sourceRowExpr} AS CHAR),
  97. CAST(COALESCE({entity.SourceBizKeyExpression}, CAST({sourceRowExpr} AS CHAR)) AS CHAR),
  98. @BatchId,
  99. @Now,
  100. 'PENDING',
  101. {rawDataExpr}
  102. FROM `{entity.SourceTable}` s
  103. ON DUPLICATE KEY UPDATE
  104. tenant_id=VALUES(tenant_id),
  105. factory_id=VALUES(factory_id),
  106. sync_batch_id=VALUES(sync_batch_id),
  107. sync_time=VALUES(sync_time),
  108. process_status=VALUES(process_status),
  109. raw_data=VALUES(raw_data),
  110. update_time=CURRENT_TIMESTAMP
  111. """,
  112. new SugarParameter("@SourceTable", entity.SourceTable),
  113. new SugarParameter("@BatchId", batchId),
  114. new SugarParameter("@Now", now));
  115. await MarkSyncLogSuccessAsync(logId, started, affected);
  116. return rowsRead;
  117. }
  118. catch (Exception ex)
  119. {
  120. await MarkSyncLogFailedAsync(logId, started, ex.Message);
  121. throw;
  122. }
  123. }
  124. private async Task<int> TransformStandardAsync(string batchId, DateTime now, CancellationToken cancellationToken)
  125. {
  126. var total = 0;
  127. foreach (var command in BuildStandardCommands(batchId, now))
  128. {
  129. cancellationToken.ThrowIfCancellationRequested();
  130. total += await _db.Ado.ExecuteCommandAsync(command.Sql, command.Parameters);
  131. }
  132. return total;
  133. }
  134. private async Task<int> BuildDwdAsync(string batchId, DateTime now, CancellationToken cancellationToken)
  135. {
  136. var total = 0;
  137. foreach (var command in BuildDwdCommands(batchId, now))
  138. {
  139. cancellationToken.ThrowIfCancellationRequested();
  140. total += await _db.Ado.ExecuteCommandAsync(command.Sql, command.Parameters);
  141. }
  142. return total;
  143. }
  144. private async Task<int> BuildS2KpiValuesAsync(string batchId, DateTime now, CancellationToken cancellationToken)
  145. {
  146. var rows = await CalculateS2KpiValuesAsync(batchId, now.Date);
  147. var affected = 0;
  148. foreach (var row in rows)
  149. {
  150. cancellationToken.ThrowIfCancellationRequested();
  151. affected += await UpsertS2KpiValueAsync(row, now.Date, now);
  152. }
  153. return affected;
  154. }
  155. private IEnumerable<S2MdpSqlCommand> BuildStandardCommands(string batchId, DateTime now)
  156. {
  157. yield return Cmd(
  158. """
  159. INSERT INTO mdp_std_work_order_schedule
  160. (tenant_id, factory_id, source_system, work_order, sales_order_no, item_code, item_name, site_code, status,
  161. priority, urgent_flag, qty_ordered, qty_completed, order_date, due_date, release_date, prod_line,
  162. source_biz_key, sync_batch_id, sync_time)
  163. SELECT tenant_id,
  164. CASE WHEN factory_id REGEXP '^[0-9]+$' THEN CAST(factory_id AS UNSIGNED) ELSE 1 END,
  165. 'AIDOP',
  166. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkOrd')),
  167. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.SalesJob')),
  168. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.ItemNum')),
  169. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.ItemName')),
  170. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.Site')),
  171. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.Status')),
  172. CASE WHEN JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.Priority')) REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.Priority')) AS DECIMAL(18,6)) END,
  173. CASE WHEN JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.Urgent')) IN ('1','true','True') THEN 1 ELSE 0 END,
  174. CASE WHEN JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.QtyOrded')) REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.QtyOrded')) AS DECIMAL(18,6)) ELSE 0 END,
  175. CASE WHEN JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.QtyCompleted')) REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.QtyCompleted')) AS DECIMAL(18,6)) ELSE 0 END,
  176. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.OrdDate')), 'null'), ''),
  177. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.DueDate')), 'null'), ''),
  178. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.ReleaseDate')), 'null'), ''),
  179. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.ProdLine')),
  180. source_biz_key, @BatchId, @Now
  181. FROM mdp_stg_schedule
  182. WHERE source_table='WorkOrdMaster' AND IFNULL(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkOrd')), '') <> ''
  183. ON DUPLICATE KEY UPDATE
  184. sales_order_no=VALUES(sales_order_no), item_code=VALUES(item_code), item_name=VALUES(item_name),
  185. site_code=VALUES(site_code), status=VALUES(status), priority=VALUES(priority), urgent_flag=VALUES(urgent_flag),
  186. qty_ordered=VALUES(qty_ordered), qty_completed=VALUES(qty_completed), order_date=VALUES(order_date),
  187. due_date=VALUES(due_date), release_date=VALUES(release_date), prod_line=VALUES(prod_line),
  188. sync_batch_id=VALUES(sync_batch_id), sync_time=VALUES(sync_time), update_time=CURRENT_TIMESTAMP
  189. """, batchId, now);
  190. yield return Cmd(
  191. """
  192. INSERT INTO mdp_std_operation_schedule
  193. (tenant_id, factory_id, source_system, work_order, op_no, work_center, line_code, item_code,
  194. plan_date, prod_date, start_time, end_time, ord_qty, comp_qty, run_crew, employee,
  195. source_biz_key, sync_batch_id, sync_time)
  196. SELECT tenant_id,
  197. CASE WHEN factory_id REGEXP '^[0-9]+$' THEN CAST(factory_id AS UNSIGNED) ELSE 1 END,
  198. 'AIDOP',
  199. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkOrds')),
  200. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.Op')),
  201. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkCtr')),
  202. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.Line')),
  203. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.ItemNum')),
  204. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.PlanDate')), 'null'), ''),
  205. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.ProdDate')), 'null'), ''),
  206. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.StartTime')), 'null'), ''),
  207. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.EndTime')), 'null'), ''),
  208. CASE WHEN JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.OrdQty')) REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.OrdQty')) AS DECIMAL(18,6)) ELSE 0 END,
  209. CASE WHEN JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.CompQty')) REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.CompQty')) AS DECIMAL(18,6)) ELSE 0 END,
  210. CASE WHEN JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.RunCrew')) REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.RunCrew')) AS DECIMAL(18,6)) END,
  211. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.Employee')),
  212. source_biz_key, @BatchId, @Now
  213. FROM mdp_stg_schedule
  214. WHERE source_table='PeriodSequenceDet' AND IFNULL(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkOrds')), '') <> ''
  215. ON DUPLICATE KEY UPDATE
  216. work_center=VALUES(work_center), line_code=VALUES(line_code), item_code=VALUES(item_code),
  217. plan_date=VALUES(plan_date), prod_date=VALUES(prod_date), start_time=VALUES(start_time), end_time=VALUES(end_time),
  218. ord_qty=VALUES(ord_qty), comp_qty=VALUES(comp_qty), run_crew=VALUES(run_crew), employee=VALUES(employee),
  219. sync_batch_id=VALUES(sync_batch_id), sync_time=VALUES(sync_time), update_time=CURRENT_TIMESTAMP
  220. """, batchId, now);
  221. yield return Cmd(
  222. """
  223. INSERT INTO mdp_std_operation_schedule
  224. (tenant_id, factory_id, source_system, work_order, op_no, work_center, line_code, item_code,
  225. plan_date, prod_date, start_time, end_time, ord_qty, comp_qty, run_crew, employee,
  226. source_biz_key, sync_batch_id, sync_time)
  227. SELECT tenant_id,
  228. CASE WHEN factory_id REGEXP '^[0-9]+$' THEN CAST(factory_id AS UNSIGNED) ELSE 1 END,
  229. 'AIDOP',
  230. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkOrd')),
  231. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.Op')),
  232. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkCtr')),
  233. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.Line')),
  234. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.ItemNum')),
  235. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkDate')), 'null'), ''),
  236. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkDate')), 'null'), ''),
  237. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkStartTime')), 'null'), ''),
  238. NULLIF(NULLIF(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkEndTime')), 'null'), ''),
  239. CASE WHEN JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkQty')) REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkQty')) AS DECIMAL(18,6)) ELSE 0 END,
  240. CASE WHEN JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkQty')) REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkQty')) AS DECIMAL(18,6)) ELSE 0 END,
  241. CASE WHEN JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.AssignedPersonnelCount')) REGEXP '^-?[0-9]+(\\.[0-9]+)?$' THEN CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.AssignedPersonnelCount')) AS DECIMAL(18,6)) END,
  242. JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.AssignedEmployeeID')),
  243. source_biz_key, @BatchId, @Now
  244. FROM mdp_stg_schedule
  245. WHERE source_table='ScheduleResultOpMaster' AND IFNULL(JSON_UNQUOTE(JSON_EXTRACT(raw_data,'$.WorkOrd')), '') <> ''
  246. ON DUPLICATE KEY UPDATE
  247. work_center=VALUES(work_center), line_code=VALUES(line_code), item_code=VALUES(item_code),
  248. plan_date=VALUES(plan_date), prod_date=VALUES(prod_date), start_time=VALUES(start_time), end_time=VALUES(end_time),
  249. ord_qty=VALUES(ord_qty), comp_qty=VALUES(comp_qty), run_crew=VALUES(run_crew), employee=VALUES(employee),
  250. sync_batch_id=VALUES(sync_batch_id), sync_time=VALUES(sync_time), update_time=CURRENT_TIMESTAMP
  251. """, batchId, now);
  252. }
  253. private IEnumerable<S2MdpSqlCommand> BuildDwdCommands(string batchId, DateTime now)
  254. {
  255. yield return Cmd(
  256. """
  257. INSERT INTO dwd_order_schedule_trans
  258. (tenant_id, factory_id, stat_date, work_order, sales_order_no, item_code, item_name, site_code, prod_line,
  259. status, urgent_flag, qty_ordered, qty_completed, order_date, due_date, release_date, first_plan_date,
  260. last_plan_date, first_start_time, last_end_time, operation_count, scheduled_qty, completed_op_qty,
  261. schedule_cycle_days, schedule_satisfaction_flag, wip_qty, resource_person_count, calc_batch_id, calc_time)
  262. SELECT w.tenant_id, COALESCE(w.factory_id, 1), @StatDate,
  263. w.work_order, w.sales_order_no, w.item_code, w.item_name, w.site_code, w.prod_line,
  264. w.status, w.urgent_flag, w.qty_ordered, w.qty_completed, w.order_date, w.due_date, w.release_date,
  265. MIN(o.plan_date), MAX(o.plan_date), MIN(o.start_time), MAX(o.end_time),
  266. COUNT(o.id), SUM(IFNULL(o.ord_qty, 0)), SUM(IFNULL(o.comp_qty, 0)),
  267. CASE
  268. WHEN COALESCE(MIN(o.plan_date), MAX(o.plan_date)) IS NOT NULL
  269. AND COALESCE(w.release_date, w.order_date) IS NOT NULL
  270. THEN TIMESTAMPDIFF(HOUR, COALESCE(w.release_date, w.order_date), COALESCE(MAX(o.plan_date), MIN(o.plan_date))) / 24
  271. END,
  272. CASE
  273. WHEN w.due_date IS NOT NULL AND COALESCE(MAX(o.plan_date), MIN(o.plan_date)) IS NOT NULL
  274. AND DATE(COALESCE(MAX(o.plan_date), MIN(o.plan_date))) <= DATE(w.due_date) THEN 1
  275. ELSE 0
  276. END,
  277. GREATEST(IFNULL(w.qty_ordered, 0) - IFNULL(w.qty_completed, 0), 0),
  278. SUM(IFNULL(o.run_crew, 0)),
  279. @BatchId, @Now
  280. FROM mdp_std_work_order_schedule w
  281. LEFT JOIN mdp_std_operation_schedule o
  282. ON o.tenant_id=w.tenant_id AND IFNULL(o.work_order,'')=IFNULL(w.work_order,'')
  283. WHERE IFNULL(w.work_order, '') <> ''
  284. GROUP BY w.tenant_id, COALESCE(w.factory_id, 1), w.work_order, w.sales_order_no, w.item_code, w.item_name,
  285. w.site_code, w.prod_line, w.status, w.urgent_flag, w.qty_ordered, w.qty_completed,
  286. w.order_date, w.due_date, w.release_date
  287. ON DUPLICATE KEY UPDATE
  288. sales_order_no=VALUES(sales_order_no), item_code=VALUES(item_code), item_name=VALUES(item_name),
  289. site_code=VALUES(site_code), prod_line=VALUES(prod_line), status=VALUES(status), urgent_flag=VALUES(urgent_flag),
  290. qty_ordered=VALUES(qty_ordered), qty_completed=VALUES(qty_completed), order_date=VALUES(order_date),
  291. due_date=VALUES(due_date), release_date=VALUES(release_date), first_plan_date=VALUES(first_plan_date),
  292. last_plan_date=VALUES(last_plan_date), first_start_time=VALUES(first_start_time), last_end_time=VALUES(last_end_time),
  293. operation_count=VALUES(operation_count), scheduled_qty=VALUES(scheduled_qty), completed_op_qty=VALUES(completed_op_qty),
  294. schedule_cycle_days=VALUES(schedule_cycle_days), schedule_satisfaction_flag=VALUES(schedule_satisfaction_flag),
  295. wip_qty=VALUES(wip_qty), resource_person_count=VALUES(resource_person_count), calc_time=VALUES(calc_time),
  296. update_time=CURRENT_TIMESTAMP
  297. """, batchId, now);
  298. }
  299. private async Task<List<S2KpiCalcRow>> CalculateS2KpiValuesAsync(string batchId, DateTime statDate)
  300. {
  301. return await _db.Ado.SqlQueryAsync<S2KpiCalcRow>(
  302. """
  303. SELECT tenant_id AS TenantId, factory_id AS FactoryId, 'S2_L1_001' AS MetricCode,
  304. ROUND(AVG(schedule_cycle_days), 4) AS MetricValue
  305. FROM dwd_order_schedule_trans
  306. WHERE calc_batch_id=@BatchId AND schedule_cycle_days IS NOT NULL AND schedule_cycle_days >= 0
  307. GROUP BY tenant_id, factory_id
  308. UNION ALL
  309. SELECT tenant_id AS TenantId, factory_id AS FactoryId, 'S2_L1_002' AS MetricCode,
  310. ROUND(100 * SUM(schedule_satisfaction_flag) / NULLIF(COUNT(1), 0), 4) AS MetricValue
  311. FROM dwd_order_schedule_trans
  312. WHERE calc_batch_id=@BatchId
  313. GROUP BY tenant_id, factory_id
  314. UNION ALL
  315. SELECT tenant_id AS TenantId, factory_id AS FactoryId, 'S2_L1_003' AS MetricCode,
  316. ROUND(COUNT(1) / NULLIF(SUM(CASE WHEN IFNULL(resource_person_count, 0) > 0 THEN resource_person_count ELSE 1 END), 0), 4) AS MetricValue
  317. FROM dwd_order_schedule_trans
  318. WHERE calc_batch_id=@BatchId
  319. GROUP BY tenant_id, factory_id
  320. UNION ALL
  321. SELECT tenant_id AS TenantId, factory_id AS FactoryId, 'S2_L1_004' AS MetricCode,
  322. ROUND(SUM(wip_qty) / NULLIF(SUM(CASE WHEN qty_completed > 0 THEN qty_completed ELSE completed_op_qty END), 0) * 30, 4) AS MetricValue
  323. FROM dwd_order_schedule_trans
  324. WHERE calc_batch_id=@BatchId
  325. GROUP BY tenant_id, factory_id
  326. UNION ALL
  327. SELECT tenant_id AS TenantId, factory_id AS FactoryId, 'S2_L2_001' AS MetricCode,
  328. ROUND(AVG(schedule_cycle_days), 4) AS MetricValue
  329. FROM dwd_order_schedule_trans
  330. WHERE calc_batch_id=@BatchId AND schedule_cycle_days IS NOT NULL AND schedule_cycle_days >= 0
  331. GROUP BY tenant_id, factory_id
  332. UNION ALL
  333. SELECT tenant_id AS TenantId, factory_id AS FactoryId, 'S2_L2_002' AS MetricCode,
  334. ROUND(100 * SUM(schedule_satisfaction_flag) / NULLIF(COUNT(1), 0), 4) AS MetricValue
  335. FROM dwd_order_schedule_trans
  336. WHERE calc_batch_id=@BatchId
  337. GROUP BY tenant_id, factory_id
  338. UNION ALL
  339. SELECT tenant_id AS TenantId, factory_id AS FactoryId, 'S2_L2_003' AS MetricCode,
  340. ROUND(COUNT(1) / NULLIF(SUM(CASE WHEN IFNULL(resource_person_count, 0) > 0 THEN resource_person_count ELSE 1 END), 0), 4) AS MetricValue
  341. FROM dwd_order_schedule_trans
  342. WHERE calc_batch_id=@BatchId
  343. GROUP BY tenant_id, factory_id
  344. """,
  345. new SugarParameter("@BatchId", batchId),
  346. new SugarParameter("@StatDate", statDate));
  347. }
  348. private async Task<int> UpsertS2KpiValueAsync(S2KpiCalcRow row, DateTime statDate, DateTime now)
  349. {
  350. if (row.MetricValue == null) return 0;
  351. var meta = await _db.Ado.SqlQuerySingleAsync<S2KpiMetaRow>(
  352. """
  353. SELECT MetricLevel, Direction, YellowThreshold, RedThreshold
  354. FROM ado_smart_ops_kpi_master
  355. WHERE TenantId=@TenantId AND ModuleCode='S2' AND MetricCode=@MetricCode AND IsEnabled=1
  356. LIMIT 1
  357. """,
  358. new SugarParameter("@TenantId", row.TenantId),
  359. new SugarParameter("@MetricCode", row.MetricCode));
  360. if (meta == null) return 0;
  361. var table = ResolveKpiValueTable(meta.MetricLevel);
  362. var current = await _db.Ado.SqlQuerySingleAsync<S2KpiValueRow>(
  363. $"""
  364. SELECT id AS Id, metric_value AS MetricValue, target_value AS TargetValue
  365. FROM {table}
  366. WHERE tenant_id=@TenantId AND factory_id=@FactoryId AND module_code='S2'
  367. AND metric_code=@MetricCode AND biz_date=@BizDate AND is_deleted=0
  368. ORDER BY id
  369. LIMIT 1
  370. """,
  371. new SugarParameter("@TenantId", row.TenantId),
  372. new SugarParameter("@FactoryId", row.FactoryId),
  373. new SugarParameter("@MetricCode", row.MetricCode),
  374. new SugarParameter("@BizDate", statDate));
  375. var prior = await _db.Ado.SqlQuerySingleAsync<S2KpiValueRow>(
  376. $"""
  377. SELECT id AS Id, metric_value AS MetricValue, target_value AS TargetValue
  378. FROM {table}
  379. WHERE tenant_id=@TenantId AND factory_id=@FactoryId AND module_code='S2'
  380. AND metric_code=@MetricCode AND biz_date<@BizDate AND is_deleted=0
  381. ORDER BY biz_date DESC, id DESC
  382. LIMIT 1
  383. """,
  384. new SugarParameter("@TenantId", row.TenantId),
  385. new SugarParameter("@FactoryId", row.FactoryId),
  386. new SugarParameter("@MetricCode", row.MetricCode),
  387. new SugarParameter("@BizDate", statDate));
  388. var actual = Math.Round(row.MetricValue.Value, 4);
  389. var target = current?.TargetValue ?? prior?.TargetValue ?? DefaultS2Target(row.MetricCode);
  390. var status = ResolveKpiStatus(actual, target, meta.Direction, meta.YellowThreshold, meta.RedThreshold);
  391. var trend = ResolveTrendFlag(actual, prior?.MetricValue);
  392. if (current != null)
  393. {
  394. return await _db.Ado.ExecuteCommandAsync(
  395. $"""
  396. UPDATE {table}
  397. SET metric_value=@MetricValue, target_value=@TargetValue, status_color=@StatusColor, trend_flag=@TrendFlag,
  398. is_active=1, status='ACTIVE', calc_time=@CalcTime, update_time=@CalcTime
  399. WHERE tenant_id=@TenantId AND factory_id=@FactoryId AND module_code='S2'
  400. AND metric_code=@MetricCode AND biz_date=@BizDate AND is_deleted=0
  401. """,
  402. new SugarParameter("@MetricValue", actual),
  403. new SugarParameter("@TargetValue", target),
  404. new SugarParameter("@StatusColor", status),
  405. new SugarParameter("@TrendFlag", trend),
  406. new SugarParameter("@CalcTime", now),
  407. new SugarParameter("@TenantId", row.TenantId),
  408. new SugarParameter("@FactoryId", row.FactoryId),
  409. new SugarParameter("@MetricCode", row.MetricCode),
  410. new SugarParameter("@BizDate", statDate));
  411. }
  412. var nextId = await _db.Ado.GetLongAsync($"SELECT COALESCE(MAX(id), 0) + 1 FROM {table}");
  413. return await _db.Ado.ExecuteCommandAsync(
  414. $"""
  415. INSERT INTO {table}
  416. (id, tenant_id, factory_id, status, biz_date, create_time, update_time, is_deleted, is_active,
  417. module_code, metric_code, metric_value, target_value, status_color, trend_flag, calc_time)
  418. VALUES
  419. (@Id, @TenantId, @FactoryId, 'ACTIVE', @BizDate, @CalcTime, @CalcTime, 0, 1,
  420. 'S2', @MetricCode, @MetricValue, @TargetValue, @StatusColor, @TrendFlag, @CalcTime)
  421. """,
  422. new SugarParameter("@Id", nextId),
  423. new SugarParameter("@TenantId", row.TenantId),
  424. new SugarParameter("@FactoryId", row.FactoryId),
  425. new SugarParameter("@BizDate", statDate),
  426. new SugarParameter("@CalcTime", now),
  427. new SugarParameter("@MetricCode", row.MetricCode),
  428. new SugarParameter("@MetricValue", actual),
  429. new SugarParameter("@TargetValue", target),
  430. new SugarParameter("@StatusColor", status),
  431. new SugarParameter("@TrendFlag", trend));
  432. }
  433. private async Task<long> InsertSyncLogAsync(long entityId, string entityName, string batchId, int rowsRead)
  434. {
  435. await _db.Ado.ExecuteCommandAsync(
  436. """
  437. INSERT INTO mdp_sync_log
  438. (tenant_id, entity_id, source_code, entity_name, sync_batch_id, sync_type, trigger_type, sync_start, rows_read, status)
  439. VALUES (0, @EntityId, 'AIDOPDEV_MYSQL', @EntityName, @BatchId, 'FULL', 'AUTO', NOW(), @RowsRead, 'RUNNING')
  440. """,
  441. new SugarParameter("@EntityId", entityId),
  442. new SugarParameter("@EntityName", entityName),
  443. new SugarParameter("@BatchId", batchId),
  444. new SugarParameter("@RowsRead", rowsRead));
  445. return await _db.Ado.GetLongAsync(
  446. "SELECT id FROM mdp_sync_log WHERE sync_batch_id=@BatchId AND entity_id=@EntityId ORDER BY id DESC LIMIT 1",
  447. new List<SugarParameter> { new("@BatchId", batchId), new("@EntityId", entityId) });
  448. }
  449. private async Task MarkSyncLogSuccessAsync(long logId, DateTime started, int affected)
  450. {
  451. await _db.Ado.ExecuteCommandAsync(
  452. """
  453. UPDATE mdp_sync_log
  454. SET sync_end=NOW(), duration_ms=@DurationMs, rows_insert=@RowsInsert, rows_update=0, rows_skip=0, rows_error=0, status='SUCCESS'
  455. WHERE id=@Id
  456. """,
  457. new SugarParameter("@DurationMs", (int)(DateTime.Now - started).TotalMilliseconds),
  458. new SugarParameter("@RowsInsert", affected),
  459. new SugarParameter("@Id", logId));
  460. }
  461. private async Task MarkSyncLogFailedAsync(long logId, DateTime started, string message)
  462. {
  463. try
  464. {
  465. await _db.Ado.ExecuteCommandAsync(
  466. """
  467. UPDATE mdp_sync_log
  468. SET sync_end=NOW(), duration_ms=@DurationMs, rows_error=1, status='FAILED', error_msg=@ErrorMsg
  469. WHERE id=@Id
  470. """,
  471. new SugarParameter("@DurationMs", (int)(DateTime.Now - started).TotalMilliseconds),
  472. new SugarParameter("@ErrorMsg", Truncate(message, 1000)),
  473. new SugarParameter("@Id", logId));
  474. }
  475. catch (Exception ex)
  476. {
  477. Console.Error.WriteLine($"[S2MdpSyncTransform] MarkSyncLogFailed write failed (syncLogId={logId}): {ex.Message}");
  478. }
  479. }
  480. private async Task<long> InsertTransformRunLogAsync(string batchId, DateTime startedAt, string triggerType)
  481. {
  482. await _db.Ado.ExecuteCommandAsync(
  483. """
  484. INSERT INTO mdp_transform_run_log
  485. (tenant_id, job_code, job_name, trigger_type, batch_id, status, start_time)
  486. VALUES (0, 'S2_MDP_SYNC_TRANSFORM', 'S2 MDP同步与KPI计算', @TriggerType, @BatchId, 'RUNNING', @StartTime)
  487. """,
  488. new SugarParameter("@TriggerType", NormalizeTriggerType(triggerType)),
  489. new SugarParameter("@BatchId", batchId),
  490. new SugarParameter("@StartTime", startedAt));
  491. return await _db.Ado.GetLongAsync(
  492. "SELECT id FROM mdp_transform_run_log WHERE batch_id=@BatchId ORDER BY id DESC LIMIT 1",
  493. new List<SugarParameter> { new("@BatchId", batchId) });
  494. }
  495. private async Task MarkTransformRunSuccessAsync(long runLogId, DateTime startedAt, S2MdpSyncTransformResult result)
  496. {
  497. var finishedAt = DateTime.Now;
  498. await _db.Ado.ExecuteCommandAsync(
  499. """
  500. UPDATE mdp_transform_run_log
  501. SET status='SUCCESS', end_time=@EndTime, duration_ms=@DurationMs,
  502. stage_rows=@StageRows, standard_rows=@StandardRows, dwd_rows=@DwdRows,
  503. summary_json=@SummaryJson, update_time=CURRENT_TIMESTAMP
  504. WHERE id=@Id
  505. """,
  506. new SugarParameter("@EndTime", finishedAt),
  507. new SugarParameter("@DurationMs", (int)(finishedAt - startedAt).TotalMilliseconds),
  508. new SugarParameter("@StageRows", result.StageRows),
  509. new SugarParameter("@StandardRows", result.StandardRows),
  510. new SugarParameter("@DwdRows", result.DwdRows),
  511. new SugarParameter("@SummaryJson", BuildRunSummaryJson(result)),
  512. new SugarParameter("@Id", runLogId));
  513. }
  514. private async Task MarkTransformRunFailedAsync(long runLogId, DateTime startedAt, string message)
  515. {
  516. try
  517. {
  518. var finishedAt = DateTime.Now;
  519. await _db.Ado.ExecuteCommandAsync(
  520. """
  521. UPDATE mdp_transform_run_log
  522. SET status='FAILED', end_time=@EndTime, duration_ms=@DurationMs,
  523. error_message=@ErrorMessage, update_time=CURRENT_TIMESTAMP
  524. WHERE id=@Id
  525. """,
  526. new SugarParameter("@EndTime", finishedAt),
  527. new SugarParameter("@DurationMs", (int)(finishedAt - startedAt).TotalMilliseconds),
  528. new SugarParameter("@ErrorMessage", Truncate(message, 2000)),
  529. new SugarParameter("@Id", runLogId));
  530. }
  531. catch (Exception ex)
  532. {
  533. Console.Error.WriteLine($"[S2MdpSyncTransform] MarkTransformRunFailed write failed (runLogId={runLogId}): {ex.Message}");
  534. }
  535. }
  536. private static S2MdpSqlCommand Cmd(string sql, string batchId, DateTime now)
  537. {
  538. return new S2MdpSqlCommand(sql, new[]
  539. {
  540. new SugarParameter("@BatchId", batchId),
  541. new SugarParameter("@Now", now),
  542. new SugarParameter("@StatDate", now.Date)
  543. });
  544. }
  545. private static string BuildJsonObjectExpression(IEnumerable<string> columns)
  546. {
  547. var parts = columns.SelectMany(c => new[] { $"'{c.Replace("'", "''")}'", $"s.`{c}`" });
  548. return $"JSON_OBJECT({string.Join(",", parts)})";
  549. }
  550. private static string FindColumn(IEnumerable<string> columns, string expected)
  551. {
  552. return columns.First(u => string.Equals(u, expected, StringComparison.OrdinalIgnoreCase));
  553. }
  554. private static string NormalizeTriggerType(string? triggerType)
  555. {
  556. return string.IsNullOrWhiteSpace(triggerType) ? "AUTO" : triggerType.Trim().ToUpperInvariant();
  557. }
  558. private static string BuildRunSummaryJson(S2MdpSyncTransformResult result)
  559. {
  560. return $$"""{"batchId":"{{result.BatchId}}","stageRows":{{result.StageRows}},"standardRows":{{result.StandardRows}},"dwdRows":{{result.DwdRows}},"kpiRows":{{result.KpiRows}}}""";
  561. }
  562. private static string ResolveKpiValueTable(int metricLevel)
  563. {
  564. return metricLevel switch
  565. {
  566. 1 => "ado_s9_kpi_value_l1_day",
  567. 2 => "ado_s9_kpi_value_l2_day",
  568. 3 => "ado_s9_kpi_value_l3_day",
  569. 4 => "ado_s9_kpi_value_l4_day",
  570. _ => "ado_s9_kpi_value_l2_day"
  571. };
  572. }
  573. private static decimal DefaultS2Target(string metricCode)
  574. {
  575. return metricCode switch
  576. {
  577. "S2_L1_001" => 20m,
  578. "S2_L1_002" => 99m,
  579. "S2_L1_003" => 20m,
  580. "S2_L1_004" => 18m,
  581. "S2_L2_001" => 1.2m,
  582. "S2_L2_002" => 95m,
  583. "S2_L2_003" => 20m,
  584. _ => 0m
  585. };
  586. }
  587. private static string ResolveKpiStatus(decimal actual, decimal target, string? direction, decimal? yellowThreshold, decimal? redThreshold)
  588. {
  589. if (target <= 0) return "gray";
  590. var ratio = actual / target * 100m;
  591. if (string.Equals(direction, "lower_is_better", StringComparison.OrdinalIgnoreCase))
  592. {
  593. if (actual <= target) return "green";
  594. if (ratio <= (yellowThreshold ?? 110m)) return "yellow";
  595. return ratio >= (redThreshold ?? 120m) ? "red" : "yellow";
  596. }
  597. if (actual >= target) return "green";
  598. if (ratio >= (yellowThreshold ?? 95m)) return "yellow";
  599. return ratio <= (redThreshold ?? 80m) ? "red" : "yellow";
  600. }
  601. private static string ResolveTrendFlag(decimal actual, decimal? previous)
  602. {
  603. if (previous == null) return "flat";
  604. if (actual > previous.Value) return "up";
  605. if (actual < previous.Value) return "down";
  606. return "flat";
  607. }
  608. private static string Truncate(string? raw, int maxLength)
  609. {
  610. if (string.IsNullOrEmpty(raw)) return string.Empty;
  611. return raw.Length <= maxLength ? raw : raw[..maxLength];
  612. }
  613. private sealed class S2ColumnRow
  614. {
  615. public string ColumnName { get; set; } = string.Empty;
  616. }
  617. private sealed class S2MdpEntityRow
  618. {
  619. public long Id { get; set; }
  620. public string EntityName { get; set; } = string.Empty;
  621. }
  622. private sealed class S2KpiCalcRow
  623. {
  624. public long TenantId { get; set; }
  625. public long FactoryId { get; set; }
  626. public string MetricCode { get; set; } = string.Empty;
  627. public decimal? MetricValue { get; set; }
  628. }
  629. private sealed class S2KpiMetaRow
  630. {
  631. public int MetricLevel { get; set; }
  632. public string Direction { get; set; } = "higher_is_better";
  633. public decimal? YellowThreshold { get; set; }
  634. public decimal? RedThreshold { get; set; }
  635. }
  636. private sealed class S2KpiValueRow
  637. {
  638. public long Id { get; set; }
  639. public decimal? MetricValue { get; set; }
  640. public decimal? TargetValue { get; set; }
  641. }
  642. }
  643. public sealed class S2MdpSyncTransformResult
  644. {
  645. public long RunLogId { get; set; }
  646. public string BatchId { get; set; } = string.Empty;
  647. public int StageRows { get; set; }
  648. public int StandardRows { get; set; }
  649. public int DwdRows { get; set; }
  650. public int KpiRows { get; set; }
  651. }
  652. internal sealed record S2MdpSqlCommand(string Sql, SugarParameter[] Parameters);
  653. internal sealed record S2MdpEntityConfig(
  654. string EntityCode,
  655. string SourceTable,
  656. string TargetTable,
  657. string SourceRowIdExpression,
  658. string SourceBizKeyExpression)
  659. {
  660. public static readonly IReadOnlyList<S2MdpEntityConfig> All = new List<S2MdpEntityConfig>
  661. {
  662. new("S2_WORK_ORDER_MASTER", "WorkOrdMaster", "mdp_stg_schedule", "RecID", "CONCAT(IFNULL(s.`Domain`,''), ':', IFNULL(s.`WorkOrd`,''))"),
  663. new("S2_WORK_ORDER_ROUTING", "WorkOrdRouting", "mdp_stg_schedule", "RecID", "CONCAT(IFNULL(s.`Domain`,''), ':', IFNULL(s.`WorkOrd`,''), ':', IFNULL(s.`OP`,''))"),
  664. new("S2_WORK_ORDER_DETAIL", "WorkOrdDetail", "mdp_stg_schedule", "RecID", "CONCAT(IFNULL(s.`Domain`,''), ':', IFNULL(s.`WorkOrd`,''), ':', IFNULL(s.`Op`,''), ':', IFNULL(s.`ItemNum`,''))"),
  665. new("S2_PERIOD_SEQUENCE_DET", "PeriodSequenceDet", "mdp_stg_schedule", "RecID", "CONCAT(IFNULL(s.`Domain`,''), ':', IFNULL(s.`WorkOrds`,''), ':', IFNULL(s.`Op`,''), ':', IFNULL(s.`Sequence`,''))"),
  666. new("S2_SCHEDULE_RESULT_OP", "ScheduleResultOpMaster", "mdp_stg_schedule", "RecID", "CONCAT(IFNULL(s.`Domain`,''), ':', IFNULL(s.`WorkOrd`,''), ':', IFNULL(s.`Op`,''), ':', IFNULL(s.`WorkDate`,''))")
  667. };
  668. }
  669. internal static class S2MdpDdl
  670. {
  671. public static readonly IReadOnlyList<string> SqlBlocks = new[]
  672. {
  673. """
  674. CREATE TABLE IF NOT EXISTS mdp_stg_schedule (
  675. id BIGINT AUTO_INCREMENT PRIMARY KEY,
  676. tenant_id BIGINT NOT NULL DEFAULT 0,
  677. factory_id VARCHAR(64) NULL,
  678. source_system VARCHAR(50) NOT NULL DEFAULT 'AIDOP',
  679. source_table VARCHAR(100) NOT NULL,
  680. source_row_id VARCHAR(100) NOT NULL,
  681. source_biz_key VARCHAR(200) NULL,
  682. sync_batch_id VARCHAR(100) NOT NULL,
  683. sync_time DATETIME NOT NULL,
  684. process_status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
  685. raw_data JSON NOT NULL,
  686. update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  687. UNIQUE KEY uk_mdp_stg_schedule (tenant_id, source_table, source_row_id),
  688. KEY idx_mdp_stg_schedule_batch (sync_batch_id),
  689. KEY idx_mdp_stg_schedule_biz (source_biz_key)
  690. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S2生产排程贴源层';
  691. """,
  692. """
  693. CREATE TABLE IF NOT EXISTS mdp_std_work_order_schedule (
  694. id BIGINT AUTO_INCREMENT PRIMARY KEY,
  695. tenant_id BIGINT NOT NULL DEFAULT 0,
  696. factory_id BIGINT NULL,
  697. source_system VARCHAR(50) NOT NULL DEFAULT 'AIDOP',
  698. work_order VARCHAR(100) NOT NULL,
  699. sales_order_no VARCHAR(100) NULL,
  700. item_code VARCHAR(100) NULL,
  701. item_name VARCHAR(200) NULL,
  702. site_code VARCHAR(50) NULL,
  703. status VARCHAR(50) NULL,
  704. priority DECIMAL(18,6) NULL,
  705. urgent_flag TINYINT NOT NULL DEFAULT 0,
  706. qty_ordered DECIMAL(18,6) NULL,
  707. qty_completed DECIMAL(18,6) NULL,
  708. order_date DATETIME NULL,
  709. due_date DATETIME NULL,
  710. release_date DATETIME NULL,
  711. prod_line VARCHAR(100) NULL,
  712. source_biz_key VARCHAR(200) NULL,
  713. sync_batch_id VARCHAR(100) NOT NULL,
  714. sync_time DATETIME NOT NULL,
  715. update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  716. UNIQUE KEY uk_std_work_order_schedule (tenant_id, work_order),
  717. KEY idx_std_work_order_schedule_batch (sync_batch_id),
  718. KEY idx_std_work_order_schedule_due (tenant_id, due_date)
  719. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S2标准工单排程';
  720. """,
  721. """
  722. CREATE TABLE IF NOT EXISTS mdp_std_operation_schedule (
  723. id BIGINT AUTO_INCREMENT PRIMARY KEY,
  724. tenant_id BIGINT NOT NULL DEFAULT 0,
  725. factory_id BIGINT NULL,
  726. source_system VARCHAR(50) NOT NULL DEFAULT 'AIDOP',
  727. work_order VARCHAR(100) NOT NULL,
  728. op_no VARCHAR(50) NULL,
  729. work_center VARCHAR(100) NULL,
  730. line_code VARCHAR(100) NULL,
  731. item_code VARCHAR(100) NULL,
  732. plan_date DATETIME NULL,
  733. prod_date DATETIME NULL,
  734. start_time DATETIME NULL,
  735. end_time DATETIME NULL,
  736. ord_qty DECIMAL(18,6) NULL,
  737. comp_qty DECIMAL(18,6) NULL,
  738. run_crew DECIMAL(18,6) NULL,
  739. employee VARCHAR(200) NULL,
  740. source_biz_key VARCHAR(200) NULL,
  741. sync_batch_id VARCHAR(100) NOT NULL,
  742. sync_time DATETIME NOT NULL,
  743. update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  744. UNIQUE KEY uk_std_operation_schedule (tenant_id, source_biz_key),
  745. KEY idx_std_operation_schedule_work_order (tenant_id, work_order),
  746. KEY idx_std_operation_schedule_batch (sync_batch_id)
  747. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S2标准工序排程';
  748. """,
  749. """
  750. CREATE TABLE IF NOT EXISTS dwd_order_schedule_trans (
  751. id BIGINT AUTO_INCREMENT PRIMARY KEY,
  752. tenant_id BIGINT NOT NULL DEFAULT 0,
  753. factory_id BIGINT NOT NULL DEFAULT 1,
  754. stat_date DATE NOT NULL,
  755. work_order VARCHAR(100) NOT NULL,
  756. sales_order_no VARCHAR(100) NULL,
  757. item_code VARCHAR(100) NULL,
  758. item_name VARCHAR(200) NULL,
  759. site_code VARCHAR(50) NULL,
  760. prod_line VARCHAR(100) NULL,
  761. status VARCHAR(50) NULL,
  762. urgent_flag TINYINT NOT NULL DEFAULT 0,
  763. qty_ordered DECIMAL(18,6) NULL,
  764. qty_completed DECIMAL(18,6) NULL,
  765. order_date DATETIME NULL,
  766. due_date DATETIME NULL,
  767. release_date DATETIME NULL,
  768. first_plan_date DATETIME NULL,
  769. last_plan_date DATETIME NULL,
  770. first_start_time DATETIME NULL,
  771. last_end_time DATETIME NULL,
  772. operation_count INT NOT NULL DEFAULT 0,
  773. scheduled_qty DECIMAL(18,6) NULL,
  774. completed_op_qty DECIMAL(18,6) NULL,
  775. schedule_cycle_days DECIMAL(18,6) NULL,
  776. schedule_satisfaction_flag TINYINT NOT NULL DEFAULT 0,
  777. wip_qty DECIMAL(18,6) NULL,
  778. resource_person_count DECIMAL(18,6) NULL,
  779. calc_batch_id VARCHAR(100) NOT NULL,
  780. calc_time DATETIME NOT NULL,
  781. update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  782. UNIQUE KEY uk_dwd_order_schedule_trans (tenant_id, work_order, calc_batch_id),
  783. KEY idx_dwd_order_schedule_trans_batch (calc_batch_id),
  784. KEY idx_dwd_order_schedule_trans_stat (tenant_id, stat_date),
  785. KEY idx_dwd_order_schedule_trans_order (tenant_id, sales_order_no)
  786. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='S2订单工单排程DWD';
  787. """,
  788. """
  789. INSERT INTO mdp_entity
  790. (tenant_id, source_id, entity_code, entity_name, entity_type, source_table_name, target_table_name, sync_mode, batch_size, status, remark)
  791. SELECT 0, s.id, v.entity_code, v.entity_name, 'TABLE', v.source_table_name, 'mdp_stg_schedule', 'FULL', 5000, 1, v.remark
  792. FROM mdp_source s
  793. JOIN (
  794. SELECT 'S2_WORK_ORDER_MASTER' AS entity_code, 'S2工单主数据' AS entity_name, 'WorkOrdMaster' AS source_table_name, '工单主数据进入 S2 贴源层' AS remark
  795. UNION ALL SELECT 'S2_WORK_ORDER_ROUTING', 'S2工单工艺路线', 'WorkOrdRouting', '工单工艺路线进入 S2 贴源层'
  796. UNION ALL SELECT 'S2_WORK_ORDER_DETAIL', 'S2工单物料明细', 'WorkOrdDetail', '工单物料需求进入 S2 贴源层'
  797. UNION ALL SELECT 'S2_PERIOD_SEQUENCE_DET', 'S2工序排程计划', 'PeriodSequenceDet', '工序间衔接与排程计划进入 S2 贴源层'
  798. UNION ALL SELECT 'S2_SCHEDULE_RESULT_OP', 'S2工序排产结果', 'ScheduleResultOpMaster', '工序排产结果进入 S2 贴源层'
  799. ) v
  800. WHERE s.tenant_id=0 AND s.source_code='AIDOPDEV_MYSQL'
  801. ON DUPLICATE KEY UPDATE
  802. source_id=VALUES(source_id), entity_name=VALUES(entity_name), entity_type=VALUES(entity_type),
  803. source_table_name=VALUES(source_table_name), target_table_name=VALUES(target_table_name),
  804. sync_mode=VALUES(sync_mode), batch_size=VALUES(batch_size), status=VALUES(status),
  805. remark=VALUES(remark), update_time=CURRENT_TIMESTAMP;
  806. """
  807. };
  808. }