WorkOrderDispatchService.cs 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. namespace Admin.NET.Plugin.AiDOP.WorkOrder;
  2. /// <summary>
  3. /// 工单下达服务 🏭
  4. /// 路由前缀:/api/WorkOrder/dispatch/...
  5. /// </summary>
  6. [ApiDescriptionSettings(Order = 260, Description = "工单下达")]
  7. [Route("api/WorkOrder")]
  8. [AllowAnonymous]
  9. [NonUnify]
  10. public class WorkOrderDispatchService : IDynamicApiController, ITransient
  11. {
  12. private readonly ISqlSugarClient _db;
  13. private readonly UserManager _userManager;
  14. public WorkOrderDispatchService(ISqlSugarClient db, UserManager userManager)
  15. {
  16. _db = db;
  17. _userManager = userManager;
  18. }
  19. // ══════════════════════════════════════════════════════════════
  20. // 列表 GET /api/WorkOrder/dispatch/list
  21. // ══════════════════════════════════════════════════════════════
  22. /// <summary>获取工单下达分页列表 🏭</summary>
  23. [DisplayName("获取工单下达列表")]
  24. [HttpGet("dispatch/list")]
  25. public async Task<object> GetDispatchList([FromQuery] WorkOrderDispatchListInput input)
  26. {
  27. var pars = new List<SugarParameter>();
  28. // 与库内其它原生 SQL 一致:统一 COLLATE,避免 utf8mb4_general_ci / utf8mb4_0900_ai_ci 混用报错
  29. const string C = "utf8mb4_general_ci";
  30. var conditions = new List<string> { $"a.Status COLLATE {C} = 'p'" };
  31. if (!string.IsNullOrWhiteSpace(input.WorkOrd))
  32. {
  33. conditions.Add($"(a.WorkOrd COLLATE {C}) LIKE (@WorkOrd COLLATE {C})");
  34. pars.Add(new SugarParameter("@WorkOrd", $"%{input.WorkOrd.Trim()}%"));
  35. }
  36. if (!string.IsNullOrWhiteSpace(input.ItemNum))
  37. {
  38. conditions.Add($"(a.ItemNum COLLATE {C}) LIKE (@ItemNum COLLATE {C})");
  39. pars.Add(new SugarParameter("@ItemNum", $"%{input.ItemNum.Trim()}%"));
  40. }
  41. if (!string.IsNullOrWhiteSpace(input.Descr))
  42. {
  43. conditions.Add($"(b.Descr COLLATE {C}) LIKE (@Descr COLLATE {C})");
  44. pars.Add(new SugarParameter("@Descr", $"%{input.Descr.Trim()}%"));
  45. }
  46. if (!string.IsNullOrWhiteSpace(input.OrdDateFrom))
  47. {
  48. conditions.Add("a.OrdDate >= @OrdDateFrom");
  49. pars.Add(new SugarParameter("@OrdDateFrom", input.OrdDateFrom.Trim()));
  50. }
  51. var whereClause = "WHERE " + string.Join(" AND ", conditions);
  52. var baseSql = $"""
  53. SELECT
  54. a.RecID AS Id,
  55. a.Domain,
  56. a.WorkOrd,
  57. a.Priority,
  58. a.ItemNum,
  59. b.Descr,
  60. b.Descr1,
  61. a.QtyOrded,
  62. m.MaterialSituation,
  63. a.OrdDate,
  64. a.DueDate,
  65. LOWER(a.Status) AS Status,
  66. a.LotSerial,
  67. IFNULL(nm.Nbr,'') AS PrevNbr,
  68. IFNULL(nm1.Nbr,'') AS Nbr,
  69. CONCAT(IFNULL(b.ItemNum,''), IFNULL(b.Descr,''), IFNULL(b.Descr1,'')) AS Wl
  70. FROM WorkOrdMaster a
  71. LEFT JOIN ItemMaster b ON a.ItemNum COLLATE utf8mb4_general_ci = b.ItemNum COLLATE utf8mb4_general_ci
  72. LEFT JOIN mes_morder m ON a.WorkOrd COLLATE utf8mb4_general_ci = m.morder_no COLLATE utf8mb4_general_ci
  73. AND CAST(a.Domain AS CHAR(64)) COLLATE utf8mb4_general_ci = CAST(m.factory_id AS CHAR(64)) COLLATE utf8mb4_general_ci
  74. LEFT JOIN NbrMaster nm
  75. ON a.Domain COLLATE utf8mb4_general_ci = nm.Domain COLLATE utf8mb4_general_ci
  76. AND a.WorkOrd COLLATE utf8mb4_general_ci = nm.WorkOrd COLLATE utf8mb4_general_ci
  77. AND nm.Type = 'SM' AND nm.TransType = 'PrevProcess'
  78. LEFT JOIN NbrMaster nm1
  79. ON a.Domain COLLATE utf8mb4_general_ci = nm1.Domain COLLATE utf8mb4_general_ci
  80. AND a.WorkOrd COLLATE utf8mb4_general_ci = nm1.WorkOrd COLLATE utf8mb4_general_ci
  81. AND nm1.Type = 'SM' AND nm1.TransType = ''
  82. {whereClause}
  83. """;
  84. var offset = (input.Page - 1) * input.PageSize;
  85. var total = await _db.Ado.GetIntAsync(
  86. $"SELECT COUNT(*) FROM ({baseSql}) AS t", pars);
  87. var list = await _db.Ado.SqlQueryAsync<WorkOrderDispatchRow>(
  88. $"SELECT * FROM ({baseSql}) AS t ORDER BY t.Id DESC LIMIT {input.PageSize} OFFSET {offset}",
  89. pars);
  90. return new { total, page = input.Page, pageSize = input.PageSize, list };
  91. }
  92. // ══════════════════════════════════════════════════════════════
  93. // 工单下达 POST /api/WorkOrder/dispatch/release
  94. // ══════════════════════════════════════════════════════════════
  95. /// <summary>工单下达(更新开工日期、生产批号,状态改为 r)🏭</summary>
  96. [DisplayName("工单下达")]
  97. [ApiDescriptionSettings(Name = "ReleaseWorkOrder"), HttpPost("dispatch/release")]
  98. public async Task<object> ReleaseWorkOrder([FromBody] WorkOrderReleaseInput input)
  99. {
  100. // 参照 SysJobService.UpdateJobDetail:先查再改
  101. var pars = new List<SugarParameter>
  102. {
  103. new SugarParameter("@WorkOrd", input.WorkOrd),
  104. new SugarParameter("@Domain", input.Domain)
  105. };
  106. var exists = await _db.Ado.GetIntAsync(
  107. "SELECT COUNT(*) FROM WorkOrdMaster WHERE WorkOrd = @WorkOrd AND Domain = @Domain AND Status = 'p'",
  108. pars);
  109. if (exists == 0)
  110. throw Oops.Oh("工单不存在或状态不为初始(p),无法下达");
  111. var ordDate = string.IsNullOrWhiteSpace(input.OrdDate)
  112. ? (DateTime?)null
  113. : DateTime.Parse(input.OrdDate);
  114. var releaseDate = DateTime.Now;
  115. var account = _userManager.Account ?? "system";
  116. var updatePars = new List<SugarParameter>
  117. {
  118. new SugarParameter("@Status", "r"),
  119. new SugarParameter("@OrdDate", ordDate),
  120. new SugarParameter("@LotSerial", input.LotSerial ?? (object)DBNull.Value),
  121. new SugarParameter("@ReleaseDate", releaseDate),
  122. new SugarParameter("@UpdateUser", account),
  123. new SugarParameter("@UpdateTime", releaseDate),
  124. new SugarParameter("@WorkOrd", input.WorkOrd),
  125. new SugarParameter("@Domain", input.Domain)
  126. };
  127. await _db.Ado.ExecuteCommandAsync(
  128. """
  129. UPDATE WorkOrdMaster
  130. SET Status = @Status,
  131. OrdDate = @OrdDate,
  132. LotSerial = @LotSerial,
  133. ReleaseDate = @ReleaseDate,
  134. UpdateUser = @UpdateUser,
  135. UpdateTime = @UpdateTime
  136. WHERE WorkOrd = @WorkOrd AND Domain = @Domain
  137. """,
  138. updatePars);
  139. return new { message = "工单下达成功" };
  140. }
  141. // ──────────────── 内部查询结果映射类 ────────────────
  142. private sealed class WorkOrderDispatchRow
  143. {
  144. public int Id { get; set; }
  145. public string? Domain { get; set; }
  146. public string? WorkOrd { get; set; }
  147. public string? Priority { get; set; }
  148. public string? ItemNum { get; set; }
  149. public string? Descr { get; set; }
  150. public string? Descr1 { get; set; }
  151. public decimal? QtyOrded { get; set; }
  152. public string? MaterialSituation { get; set; }
  153. public DateTime? OrdDate { get; set; }
  154. public DateTime? DueDate { get; set; }
  155. public string? Status { get; set; }
  156. public string? LotSerial { get; set; }
  157. public string? PrevNbr { get; set; }
  158. public string? Nbr { get; set; }
  159. public string? Wl { get; set; }
  160. }
  161. }