diff --git a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs
index d61441a..9e91592 100644
--- a/src/Oms.Application/Services/Orders/OrderService.Helpers.cs
+++ b/src/Oms.Application/Services/Orders/OrderService.Helpers.cs
@@ -16,6 +16,7 @@ using Oms.Application.Storage;
using Oms.Domain.OrdersAggregate;
using Oms.Domain.ChannelsAggregate.Consts;
using Oms.Domain.ChannelsAggregate.Entities;
+using Oms.Domain.CustomersAggregate.Entities;
using Oms.Domain.OrdersAggregate.Consts;
using Oms.Domain.OrdersAggregate.Entities;
using Oms.Domain.SystemAggregate.Entities;
@@ -296,6 +297,298 @@ public partial class OrderService
return outputs;
}
+ ///
+ /// 列表页专用映射:不加载申报明细/费用明细,并行 enrichment,减小 JSON 与 DB 开销。
+ ///
+ private async Task> MapOrderListOutputsAsync(IReadOnlyCollection orders)
+ {
+ if (orders.Count == 0)
+ {
+ return [];
+ }
+
+ var orderIds = orders.Select(x => x.Id).ToArray();
+ var warehouseIds = orders.Where(x => x.WarehouseId.HasValue).Select(x => x.WarehouseId!.Value).Distinct().ToArray();
+ var productCodes = orders
+ .Where(x => !string.IsNullOrWhiteSpace(x.ProductCode))
+ .Select(x => x.ProductCode!.Trim())
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+ var channelCodes = orders
+ .Where(x => !string.IsNullOrWhiteSpace(x.ChannelCode))
+ .Select(x => x.ChannelCode!.Trim())
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+ var countryCodes = orders
+ .Where(x => !string.IsNullOrWhiteSpace(x.ConsigneeCountry))
+ .Select(x => x.ConsigneeCountry!.Trim())
+ .Distinct(StringComparer.OrdinalIgnoreCase)
+ .ToArray();
+ var customerIds = orders
+ .Where(x => x.CustomerId is > 0)
+ .Select(x => x.CustomerId!.Value)
+ .Distinct()
+ .ToArray();
+ var pickupOrderIds = orders
+ .Where(x => x.PickupOrderId is > 0)
+ .Select(x => x.PickupOrderId!.Value)
+ .Distinct()
+ .ToArray();
+
+ var sendersTask = OrderRepository.GetSendersByOrderIdsAsync(orderIds);
+ var recipientsTask = OrderRepository.GetRecipientsByOrderIdsAsync(orderIds);
+ var weightsTask = OrderRepository.GetWeightsByOrderIdsAsync(orderIds);
+ var receivableSumTask = OrderRepository.GetReceivableSumByOrderIdsAsync(orderIds);
+ var warehousesTask = warehouseIds.Length == 0
+ ? Task.FromResult(new List())
+ : WarehouseAddressRepository.GetListAsync(x => warehouseIds.Contains(x.Id));
+ var productsTask = productCodes.Length == 0
+ ? Task.FromResult(new List())
+ : SalesProductRepository.GetListAsync(x => productCodes.Contains(x.ProductCode));
+ var channelsTask = channelCodes.Length == 0
+ ? Task.FromResult(new List())
+ : ServiceChannelRepository.GetListAsync(x => channelCodes.Contains(x.ChannelCode));
+ var countriesTask = countryCodes.Length == 0
+ ? Task.FromResult(new List())
+ : CountryRepository.GetListAsync(x => countryCodes.Contains(x.CountryCode));
+ var auditUserNameTask = UserDisplayNameResolver.ResolveManyAsync(
+ orders.SelectMany(x => new[] { x.CreateUserId, x.UpdateUserId }));
+ var customersTask = customerIds.Length == 0
+ ? Task.FromResult(new List())
+ : CustomerRepository.GetListAsync(x => customerIds.Contains(x.Id));
+ var pickupOrdersTask = pickupOrderIds.Length == 0
+ ? Task.FromResult(new List())
+ : PickupOrderRepository.GetListAsync(x => pickupOrderIds.Contains(x.Id));
+ var bindSummaryTask = LoadStockBindListSummariesAsync(orderIds);
+
+ await Task.WhenAll(
+ sendersTask,
+ recipientsTask,
+ weightsTask,
+ receivableSumTask,
+ warehousesTask,
+ productsTask,
+ channelsTask,
+ countriesTask,
+ auditUserNameTask,
+ customersTask,
+ pickupOrdersTask,
+ bindSummaryTask);
+
+ var senders = await sendersTask;
+ var recipients = await recipientsTask;
+ var weights = await weightsTask;
+ var receivableSumMap = await receivableSumTask;
+ var warehouses = await warehousesTask;
+ var products = await productsTask;
+ var channels = await channelsTask;
+ var countries = await countriesTask;
+ var auditUserNameMap = await auditUserNameTask;
+ var customers = await customersTask;
+ var pickupOrders = await pickupOrdersTask;
+ var bindSummaryMap = await bindSummaryTask;
+
+ var senderMap = senders.GroupBy(x => x.OrderId).ToDictionary(x => x.Key, x => x.First());
+ var recipientMap = recipients.GroupBy(x => x.OrderId).ToDictionary(x => x.Key, x => x.First());
+ var weightMap = weights.GroupBy(x => x.OrderId).ToDictionary(x => x.Key, x => x.ToList());
+ var warehouseMap = warehouses.ToDictionary(x => x.Id);
+ var productMap = products.ToDictionary(x => x.ProductCode, StringComparer.OrdinalIgnoreCase);
+ var channelMap = channels.ToDictionary(x => x.ChannelCode, StringComparer.OrdinalIgnoreCase);
+ var countryMap = countries.ToDictionary(x => x.CountryCode, StringComparer.OrdinalIgnoreCase);
+ var customerMap = customers.ToDictionary(x => x.Id);
+ var pickupNoMap = pickupOrders.ToDictionary(x => x.Id, x => x.PickupNo);
+ var carrierWaybillNoMap = pickupOrders
+ .Where(x => !string.IsNullOrWhiteSpace(x.CarrierWaybillNo))
+ .ToDictionary(x => x.Id, x => x.CarrierWaybillNo!);
+
+ var addressCountryIds = senders
+ .Select(x => x.Country)
+ .Concat(recipients.Select(x => x.Country))
+ .Where(x => x.HasValue)
+ .Select(x => x!.Value)
+ .Distinct()
+ .ToArray();
+ var addressCountries = addressCountryIds.Length == 0
+ ? []
+ : await CountryRepository.GetListAsync(x => addressCountryIds.Contains(x.Id));
+ var countryIdCodeMap = addressCountries.ToDictionary(
+ x => x.Id,
+ x => x.CountryCode ?? string.Empty);
+
+ var outputs = orders.Select(order =>
+ {
+ senderMap.TryGetValue(order.Id, out var sender);
+ recipientMap.TryGetValue(order.Id, out var recipient);
+ weightMap.TryGetValue(order.Id, out var orderWeights);
+ var chargeableWeight = ResolveDisplayChargeableWeight(orderWeights);
+ decimal? receivableAmount = receivableSumMap.TryGetValue(order.Id, out var sum)
+ ? sum
+ : null;
+ var additionalServices = NormalizeAdditionalServiceCodes(
+ OmsDocHelper.DeserializeJson>(order.AdditionalServices));
+ var hasInsurance = additionalServices.Any(x =>
+ string.Equals(x, "insurance", StringComparison.OrdinalIgnoreCase));
+ string? customerName = null;
+ string? customerCode = null;
+ if (order.CustomerId is > 0 && customerMap.TryGetValue(order.CustomerId.Value, out var customer))
+ {
+ customerName = customer.CustomerName;
+ customerCode = customer.CustomerCode;
+ }
+
+ SalesProduct? product = null;
+ if (!string.IsNullOrWhiteSpace(order.ProductCode))
+ {
+ productMap.TryGetValue(order.ProductCode.Trim(), out product);
+ }
+
+ bindSummaryMap.TryGetValue(order.Id, out var bindSummary);
+
+ return new OrderOutput
+ {
+ Id = order.Id,
+ ClientNo = order.ClientNo,
+ TrackNo = order.TrackNo,
+ DownstreamServiceNo = order.DownstreamServiceNo,
+ ServiceNo = order.ServiceNo,
+ ProductCode = order.ProductCode,
+ ProductName = product?.ProductName,
+ MaxTimeliness = product?.MaxTimeliness,
+ ChannelCode = order.ChannelCode,
+ DownstreamSupplierCode = order.DownstreamSupplierCode,
+ DownstreamSupplierName = order.DownstreamSupplierName,
+ DownstreamAppKey = order.DownstreamAppKey,
+ DownstreamAppId = order.DownstreamAppId,
+ ChannelForecastCode = order.ChannelForecastCode,
+ ChannelName = !string.IsNullOrWhiteSpace(order.ChannelCode)
+ && channelMap.TryGetValue(order.ChannelCode.Trim(), out var channel)
+ ? channel.ChannelName
+ : null,
+ ConsigneeCountry = order.ConsigneeCountry,
+ ConsigneeCountryName = ResolveConsigneeCountryName(order, countryMap),
+ CargoAttribute = order.CargoAttribute,
+ WarehouseId = order.WarehouseId,
+ Warehouse = order.WarehouseId.HasValue && warehouseMap.TryGetValue(order.WarehouseId.Value, out var warehouse)
+ ? warehouse.WarehouseName
+ : null,
+ CustomerId = order.CustomerId,
+ CustomerName = customerName,
+ CustomerCode = customerCode,
+ OrderSource = string.IsNullOrWhiteSpace(order.OrderSource)
+ ? OrderSourceCodes.Manual
+ : order.OrderSource,
+ PickupTime = order.PickupTime,
+ CustomerSenderAddressId = order.CustomerSenderAddressId,
+ InboundMethod = order.InboundMethod,
+ FulfillmentType = string.IsNullOrWhiteSpace(order.FulfillmentType)
+ ? FulfillmentTypeCodes.Direct
+ : order.FulfillmentType,
+ FulfillmentSource = order.FulfillmentSource,
+ StockBindStatus = bindSummary?.BindStatus ?? "none",
+ StockBindLineCount = bindSummary?.LineCount ?? 0,
+ StockBindQtyTotal = bindSummary?.QtyTotal ?? 0,
+ DomesticTrackingNos = bindSummary?.TrackingNosText,
+ FulfillProgress = FulfillmentProgressCodes.Resolve(
+ FulfillmentTypeCodes.IsWarehouse(order.FulfillmentType),
+ bindSummary?.BindStatus ?? "none",
+ bindSummary?.LineCount ?? 0,
+ bindSummary?.HasInTransitParcel ?? false),
+ PrimaryBinCode = bindSummary?.PrimaryBinCode,
+ PrimarySkuCodes = bindSummary?.PrimarySkuCodes,
+ PickupOrderId = order.PickupOrderId,
+ PickupNo = order.PickupOrderId is > 0
+ && pickupNoMap.TryGetValue(order.PickupOrderId.Value, out var pickupNo)
+ ? pickupNo
+ : null,
+ CarrierWaybillNo = order.PickupOrderId is > 0
+ && carrierWaybillNoMap.TryGetValue(order.PickupOrderId.Value, out var carrierWaybillNo)
+ ? carrierWaybillNo
+ : null,
+ RequestId = order.RequestId,
+ ExportCustomsMode = order.ExportCustomsMode,
+ ExportQualificationId = order.ExportQualificationId,
+ PredeclareFailReason = string.IsNullOrWhiteSpace(order.PredeclareFailReason)
+ ? null
+ : order.PredeclareFailReason.Trim(),
+ AllocationPendingReason = string.IsNullOrWhiteSpace(order.AllocationPendingReason)
+ ? null
+ : order.AllocationPendingReason.Trim(),
+ AllocationScheme = string.IsNullOrWhiteSpace(order.AllocationScheme)
+ ? null
+ : order.AllocationScheme.Trim(),
+ // 分页已 IgnoreColumns(AllocationTraceJson);与写入路径一并落库的 TraceTime 作为布尔依据
+ HasAllocationTrace = order.AllocationTraceTime.HasValue
+ || !string.IsNullOrWhiteSpace(order.AllocationTraceJson),
+ DeclaredCurrency = order.DeclaredCurrency,
+ DeclaredSum = order.DeclaredSum,
+ Quantity = order.Quantity,
+ IsCod = order.IsCod > 0,
+ CodCurrency = order.CodCurrency,
+ CodSum = order.CodSum,
+ OrderStatus = OmsDocHelper.GetOrderStatusName(order.OrderStatus),
+ Status = order.OrderStatus,
+ HandleStatus = OrderHandleStatusCodes.IsProcessed(order.HandleStatus)
+ ? OrderHandleStatusCodes.Processed
+ : OrderHandleStatusCodes.Pending,
+ HandleStatusName = OrderHandleStatusCodes.GetDisplayName(order.HandleStatus),
+ IssueType = order.IssueType,
+ IssueTypeName = IssueTypeCodes.GetDisplayName(order.IssueType),
+ IssueTitle = order.IssueTitle,
+ IssueContent = order.IssueContent,
+ IssueTemplateId = order.IssueTemplateId,
+ IssueRemark = order.IssueRemark,
+ IssueTime = order.IssueTime,
+ ClaimType = order.ClaimType,
+ ClaimTypeName = ClaimTypeCodes.GetDisplayName(order.ClaimType),
+ ClaimAmount = order.ClaimAmount,
+ ClaimCurrency = order.ClaimCurrency,
+ ClaimReason = order.ClaimReason,
+ ClaimTime = order.ClaimTime,
+ PreviousOrderStatus = order.PreviousOrderStatus,
+ ReceiveTime = order.ReceiveTime,
+ ShipTime = order.ShipTime,
+ PayTime = order.PayTime,
+ IsPaid = order.IsPaid > 0,
+ SignTime = order.SignTime,
+ IsIntercept = order.IsIntercept > 0,
+ IsRemote = order.IsRemote > 0,
+ AdditionalServices = additionalServices,
+ HasInsurance = hasInsurance,
+ ForecastReceivableAmount = order.ForecastReceivableAmount,
+ ForecastPayableAmount = order.ForecastPayableAmount,
+ ReceivableSubtotal = order.ReceivableSubtotal,
+ LevelDiscountAmount = order.LevelDiscountAmount,
+ CouponDiscountAmount = order.CouponDiscountAmount,
+ ActualPayAmount = order.PayableAmount,
+ CouponInstanceId = order.CouponInstanceId,
+ SalesProductRulesVersion = string.IsNullOrWhiteSpace(order.SalesProductRulesVersion)
+ ? null
+ : order.SalesProductRulesVersion.Trim(),
+ SalesProductPriceVersion = string.IsNullOrWhiteSpace(order.SalesProductPriceVersion)
+ ? null
+ : order.SalesProductPriceVersion.Trim(),
+ CreateTime = order.CreateTime,
+ CreateBy = OmsRbacUserDisplayNameResolver.FormatUserId(order.CreateUserId, auditUserNameMap),
+ UpdateTime = OmsDocHelper.NullIfDefault(order.UpdateTime),
+ UpdateBy = OmsRbacUserDisplayNameResolver.FormatUserId(order.UpdateUserId, auditUserNameMap),
+ Sender = MapSender(sender, countryIdCodeMap),
+ Receiver = MapReceiver(recipient, countryIdCodeMap),
+ Items = [],
+ WeightList = [],
+ FeeList = [],
+ ChargeableWeight = chargeableWeight,
+ ReceivableAmount = receivableAmount,
+ };
+ }).ToList();
+
+ if (OmsSessionHelper.IsCustomerLogin(CurrentUser))
+ {
+ RedactChannelFieldsForCustomer(outputs);
+ }
+
+ return outputs;
+ }
+
private sealed class StockBindListSummary
{
public string BindStatus { get; init; } = "none";
diff --git a/src/Oms.Application/Services/Orders/OrderService.cs b/src/Oms.Application/Services/Orders/OrderService.cs
index d4e4090..068e567 100644
--- a/src/Oms.Application/Services/Orders/OrderService.cs
+++ b/src/Oms.Application/Services/Orders/OrderService.cs
@@ -70,7 +70,11 @@ public partial class OrderService : ApplicationService
pageIndex,
pageSize,
filter);
- return new OmsPageResult { Items = await MapOrderOutputsAsync(orders), Total = total };
+ return new OmsPageResult
+ {
+ Items = await MapOrderListOutputsAsync(orders),
+ Total = total,
+ };
}
///
diff --git a/src/Oms.Domain/OrdersAggregate/Repositories/IOrderRepository.cs b/src/Oms.Domain/OrdersAggregate/Repositories/IOrderRepository.cs
index c5d8ab0..1e1397d 100644
--- a/src/Oms.Domain/OrdersAggregate/Repositories/IOrderRepository.cs
+++ b/src/Oms.Domain/OrdersAggregate/Repositories/IOrderRepository.cs
@@ -70,6 +70,13 @@ public interface IOrderRepository : IRepository
Task> GetFeesByOrderIdsAsync(IEnumerable orderIds, CancellationToken cancellationToken = default);
+ ///
+ /// 列表页:按订单聚合应收金额合计(不加载费用明细行)。
+ ///
+ Task> GetReceivableSumByOrderIdsAsync(
+ IEnumerable orderIds,
+ CancellationToken cancellationToken = default);
+
Task<(List Items, int Total)> GetReceivablePayablePagedListAsync(
int pageIndex,
int pageSize,
diff --git a/src/Oms.SqlSugarCore/Orders/OrderRepository.cs b/src/Oms.SqlSugarCore/Orders/OrderRepository.cs
index ad4e985..9819fbd 100644
--- a/src/Oms.SqlSugarCore/Orders/OrderRepository.cs
+++ b/src/Oms.SqlSugarCore/Orders/OrderRepository.cs
@@ -23,7 +23,9 @@ public class OrderRepository : OmsSqlSugarRepository, IOrderRepository, I
var query = ApplyListFilter(db.Queryable(), filter);
var total = await query.CountAsync(cancellationToken);
query = ApplyListSort(query, filter);
+ // 列表仅需 HasAllocationTrace 布尔语义,勿拉取大字段 AllocationTraceJson
var items = await query
+ .IgnoreColumns(x => x.AllocationTraceJson)
.ToPageListAsync(pageIndex, pageSize, cancellationToken);
return (items, total);
}
@@ -1104,6 +1106,34 @@ public class OrderRepository : OmsSqlSugarRepository, IOrderRepository, I
.ToListAsync(cancellationToken);
}
+ ///
+ /// 列表页:按订单聚合应收金额合计(不加载费用明细行)。
+ ///
+ public async Task> GetReceivableSumByOrderIdsAsync(
+ IEnumerable orderIds,
+ CancellationToken cancellationToken = default)
+ {
+ var ids = orderIds.Distinct().ToArray();
+ if (ids.Length == 0)
+ {
+ return new Dictionary();
+ }
+
+ var db = await GetDbContextAsync(cancellationToken);
+ var rows = await db.Queryable()
+ .Where(x => ids.Contains(x.OrderId)
+ && !x.IsDeleted
+ && x.Type == OrderFeeTypeCodes.Receivable)
+ .GroupBy(x => x.OrderId)
+ .Select(x => new
+ {
+ OrderId = x.OrderId,
+ Amount = SqlFunc.AggregateSum(x.Amount),
+ })
+ .ToListAsync(cancellationToken);
+ return rows.ToDictionary(x => x.OrderId, x => x.Amount);
+ }
+
public async Task<(List Items, int Total)> GetReceivablePayablePagedListAsync(
int pageIndex,
int pageSize,