为 WarehouseService、PlatformStoreService、PlatformStoreOrderService 全部新增接口添加 XML 注释,便于 Swagger 中文展示与前端对接。 Co-authored-by: Cursor <cursoragent@cursor.com>
147 lines
5.7 KiB
C#
147 lines
5.7 KiB
C#
using Fake.Application;
|
||
using Microsoft.AspNetCore.Mvc;
|
||
using Oms.Application.Dtos.Shared;
|
||
using Oms.Application.Dtos.Warehouse;
|
||
using Oms.Application.Shared;
|
||
|
||
namespace Oms.Application.Services.Warehouse;
|
||
|
||
public partial class WarehouseService
|
||
{
|
||
/// <summary>
|
||
/// 查询库存列表。
|
||
/// </summary>
|
||
/// <param name="input">入参:库存分页查询条件。</param>
|
||
/// <returns>按 SKU 汇总的库存分页结果。</returns>
|
||
[HttpPost("/warehouse/inventory/list")]
|
||
public async Task<OmsPageResult<WarehouseInventoryOutput>> GetInventoryListAsync(WarehouseInventoryListInput input)
|
||
{
|
||
var customerId = await ResolveCustomerIdAsync(input.CustomerId);
|
||
var inventories = await InventoryRepository.GetListAsync(x =>
|
||
x.CustomerId == customerId
|
||
&& x.Qty > 0
|
||
&& (!input.WarehouseId.HasValue || x.WarehouseId == input.WarehouseId.Value));
|
||
|
||
var skuIds = inventories.Select(x => x.SkuId).Distinct().ToArray();
|
||
var skus = skuIds.Length == 0
|
||
? []
|
||
: await SkuRepository.GetListAsync(x => x.CustomerId == customerId && skuIds.Contains(x.Id));
|
||
var skuMap = skus.ToDictionary(x => x.Id);
|
||
|
||
var keyword = input.Keyword?.Trim();
|
||
var grouped = inventories
|
||
.GroupBy(x => new { x.SkuId, x.WarehouseId })
|
||
.Select(g =>
|
||
{
|
||
skuMap.TryGetValue(g.Key.SkuId, out var sku);
|
||
return new WarehouseInventoryOutput
|
||
{
|
||
SkuId = g.Key.SkuId,
|
||
SkuCode = sku?.SkuCode ?? string.Empty,
|
||
SkuName = sku?.SkuName ?? string.Empty,
|
||
WarehouseId = g.Key.WarehouseId,
|
||
TotalQty = g.Sum(x => x.Qty),
|
||
BinCount = g.Count()
|
||
};
|
||
})
|
||
.Where(x =>
|
||
string.IsNullOrWhiteSpace(keyword)
|
||
|| x.SkuCode.Contains(keyword!, StringComparison.OrdinalIgnoreCase)
|
||
|| x.SkuName.Contains(keyword!, StringComparison.OrdinalIgnoreCase))
|
||
.OrderBy(x => x.SkuCode)
|
||
.ThenBy(x => x.WarehouseId)
|
||
.ToList();
|
||
|
||
var pageIndex = OmsDocHelper.NormalizePage(input.Page);
|
||
var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize);
|
||
var skip = (pageIndex - 1) * pageSize;
|
||
|
||
return new OmsPageResult<WarehouseInventoryOutput>
|
||
{
|
||
Items = grouped.Skip(skip).Take(pageSize).ToList(),
|
||
Total = grouped.Count
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 查询 SKU 在各库位的库存明细。
|
||
/// </summary>
|
||
/// <param name="skuId">SKU ID。</param>
|
||
/// <param name="warehouseId">仓库 ID。</param>
|
||
/// <param name="customerId">客户 ID(可选)。</param>
|
||
/// <returns>库位库存明细列表。</returns>
|
||
[HttpGet("/warehouse/inventory/bin-detail")]
|
||
public async Task<OmsItemsResult<WarehouseInventoryBinDetailOutput>> GetInventoryBinDetailAsync(
|
||
[FromQuery] int skuId,
|
||
[FromQuery] int warehouseId,
|
||
[FromQuery] int? customerId = null)
|
||
{
|
||
if (skuId <= 0 || warehouseId <= 0)
|
||
{
|
||
throw new BusinessException("SKU 和仓库不能为空");
|
||
}
|
||
|
||
var resolvedCustomerId = await ResolveCustomerIdAsync(customerId);
|
||
await GetSkuOrThrowAsync(resolvedCustomerId, skuId);
|
||
|
||
var inventories = await InventoryRepository.GetListAsync(x =>
|
||
x.CustomerId == resolvedCustomerId
|
||
&& x.WarehouseId == warehouseId
|
||
&& x.SkuId == skuId
|
||
&& x.Qty > 0);
|
||
var binIds = inventories.Select(x => x.BinLocationId).Distinct().ToArray();
|
||
var bins = binIds.Length == 0
|
||
? []
|
||
: await BinLocationRepository.GetListAsync(x => binIds.Contains(x.Id));
|
||
var binMap = bins.ToDictionary(x => x.Id);
|
||
|
||
return new OmsItemsResult<WarehouseInventoryBinDetailOutput>
|
||
{
|
||
Items = inventories
|
||
.OrderBy(x => x.BinLocationId)
|
||
.Select(x =>
|
||
{
|
||
binMap.TryGetValue(x.BinLocationId, out var bin);
|
||
return new WarehouseInventoryBinDetailOutput
|
||
{
|
||
Id = x.Id,
|
||
BinLocationId = x.BinLocationId,
|
||
BinCode = bin?.BinCode ?? string.Empty,
|
||
BinName = bin?.BinName,
|
||
Qty = x.Qty
|
||
};
|
||
})
|
||
.ToList()
|
||
};
|
||
}
|
||
|
||
/// <summary>
|
||
/// 库存移库(库位间调拨)。
|
||
/// </summary>
|
||
/// <param name="input">入参:源库位、目标库位与数量。</param>
|
||
[HttpPost("/warehouse/inventory/relocate")]
|
||
public async Task RelocateInventoryAsync(WarehouseInventoryRelocateInput input)
|
||
{
|
||
var customerId = await ResolveCustomerIdAsync(input.CustomerId);
|
||
if (input.SourceBinLocationId == input.TargetBinLocationId)
|
||
{
|
||
throw new BusinessException("源库位与目标库位不能相同");
|
||
}
|
||
|
||
await GetSkuOrThrowAsync(customerId, input.SkuId);
|
||
|
||
var source = await InventoryRepository.FirstOrDefaultAsync(x =>
|
||
x.CustomerId == customerId
|
||
&& x.WarehouseId == input.WarehouseId
|
||
&& x.SkuId == input.SkuId
|
||
&& x.BinLocationId == input.SourceBinLocationId);
|
||
if (source == null || source.Qty < input.Qty)
|
||
{
|
||
throw new BusinessException("源库位库存不足");
|
||
}
|
||
|
||
await UpsertInventoryQtyAsync(customerId, input.WarehouseId, input.SkuId, input.SourceBinLocationId, -input.Qty);
|
||
await UpsertInventoryQtyAsync(customerId, input.WarehouseId, input.SkuId, input.TargetBinLocationId, input.Qty);
|
||
}
|
||
}
|