PurchaseReceiptService.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. using Admin.NET.Plugin.AiDOP.MaterialWarehouse.Dto;
  2. namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse;
  3. /// <summary>
  4. /// S5 采购收货单 只读 list 服务。
  5. ///
  6. /// 数据源:DOP 数据中台标准层 mdp_std_purchase_receipt(明细粒度 domain#receiver#line)。
  7. /// 由 PurchaseReceiptMdpSyncService 从 aidopdev.PurOrdRctDetail/PurOrdRctMaster RctType='rc' 同步标准化而来。
  8. ///
  9. /// 本服务仅 SELECT:无新增/编辑/删除/收货确认/入库写入/库存事务/状态流转;无 detail(扁平列表)。
  10. /// </summary>
  11. [ApiDescriptionSettings(Order = 305, Description = "采购收货单")]
  12. [Route("api/PurchaseReceipt")]
  13. [NonUnify]
  14. public class PurchaseReceiptService : IDynamicApiController, ITransient
  15. {
  16. private readonly ISqlSugarClient _db;
  17. private readonly UserManager _userManager;
  18. public PurchaseReceiptService(ISqlSugarClient db, UserManager userManager)
  19. {
  20. _db = db;
  21. _userManager = userManager;
  22. }
  23. /// <summary>严格可信租户解析,见 <see cref="Infrastructure.AidopTenantScope.ResolveOrThrow"/>。</summary>
  24. private long ResolveTenantOrThrow() => Infrastructure.AidopTenantScope.ResolveOrThrow(_userManager);
  25. /// <summary>
  26. /// 采购收货单列表(只读分页查询)。
  27. /// </summary>
  28. [DisplayName("采购收货单列表")]
  29. [HttpGet("list")]
  30. public async Task<object> GetList([FromQuery] PurchaseReceiptListInput input)
  31. {
  32. var page = input.Page <= 0 ? 1 : input.Page;
  33. var pageSize = input.PageSize <= 0 ? 10 : input.PageSize;
  34. var offset = (page - 1) * pageSize;
  35. var where = new List<string> { "1=1" };
  36. var pars = new List<SugarParameter>();
  37. // 强制租户过滤:租户只来自认证后可信 JWT,忽略前端 input.TenantId,杜绝跨租户可见。
  38. var tid = ResolveTenantOrThrow();
  39. where.Add("m.tenant_id = @TenantId");
  40. pars.Add(new SugarParameter("@TenantId", tid));
  41. if (!string.IsNullOrWhiteSpace(input.Supp))
  42. {
  43. where.Add("(m.supp LIKE @Supp OR m.sort_name LIKE @Supp)");
  44. pars.Add(new SugarParameter("@Supp", $"%{input.Supp.Trim()}%"));
  45. }
  46. if (!string.IsNullOrWhiteSpace(input.ItemNum))
  47. {
  48. where.Add("m.item_num LIKE @ItemNum");
  49. pars.Add(new SugarParameter("@ItemNum", $"%{input.ItemNum.Trim()}%"));
  50. }
  51. if (!string.IsNullOrWhiteSpace(input.RctDateFrom))
  52. {
  53. where.Add("m.rct_date >= @RctDateFrom");
  54. pars.Add(new SugarParameter("@RctDateFrom", $"{input.RctDateFrom.Trim()} 00:00:00"));
  55. }
  56. if (!string.IsNullOrWhiteSpace(input.RctDateTo))
  57. {
  58. where.Add("m.rct_date <= @RctDateTo");
  59. pars.Add(new SugarParameter("@RctDateTo", $"{input.RctDateTo.Trim()} 23:59:59"));
  60. }
  61. if (!string.IsNullOrWhiteSpace(input.PurOrd))
  62. {
  63. where.Add("m.pur_ord LIKE @PurOrd");
  64. pars.Add(new SugarParameter("@PurOrd", $"%{input.PurOrd.Trim()}%"));
  65. }
  66. if (!string.IsNullOrWhiteSpace(input.Receiver))
  67. {
  68. where.Add("m.receiver LIKE @Receiver");
  69. pars.Add(new SugarParameter("@Receiver", $"%{input.Receiver.Trim()}%"));
  70. }
  71. if (!string.IsNullOrWhiteSpace(input.SalesJob))
  72. {
  73. where.Add("m.sales_job LIKE @SalesJob");
  74. pars.Add(new SugarParameter("@SalesJob", $"%{input.SalesJob.Trim()}%"));
  75. }
  76. if (!string.IsNullOrWhiteSpace(input.Req))
  77. {
  78. where.Add("m.req LIKE @Req");
  79. pars.Add(new SugarParameter("@Req", $"%{input.Req.Trim()}%"));
  80. }
  81. if (!string.IsNullOrWhiteSpace(input.DopReq))
  82. {
  83. where.Add("m.dop_req LIKE @DopReq");
  84. pars.Add(new SugarParameter("@DopReq", $"%{input.DopReq.Trim()}%"));
  85. }
  86. var whereSql = string.Join(" AND ", where);
  87. var total = await _db.Ado.GetIntAsync(
  88. $"SELECT COUNT(1) FROM mdp_std_purchase_receipt m WHERE {whereSql}", pars);
  89. var list = await _db.Ado.SqlQueryAsync<PurchaseReceiptListRow>(
  90. $"""
  91. SELECT
  92. m.id AS Id,
  93. m.receiver AS Receiver,
  94. m.line AS Line,
  95. m.rct_date AS RctDate,
  96. m.supp AS Supp,
  97. m.sort_name AS SortName,
  98. m.item_num AS ItemNum,
  99. m.item_name AS ItemName,
  100. m.item_spec AS ItemSpec,
  101. m.um AS Um,
  102. m.qty_ordered AS QtyOrdered,
  103. m.qty_received AS QtyReceived,
  104. m.lot_serial AS LotSerial,
  105. m.location AS Location,
  106. m.pur_ord AS PurOrd,
  107. m.sales_job AS SalesJob,
  108. m.address1 AS Address1,
  109. m.req AS Req,
  110. m.dop_req AS DopReq,
  111. m.ord_nbr AS OrdNbr
  112. FROM mdp_std_purchase_receipt m
  113. WHERE {whereSql}
  114. ORDER BY {BuildOrderBy(input.OrderBy, input.OrderDir)}
  115. LIMIT {pageSize} OFFSET {offset}
  116. """,
  117. pars);
  118. return new { total, page, pageSize, list };
  119. }
  120. /// <summary>
  121. /// 排序白名单:仅允许按已展示列排序,杜绝 SQL 注入。
  122. /// </summary>
  123. private static string BuildOrderBy(string? orderBy, string? orderDir)
  124. {
  125. var column = orderBy switch
  126. {
  127. "receiver" => "m.receiver",
  128. "rctDate" => "m.rct_date",
  129. "itemNum" => "m.item_num",
  130. "sortName" => "m.sort_name",
  131. "qtyOrdered" => "m.qty_ordered",
  132. "qtyReceived" => "m.qty_received",
  133. "purOrd" => "m.pur_ord",
  134. "salesJob" => "m.sales_job",
  135. _ => "m.rct_date",
  136. };
  137. var direction = string.Equals(orderDir, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  138. return $"{column} {direction}, m.id DESC";
  139. }
  140. }