using Fake.Application; using Microsoft.AspNetCore.Mvc; using Oms.Application.Dtos.Operations; using Oms.Application.Dtos.Shared; using Oms.Application.Services.Orders; using Oms.Application.Shared; using Oms.Domain.ChannelsAggregate.Repositories; using Oms.Domain.OperationsAggregate.Entities; using Oms.Domain.OperationsAggregate.Repositories; using Oms.Domain.OrdersAggregate.Repositories; using Oms.Domain.SystemAggregate.Repositories; namespace Oms.Application.Services.Operations; /// /// 入库记录 API 服务。 /// [ApiExplorerSettings(GroupName = "OMS-Operation")] public class InboundRecordService : ApplicationService { private IInboundRecordRepository InboundRecordRepository => LazyServiceProvider.GetRequiredService(); private IOrderRepository OrderRepository => LazyServiceProvider.GetRequiredService(); private ISalesProductRepository SalesProductRepository => LazyServiceProvider.GetRequiredService(); private IWarehouseAddressRepository WarehouseAddressRepository => LazyServiceProvider.GetRequiredService(); private ICountryRepository CountryRepository => LazyServiceProvider.GetRequiredService(); private OrderService OrderService => LazyServiceProvider.GetRequiredService(); /// /// 按单号查询订单(入库扫描展示销售产品、目的国,只读)。 /// [HttpGet("/operation/inbound/order-lookup")] public async Task LookupOrderAsync([FromQuery] string waybillNo) { if (string.IsNullOrWhiteSpace(waybillNo)) { throw new BusinessException("请扫描或输入单号"); } var order = await OrderRepository.GetByWaybillNoAsync(waybillNo.Trim()); if (order == null) { throw new BusinessException("未找到对应订单,请检查单号"); } var output = new InboundOrderLookupOutput { OrderId = order.Id, ServiceNo = order.ServiceNo, ClientNo = order.ClientNo, TrackNo = order.TrackNo, ProductCode = order.ProductCode, DestinationCountry = order.ConsigneeCountry, OrderStatus = order.OrderStatus, OrderStatusName = OmsDocHelper.GetOrderStatusName(order.OrderStatus) }; if (!string.IsNullOrWhiteSpace(order.ProductCode)) { var product = await SalesProductRepository.GetByProductCodeAsync(order.ProductCode.Trim()); if (product != null) { output.SalesProductId = product.Id; output.SalesProductName = product.ProductName; } } if (!string.IsNullOrWhiteSpace(order.ConsigneeCountry)) { var country = await CountryRepository.FirstOrDefaultAsync(x => x.CountryCode == order.ConsigneeCountry); output.DestinationCountryName = country?.ChineseName ?? country?.EnglishName ?? order.ConsigneeCountry; } return output; } /// /// 新增入库记录。 /// /// 入参:入库记录创建参数。 /// 入库记录结果。 /// /// 核心业务逻辑: /// 1. 校验入参与关联对象、唯一性等前置条件。 /// 2. 组装目标实体及相关明细数据。 /// 3. 写入审计字段并持久化结果。 /// [HttpPost("/operation/inbound/create")] public async Task CreateAsync(CreateInboundRecordInput input) { if (string.IsNullOrWhiteSpace(input.WaybillNo)) { throw new BusinessException("运单号不能为空"); } var order = await OrderRepository.GetByWaybillNoAsync(input.WaybillNo.Trim()); if (order == null) { throw new BusinessException("未找到对应订单,请检查单号"); } if (!input.Weight.HasValue || !input.Length.HasValue || !input.Width.HasValue || !input.Height.HasValue || input.Weight <= 0 || input.Length <= 0 || input.Width <= 0 || input.Height <= 0) { throw new BusinessException("收货实重与长宽高不能为空且须大于 0"); } int? salesProductId = null; string? destinationCountry = order.ConsigneeCountry; if (!string.IsNullOrWhiteSpace(order.ProductCode)) { var product = await SalesProductRepository.GetByProductCodeAsync(order.ProductCode.Trim()); salesProductId = product?.Id; } var now = DateTime.Now; var operatorId = CurrentUser.GetId(); var entity = new InboundRecord { WaybillNo = input.WaybillNo.Trim(), Weight = input.Weight, Length = input.Length, Width = input.Width, Height = input.Height, SalesProductId = salesProductId, DestinationCountry = destinationCountry, WarehouseId = input.WarehouseId ?? order.WarehouseId, OperateTime = now, OperatorId = operatorId }; await OrderService.ApplyReceiveBillingByWaybillAsync( input.WaybillNo.Trim(), input.Length, input.Width, input.Height, input.Weight); var savedEntity = await InboundRecordRepository.InsertAsync(entity); return ObjectMapper.Map(savedEntity); } /// /// 查询最近入库记录列表。 /// /// 入参:返回记录条数上限。 /// 入参:warehouseId。 /// 入库记录列表结果。 /// /// 核心业务逻辑: /// 1. 根据查询条件构造筛选规则。 /// 2. 查询目标数据并补齐关联展示字段。 /// 3. 按接口约定返回列表结果。 /// [HttpGet("/operation/inbound/recent-list")] public async Task> GetRecentListAsync([FromQuery] int limit = 20, [FromQuery] int? warehouseId = null) { var hasWarehouseFilter = warehouseId.HasValue; var warehouseIdValue = warehouseId.GetValueOrDefault(); var entities = await InboundRecordRepository.GetPagedListAsync( x => !hasWarehouseFilter || x.WarehouseId == warehouseIdValue, 1, limit <= 0 ? 20 : limit, new Dictionary { [nameof(InboundRecord.OperateTime)] = false, [nameof(InboundRecord.Id)] = false }); var salesProductIds = entities.Where(x => x.SalesProductId.HasValue).Select(x => x.SalesProductId!.Value).Distinct().ToArray(); var warehouseIds = entities.Where(x => x.WarehouseId.HasValue).Select(x => x.WarehouseId!.Value).Distinct().ToArray(); var products = salesProductIds.Length == 0 ? [] : await SalesProductRepository.GetListAsync(x => salesProductIds.Contains(x.Id)); var warehouses = warehouseIds.Length == 0 ? [] : await WarehouseAddressRepository.GetListAsync(x => warehouseIds.Contains(x.Id)); var productMap = products.ToDictionary(x => x.Id); var warehouseMap = warehouses.ToDictionary(x => x.Id); var items = entities.Select(entity => { var output = ObjectMapper.Map(entity); output.SalesProductName = entity.SalesProductId.HasValue && productMap.TryGetValue(entity.SalesProductId.Value, out var product) ? product.ProductName : null; output.WarehouseName = entity.WarehouseId.HasValue && warehouseMap.TryGetValue(entity.WarehouseId.Value, out var warehouse) ? warehouse.WarehouseName : null; output.OperatorName = entity.OperatorId?.ToString(); return output; }).ToList(); return new OmsItemsResult { Items = items }; } }