StockQueryService.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. using Admin.NET.Plugin.AiDOP.Infrastructure;
  2. using Admin.NET.Plugin.AiDOP.MaterialWarehouse.Dto;
  3. using Microsoft.Extensions.Options;
  4. namespace Admin.NET.Plugin.AiDOP.MaterialWarehouse;
  5. /// <summary>
  6. /// S5 库存查询:经 IInventoryBalanceReader,优先 LIVE_165,回落 mdp_std_inventory。
  7. /// Domain 由 SourceDomainTenantResolver 反查,禁止回落 8010。
  8. /// </summary>
  9. [ApiDescriptionSettings(Order = 300, Description = "库存查询")]
  10. [Route("api/S5StockQuery")]
  11. [NonUnify]
  12. public class StockQueryService : IDynamicApiController, ITransient
  13. {
  14. private const long MainTenantId = 1300000000001L;
  15. private readonly ISqlSugarClient _db;
  16. private readonly UserManager _userManager;
  17. private readonly IInventoryBalanceReader _balanceReader;
  18. private readonly SourceDomainTenantResolver _domainTenant;
  19. private readonly AidopInventoryOptions _opt;
  20. public StockQueryService(
  21. ISqlSugarClient db,
  22. UserManager userManager,
  23. IInventoryBalanceReader balanceReader,
  24. SourceDomainTenantResolver domainTenant,
  25. IOptions<AidopInventoryOptions> opt)
  26. {
  27. _db = db;
  28. _userManager = userManager;
  29. _balanceReader = balanceReader;
  30. _domainTenant = domainTenant;
  31. _opt = opt.Value;
  32. }
  33. private long ResolveTenantOrThrow()
  34. {
  35. var tid = _userManager.TenantId;
  36. if (tid <= 0)
  37. throw Oops.Oh("无法确定当前租户,请重新登录或选择目标租户");
  38. if (_userManager.SuperAdmin && tid == MainTenantId)
  39. throw Oops.Oh("超级管理员操作前必须选择目标租户");
  40. return tid;
  41. }
  42. private async Task<string> ResolveDomainOrThrowAsync(long tenantId, CancellationToken ct = default)
  43. {
  44. var sourceCode = string.IsNullOrWhiteSpace(_opt.SourceCode) ? "DOPDEMORQ_SQLSERVER" : _opt.SourceCode.Trim();
  45. try
  46. {
  47. return await _domainTenant.ResolveDomainAsync(sourceCode, tenantId, ct);
  48. }
  49. catch (Exception ex)
  50. {
  51. throw Oops.Oh($"无法解析当前租户 Domain 映射:{ex.Message}");
  52. }
  53. }
  54. private const string LocTypeExpr = "CASE WHEN t.typed='VMI' THEN 'VMI' WHEN t.typed='Supp' THEN 'O库' ELSE '' END";
  55. [DisplayName("库存查询列表")]
  56. [HttpGet("list")]
  57. public async Task<object> GetList([FromQuery] StockQueryListInput input, CancellationToken ct)
  58. {
  59. var tenantId = ResolveTenantOrThrow();
  60. var domain = await ResolveDomainOrThrowAsync(tenantId, ct);
  61. var page = await _balanceReader.QueryPageAsync(new InventoryBalanceQuery
  62. {
  63. TenantId = tenantId,
  64. Domain = domain,
  65. Type = input.Type,
  66. Location = input.Location,
  67. MaterialCode = input.MaterialCode,
  68. BatchNo = input.BatchNo,
  69. StockGt = input.StockGt,
  70. UnrestrictedGt = input.UnrestrictedGt,
  71. QcStockGt = input.QcStockGt,
  72. FrozenGt = input.FrozenGt,
  73. Page = input.Page,
  74. PageSize = input.PageSize
  75. }, ct);
  76. return new
  77. {
  78. total = page.Total,
  79. page = page.Page,
  80. pageSize = page.PageSize,
  81. list = page.List.Select(x => new StockQueryListRow
  82. {
  83. Type = x.Type,
  84. Location = x.Location,
  85. LocationName = x.LocationName,
  86. MaterialCode = x.MaterialCode,
  87. MaterialName = x.MaterialName,
  88. BatchNo = x.BatchNo,
  89. Shelf = x.Shelf,
  90. Version = x.Version,
  91. UnrestrictedStock = x.UnrestrictedStock,
  92. QcStock = x.QcStock,
  93. FrozenStock = x.FrozenStock,
  94. QtyOnHand = x.QtyOnHand,
  95. Unit = x.Unit,
  96. Supplier = x.Supplier
  97. }).ToList(),
  98. grandTotalQtyOnHand = page.GrandTotalQtyOnHand,
  99. grandUnrestricted = page.GrandUnrestricted,
  100. grandQc = page.GrandQc,
  101. grandFrozen = page.GrandFrozen,
  102. dataSource = page.DataSource,
  103. asOf = page.AsOf,
  104. isRealtime = page.IsRealtime
  105. };
  106. }
  107. [DisplayName("库存查询类型选项")]
  108. [HttpGet("type-options")]
  109. public async Task<object> GetTypeOptions(CancellationToken ct)
  110. {
  111. var tenantId = ResolveTenantOrThrow();
  112. var domain = await ResolveDomainOrThrowAsync(tenantId, ct);
  113. var list = await _db.Ado.SqlQueryAsync<StockQueryTypeOption>(
  114. $"""
  115. SELECT DISTINCT {LocTypeExpr.Replace("t.typed", "typed")} AS Val
  116. FROM LocationMaster
  117. WHERE Domain = @Domain AND tenant_id = @TenantId AND {LocTypeExpr.Replace("t.typed", "typed")} <> ''
  118. ORDER BY Val
  119. """,
  120. new List<SugarParameter> { new("@Domain", domain), new("@TenantId", tenantId) });
  121. return list;
  122. }
  123. [DisplayName("库存查询库位选项")]
  124. [HttpGet("location-options")]
  125. public async Task<object> GetLocationOptions(CancellationToken ct)
  126. {
  127. var tenantId = ResolveTenantOrThrow();
  128. var domain = await ResolveDomainOrThrowAsync(tenantId, ct);
  129. var list = await _db.Ado.SqlQueryAsync<StockQueryLocationOption>(
  130. """
  131. SELECT location AS Val, descr AS Label
  132. FROM LocationMaster
  133. WHERE Domain = @Domain AND tenant_id = @TenantId AND IFNULL(location,'') <> ''
  134. ORDER BY location
  135. """,
  136. new List<SugarParameter> { new("@Domain", domain), new("@TenantId", tenantId) });
  137. return list;
  138. }
  139. }