RequirementExamineDetailService.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. namespace Admin.NET.Plugin.AiDOP.Order;
  2. /// <summary>
  3. /// 需求明细核验(资源检查运算明细,只读查询)
  4. /// 路由前缀:/api/Order/examine-detail/...
  5. ///
  6. /// 【租户安全边界】需认证访问(已移除类级 <c>[AllowAnonymous]</c>);租户一律经
  7. /// <c>AidopTenantScope.ResolveOrThrow</c> 从认证后 JWT 解析,无有效租户即拒绝,
  8. /// 不读前端 tenantId、无默认回退(原实现:类级匿名 + 宽松解析,
  9. /// 未认证方可携带 <c>?tenantId=</c> 读任意租户需求明细核验数据)。
  10. /// </summary>
  11. [ApiDescriptionSettings(Order = 255, Description = "需求明细核验")]
  12. [Route("api/Order")]
  13. [NonUnify]
  14. public class RequirementExamineDetailService : IDynamicApiController, ITransient
  15. {
  16. private readonly ISqlSugarClient _db;
  17. private readonly UserManager _userManager;
  18. public RequirementExamineDetailService(ISqlSugarClient db, UserManager userManager)
  19. {
  20. _db = db;
  21. _userManager = userManager;
  22. }
  23. /// <summary>
  24. /// 分页查询需求明细核验(扁平行;前端组树)
  25. /// </summary>
  26. [DisplayName("需求明细分页列表")]
  27. [HttpGet("examine-detail/list")]
  28. public async Task<object> PageRequirementExamineDetail([FromQuery] RequirementExamineDetailListInput input)
  29. {
  30. var tenantId = AidopTenantScope.ResolveOrThrow(_userManager);
  31. var pars = new List<SugarParameter> { new("@TenantId", tenantId) };
  32. var where = new List<string>
  33. {
  34. "er.tenant_id = @TenantId",
  35. "IFNULL(er.IsDeleted, 0) = 0",
  36. "IFNULL(bce.is_use, 0) = 1"
  37. };
  38. if (!string.IsNullOrWhiteSpace(input.BillNo))
  39. {
  40. where.Add("er.bill_no LIKE @BillNo");
  41. pars.Add(new SugarParameter("@BillNo", $"%{input.BillNo.Trim()}%"));
  42. }
  43. if (!string.IsNullOrWhiteSpace(input.MorderNo))
  44. {
  45. where.Add("er.morder_no LIKE @MorderNo");
  46. pars.Add(new SugarParameter("@MorderNo", $"%{input.MorderNo.Trim()}%"));
  47. }
  48. if (!string.IsNullOrWhiteSpace(input.ItemNumber))
  49. {
  50. where.Add("bce.item_number = @ItemNumber");
  51. pars.Add(new SugarParameter("@ItemNumber", input.ItemNumber.Trim()));
  52. }
  53. var whereSql = string.Join(" AND ", where);
  54. var orderExpr = ResolveOrderClause(input.OrderBy, input.Sort);
  55. var baseSql = $"""
  56. SELECT
  57. bce.Id AS RowId,
  58. bce.parent_id AS ParentRowId,
  59. bce.num AS Num,
  60. bce.item_number AS ItemNumber,
  61. bce.item_name AS ItemName,
  62. bce.bom_number AS BomNumber,
  63. bce.model AS Model,
  64. DATE_FORMAT(bce.kitting_time, '%Y-%m-%d') AS KittingTime,
  65. bce.type AS Type,
  66. bce.erp_cls_name AS ErpClsName,
  67. bce.qty AS Qty,
  68. IFNULL(bce.wastage, 0) AS Wastage,
  69. bce.needCount AS NeedCount,
  70. bce.sqty AS Sqty,
  71. bce.use_qty AS UseQty,
  72. bce.self_lack_qty AS SelfLackQty,
  73. bce.lack_qty AS LackQty,
  74. bce.mo_qty AS MoQty,
  75. bce.make_qty AS MakeQty,
  76. bce.purchase_qty AS PurchaseQty,
  77. bce.purchase_occupy_qty AS PurchaseOccupyQty,
  78. DATE_FORMAT(bce.satisfy_time, '%Y-%m-%d') AS SatisfyTime,
  79. bce.haveicsubs AS HaveIcSubs,
  80. bce.substitute_code AS SubstituteCode,
  81. er.bill_no AS BillNo,
  82. er.morder_no AS MorderNo,
  83. bce.create_time AS CreateTime
  84. FROM b_examine_result er
  85. INNER JOIN (
  86. SELECT morder_no, MAX(Id) AS MaxId
  87. FROM b_examine_result
  88. WHERE tenant_id = @TenantId AND IFNULL(IsDeleted, 0) = 0
  89. GROUP BY morder_no
  90. ) latest ON er.Id = latest.MaxId
  91. INNER JOIN b_bom_child_examine bce ON er.Id = bce.examine_id AND bce.tenant_id = er.tenant_id
  92. WHERE {whereSql}
  93. """;
  94. var countSql = $"SELECT COUNT(*) FROM ({baseSql}) AS t";
  95. var total = await _db.Ado.GetIntAsync(countSql, pars);
  96. var offset = (input.Page - 1) * input.PageSize;
  97. var dataSql = $"""
  98. SELECT * FROM ({baseSql}) AS t
  99. ORDER BY {orderExpr}
  100. LIMIT {input.PageSize} OFFSET {offset}
  101. """;
  102. var list = await _db.Ado.SqlQueryAsync<RequirementExamineDetailRow>(dataSql, pars);
  103. // 分页内重算序号(与当前页一致)
  104. var snoStart = offset + 1;
  105. for (var i = 0; i < list.Count; i++)
  106. list[i].Sno = snoStart + i;
  107. return new
  108. {
  109. total,
  110. page = input.Page,
  111. pageSize = input.PageSize,
  112. list
  113. };
  114. }
  115. private static string ResolveOrderClause(string? orderBy, string? sort)
  116. {
  117. var desc = string.Equals(sort?.Trim(), "desc", StringComparison.OrdinalIgnoreCase);
  118. var dir = desc ? "DESC" : "ASC";
  119. var key = orderBy?.Trim();
  120. var col = key?.ToLowerInvariant() switch
  121. {
  122. "sno" => "t.RowId",
  123. "rowid" => "t.RowId",
  124. "billno" => "t.BillNo",
  125. "morderno" => "t.MorderNo",
  126. "itemnumber" => "t.ItemNumber",
  127. "kittingtime" => "t.KittingTime",
  128. _ => "t.RowId"
  129. };
  130. return $"{col} {dir}, t.RowId ASC";
  131. }
  132. }