LabelQueryService.cs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. using Admin.NET.Plugin.AiDOP.MaterialWarehouse.Dto;
  2. namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse;
  3. /// <summary>
  4. /// S5 标签查询 只读 list 服务。
  5. ///
  6. /// 数据源:aidopdev.MissedPrint(legacy 标签/条码记录表)。状态描述经 GeneralizedCodeMaster(FldName='BarcodeStatus') 解码。
  7. /// 参考本仓既有同表只读投影先例:Controllers/S0/Manufacturing/AdoS0MfgProcessFlowCardsController(同解 BarcodeStatus)。
  8. ///
  9. /// 本服务仅 SELECT:无新增/编辑/删除/库位转移/库存事务/状态流转/物料卡写入;扁平列表,无 detail。
  10. /// MissedPrint 无 tenant_id 列,不做租户/工厂域过滤(对齐既有 MissedPrint 只读投影先例;按工厂域隔离为后续独立议题)。
  11. /// </summary>
  12. [ApiDescriptionSettings(Order = 302, Description = "标签查询")]
  13. [Route("api/S5LabelQuery")]
  14. [AllowAnonymous]
  15. [NonUnify]
  16. public class LabelQueryService : IDynamicApiController, ITransient
  17. {
  18. private readonly ISqlSugarClient _db;
  19. public LabelQueryService(ISqlSugarClient db)
  20. {
  21. _db = db;
  22. }
  23. /// <summary>
  24. /// 标签查询列表(只读分页查询)。
  25. /// </summary>
  26. [DisplayName("标签查询列表")]
  27. [HttpGet("list")]
  28. public async Task<object> GetList([FromQuery] LabelQueryListInput input)
  29. {
  30. var page = input.Page <= 0 ? 1 : input.Page;
  31. var pageSize = input.PageSize <= 0 ? 10 : input.PageSize;
  32. var offset = (page - 1) * pageSize;
  33. var where = new List<string> { "1=1" };
  34. var pars = new List<SugarParameter>();
  35. if (!string.IsNullOrWhiteSpace(input.ItemNum))
  36. {
  37. where.Add("m.ItemNum LIKE @ItemNum");
  38. pars.Add(new SugarParameter("@ItemNum", $"%{input.ItemNum.Trim()}%"));
  39. }
  40. if (!string.IsNullOrWhiteSpace(input.LotSerial))
  41. {
  42. where.Add("m.LotSerial LIKE @LotSerial");
  43. pars.Add(new SugarParameter("@LotSerial", $"%{input.LotSerial.Trim()}%"));
  44. }
  45. if (!string.IsNullOrWhiteSpace(input.Location))
  46. {
  47. where.Add("m.Location = @Location");
  48. pars.Add(new SugarParameter("@Location", input.Location.Trim()));
  49. }
  50. if (!string.IsNullOrWhiteSpace(input.Shelf))
  51. {
  52. where.Add("m.Shelf LIKE @Shelf");
  53. pars.Add(new SugarParameter("@Shelf", $"%{input.Shelf.Trim()}%"));
  54. }
  55. if (!string.IsNullOrWhiteSpace(input.ShipperNbr))
  56. {
  57. where.Add("m.ShipperNbr LIKE @ShipperNbr");
  58. pars.Add(new SugarParameter("@ShipperNbr", $"%{input.ShipperNbr.Trim()}%"));
  59. }
  60. if (!string.IsNullOrWhiteSpace(input.Status))
  61. {
  62. where.Add("m.Status = @Status");
  63. pars.Add(new SugarParameter("@Status", input.Status.Trim()));
  64. }
  65. if (!string.IsNullOrWhiteSpace(input.BarCode))
  66. {
  67. where.Add("m.BarCode LIKE @BarCode");
  68. pars.Add(new SugarParameter("@BarCode", $"%{input.BarCode.Trim()}%"));
  69. }
  70. var whereSql = string.Join(" AND ", where);
  71. var total = await _db.Ado.GetIntAsync(
  72. $"SELECT COUNT(1) FROM MissedPrint m WHERE {whereSql}", pars);
  73. var list = await _db.Ado.SqlQueryAsync<LabelQueryListRow>(
  74. $"""
  75. SELECT
  76. m.RecID AS Id,
  77. m.BarCode AS BarCode,
  78. m.ItemNum AS ItemNum,
  79. m.Descr AS Descr,
  80. m.Product AS Product,
  81. m.Qty AS Qty,
  82. m.Status AS Status,
  83. COALESCE(g.Comments, m.Status) AS StatusDesc,
  84. m.LotSerial AS LotSerial,
  85. m.Location AS Location,
  86. m.Shelf AS Shelf,
  87. m.OrdNbr AS OrdNbr,
  88. m.PackingQty AS PackingQty,
  89. m.ProdDate AS ProdDate,
  90. m.WorkOrd AS WorkOrd,
  91. m.ProdLine AS ProdLine,
  92. m.LabelFormat AS LabelFormat,
  93. m.Position AS Position,
  94. m.Carton AS Carton,
  95. m.Period AS Period,
  96. m.Supply AS Supply,
  97. m.PurOrd AS PurOrd,
  98. m.PurLine AS PurLine,
  99. m.ShipperNbr AS ShipperNbr,
  100. m.ShipperLine AS ShipperLine,
  101. m.InvDate AS InvDate,
  102. m.ExpireDate AS ExpireDate,
  103. m.Remark AS Remark
  104. FROM MissedPrint m
  105. LEFT JOIN GeneralizedCodeMaster g ON g.FldName = 'BarcodeStatus' AND g.Val = m.Status
  106. WHERE {whereSql}
  107. ORDER BY {BuildOrderBy(input.OrderBy, input.OrderDir)}
  108. LIMIT {pageSize} OFFSET {offset}
  109. """,
  110. pars);
  111. return new { total, page, pageSize, list };
  112. }
  113. /// <summary>
  114. /// 状态下拉选项(BarcodeStatus 码表,Val + 中文描述,只读)。
  115. /// </summary>
  116. [DisplayName("标签查询状态选项")]
  117. [HttpGet("status-options")]
  118. public async Task<object> GetStatusOptions()
  119. {
  120. var list = await _db.Ado.SqlQueryAsync<LabelQueryStatusOption>(
  121. """
  122. SELECT Val AS Val, Comments AS Label
  123. FROM GeneralizedCodeMaster
  124. WHERE FldName = 'BarcodeStatus' AND Comments IS NOT NULL AND Comments <> ''
  125. GROUP BY Val, Comments
  126. ORDER BY Val
  127. """);
  128. return list;
  129. }
  130. /// <summary>
  131. /// 库位下拉选项(MissedPrint.Location distinct,只读)。
  132. /// </summary>
  133. [DisplayName("标签查询库位选项")]
  134. [HttpGet("location-options")]
  135. public async Task<object> GetLocationOptions()
  136. {
  137. var list = await _db.Ado.SqlQueryAsync<LabelQueryLocationOption>(
  138. """
  139. SELECT DISTINCT Location AS Val
  140. FROM MissedPrint
  141. WHERE Location IS NOT NULL AND Location <> ''
  142. ORDER BY Location
  143. """);
  144. return list;
  145. }
  146. /// <summary>
  147. /// 排序白名单:仅允许按已展示列排序,杜绝 SQL 注入。
  148. /// </summary>
  149. private static string BuildOrderBy(string? orderBy, string? orderDir)
  150. {
  151. var column = orderBy switch
  152. {
  153. "barCode" => "m.BarCode",
  154. "itemNum" => "m.ItemNum",
  155. "qty" => "m.Qty",
  156. "status" => "m.Status",
  157. "lotSerial" => "m.LotSerial",
  158. "location" => "m.Location",
  159. "prodDate" => "m.ProdDate",
  160. "invDate" => "m.InvDate",
  161. "expireDate" => "m.ExpireDate",
  162. _ => "m.CreateTime",
  163. };
  164. var direction = string.Equals(orderDir, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  165. return $"{column} {direction}, m.RecID DESC";
  166. }
  167. }