ProductionIssueService.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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_production_issue(头级)。
  7. /// 由 ProductionIssueMdpSyncService 从 aidopdev.NbrMaster Type='SM' 同步标准化而来。
  8. ///
  9. /// 本服务仅 SELECT:无新增/编辑/删除/发料确认/库存事务/状态流转/明细。
  10. /// 领料单为头级列表(一单一行),不 JOIN 明细,无重复。
  11. /// </summary>
  12. [ApiDescriptionSettings(Order = 305, Description = "生产领料单")]
  13. [Route("api/ProductionIssue")]
  14. [NonUnify]
  15. public class ProductionIssueService : IDynamicApiController, ITransient
  16. {
  17. private readonly ISqlSugarClient _db;
  18. private readonly UserManager _userManager;
  19. public ProductionIssueService(ISqlSugarClient db, UserManager userManager)
  20. {
  21. _db = db;
  22. _userManager = userManager;
  23. }
  24. /// <summary>严格可信租户解析,见 <see cref="Infrastructure.AidopTenantScope.ResolveOrThrow"/>。</summary>
  25. private long ResolveTenantOrThrow() => Infrastructure.AidopTenantScope.ResolveOrThrow(_userManager);
  26. /// <summary>
  27. /// 生产领料单列表(只读分页查询)。
  28. /// </summary>
  29. [DisplayName("生产领料单列表")]
  30. [HttpGet("list")]
  31. public async Task<object> GetList([FromQuery] ProductionIssueListInput input)
  32. {
  33. var page = input.Page <= 0 ? 1 : input.Page;
  34. var pageSize = input.PageSize <= 0 ? 10 : input.PageSize;
  35. var offset = (page - 1) * pageSize;
  36. var where = new List<string> { "1=1" };
  37. var pars = new List<SugarParameter>();
  38. // 强制租户过滤:租户只来自认证后可信 JWT,忽略前端 input.TenantId,杜绝跨租户可见。
  39. var tid = ResolveTenantOrThrow();
  40. where.Add("m.tenant_id = @TenantId");
  41. pars.Add(new SugarParameter("@TenantId", tid));
  42. if (!string.IsNullOrWhiteSpace(input.Nbr))
  43. {
  44. where.Add("m.nbr LIKE @Nbr");
  45. pars.Add(new SugarParameter("@Nbr", $"%{input.Nbr.Trim()}%"));
  46. }
  47. if (!string.IsNullOrWhiteSpace(input.WorkOrd))
  48. {
  49. where.Add("m.work_ord LIKE @WorkOrd");
  50. pars.Add(new SugarParameter("@WorkOrd", $"%{input.WorkOrd.Trim()}%"));
  51. }
  52. if (!string.IsNullOrWhiteSpace(input.Status))
  53. {
  54. where.Add("m.status = @Status");
  55. pars.Add(new SugarParameter("@Status", input.Status.Trim()));
  56. }
  57. if (!string.IsNullOrWhiteSpace(input.ProdLine))
  58. {
  59. where.Add("m.prod_line LIKE @ProdLine");
  60. pars.Add(new SugarParameter("@ProdLine", $"%{input.ProdLine.Trim()}%"));
  61. }
  62. if (!string.IsNullOrWhiteSpace(input.DateFrom))
  63. {
  64. where.Add("m.issue_date >= @DateFrom");
  65. pars.Add(new SugarParameter("@DateFrom", $"{input.DateFrom.Trim()} 00:00:00"));
  66. }
  67. if (!string.IsNullOrWhiteSpace(input.DateTo))
  68. {
  69. where.Add("m.issue_date <= @DateTo");
  70. pars.Add(new SugarParameter("@DateTo", $"{input.DateTo.Trim()} 23:59:59"));
  71. }
  72. var whereSql = string.Join(" AND ", where);
  73. var total = await _db.Ado.GetIntAsync(
  74. $"SELECT COUNT(1) FROM mdp_std_production_issue m WHERE {whereSql}", pars);
  75. var list = await _db.Ado.SqlQueryAsync<ProductionIssueListRow>(
  76. $"""
  77. SELECT
  78. m.id AS Id,
  79. m.nbr AS Nbr,
  80. m.issue_date AS IssueDate,
  81. m.status_desc AS StatusDesc,
  82. m.work_ord AS WorkOrd,
  83. m.department_desc AS DepartmentDesc,
  84. m.prod_line AS ProdLine,
  85. m.qty_ord AS QtyOrd,
  86. m.issue_user AS IssueUser,
  87. m.remark AS Remark,
  88. m.create_user AS CreateUser,
  89. m.source_create_time AS CreateTime
  90. FROM mdp_std_production_issue m
  91. WHERE {whereSql}
  92. ORDER BY {BuildOrderBy(input.OrderBy, input.OrderDir)}
  93. LIMIT {pageSize} OFFSET {offset}
  94. """,
  95. pars);
  96. return new { total, page, pageSize, list };
  97. }
  98. /// <summary>
  99. /// 排序白名单:仅允许按已展示列排序,杜绝 SQL 注入;非法字段回落默认。
  100. /// </summary>
  101. private static string BuildOrderBy(string? orderBy, string? orderDir)
  102. {
  103. var column = orderBy switch
  104. {
  105. "nbr" => "m.nbr",
  106. "issueDate" => "m.issue_date",
  107. "status" => "m.status",
  108. "workOrd" => "m.work_ord",
  109. "prodLine" => "m.prod_line",
  110. "createTime" => "m.source_create_time",
  111. _ => "m.issue_date",
  112. };
  113. var direction = string.Equals(orderDir, "asc", StringComparison.OrdinalIgnoreCase) ? "ASC" : "DESC";
  114. return $"{column} {direction}, m.id DESC";
  115. }
  116. }