diff --git a/src/Oms.Application/Dtos/Orders/OrderPrintOutput.cs b/src/Oms.Application/Dtos/Orders/OrderPrintOutput.cs
index 95f2988..b0764fb 100644
--- a/src/Oms.Application/Dtos/Orders/OrderPrintOutput.cs
+++ b/src/Oms.Application/Dtos/Orders/OrderPrintOutput.cs
@@ -1,3 +1,5 @@
+using System.ComponentModel.DataAnnotations;
+
namespace Oms.Application.Dtos.Orders;
///
@@ -20,3 +22,37 @@ public class OrderPrintOutput
///
public string? LabelGenerateError { get; set; }
}
+
+///
+/// 批量获取面单打印地址入参。
+///
+public class OrderPrintBatchInput
+{
+ [Required]
+ [MinLength(1)]
+ public List OrderIds { get; set; } = [];
+}
+
+///
+/// 批量获取面单打印地址单条结果。
+///
+public class OrderPrintBatchItemOutput
+{
+ public int OrderId { get; set; }
+
+ public bool Success { get; set; }
+
+ public string PrintUrl { get; set; } = string.Empty;
+
+ public string? LabelGenerateStatus { get; set; }
+
+ public string? Error { get; set; }
+}
+
+///
+/// 批量获取面单打印地址出参。
+///
+public class OrderPrintBatchOutput
+{
+ public List Items { get; set; } = [];
+}
diff --git a/src/Oms.Application/Services/Orders/OrderFileManager.cs b/src/Oms.Application/Services/Orders/OrderFileManager.cs
index b4ae163..3515518 100644
--- a/src/Oms.Application/Services/Orders/OrderFileManager.cs
+++ b/src/Oms.Application/Services/Orders/OrderFileManager.cs
@@ -34,6 +34,37 @@ public sealed class OrderFileManager(
return string.IsNullOrWhiteSpace(file?.FileUrl) ? null : fileStorage.GetPublicUrl(file.FileUrl.Trim());
}
+ ///
+ /// 批量取系统面单公开地址(已存在才返回)。
+ ///
+ public async Task> GetSystemLabelUrlsAsync(
+ IReadOnlyCollection orderIds,
+ CancellationToken cancellationToken = default)
+ {
+ var result = new Dictionary();
+ var ids = orderIds?.Where(x => x > 0).Distinct().ToList() ?? [];
+ if (ids.Count == 0)
+ {
+ return result;
+ }
+
+ var files = await orderFileRepository.GetByOrderIdsAndTypeAsync(
+ ids,
+ OrderFileTypeCodes.SystemLabel,
+ cancellationToken);
+ foreach (var file in files)
+ {
+ if (string.IsNullOrWhiteSpace(file.FileUrl) || result.ContainsKey(file.OrderId))
+ {
+ continue;
+ }
+
+ result[file.OrderId] = fileStorage.GetPublicUrl(file.FileUrl.Trim());
+ }
+
+ return result;
+ }
+
public async Task GetGroupedAsync(
int orderId,
bool customerView,
diff --git a/src/Oms.Application/Services/Orders/OrderService.cs b/src/Oms.Application/Services/Orders/OrderService.cs
index 91bff8f..cce98db 100644
--- a/src/Oms.Application/Services/Orders/OrderService.cs
+++ b/src/Oms.Application/Services/Orders/OrderService.cs
@@ -317,6 +317,89 @@ public partial class OrderService : ApplicationService
};
}
+ ///
+ /// 批量获取打印面单地址(同步返回,避免前端逐单请求)。
+ ///
+ /// 入参:订单 ID 列表。
+ /// 各订单打印结果。
+ [HttpPost("/order/print/batch")]
+ public async Task PrintBatchAsync([FromBody] OrderPrintBatchInput input)
+ {
+ var orderIds = (input.OrderIds ?? []).Where(x => x > 0).Distinct().ToList();
+ if (orderIds.Count == 0)
+ {
+ throw new BusinessException("请选择要打印的订单");
+ }
+
+ const int maxBatchSize = 100;
+ if (orderIds.Count > maxBatchSize)
+ {
+ throw new BusinessException($"单次最多获取 {maxBatchSize} 张面单");
+ }
+
+ var output = new OrderPrintBatchOutput
+ {
+ Items = new List(orderIds.Count)
+ };
+
+ // 已有系统面单一律批量查出,避免 N 次附件查询
+ var existingUrls = await OrderFileManager.GetSystemLabelUrlsAsync(orderIds);
+ var pendingIds = new List();
+ foreach (var orderId in orderIds)
+ {
+ if (existingUrls.TryGetValue(orderId, out var url) && !string.IsNullOrWhiteSpace(url))
+ {
+ output.Items.Add(new OrderPrintBatchItemOutput
+ {
+ OrderId = orderId,
+ Success = true,
+ PrintUrl = url,
+ LabelGenerateStatus = LabelGenerateStatusCodes.Ready
+ });
+ }
+ else
+ {
+ pendingIds.Add(orderId);
+ }
+ }
+
+ // 未就绪的走单票打印逻辑(含即时补生成),单条失败不影响其它
+ foreach (var orderId in pendingIds)
+ {
+ try
+ {
+ var print = await PrintAsync(new OrderActionInput { Id = orderId });
+ var ok = !string.IsNullOrWhiteSpace(print.PrintUrl);
+ output.Items.Add(new OrderPrintBatchItemOutput
+ {
+ OrderId = orderId,
+ Success = ok,
+ PrintUrl = print.PrintUrl ?? string.Empty,
+ LabelGenerateStatus = print.LabelGenerateStatus,
+ Error = ok ? null : (print.LabelGenerateError ?? "未获取到打印文件")
+ });
+ }
+ catch (Exception ex)
+ {
+ output.Items.Add(new OrderPrintBatchItemOutput
+ {
+ OrderId = orderId,
+ Success = false,
+ Error = ex is BusinessException be ? be.Message : ex.Message
+ });
+ }
+ }
+
+ // 保持与入参顺序一致
+ var byId = output.Items.ToDictionary(x => x.OrderId);
+ output.Items = orderIds
+ .Select(id => byId.TryGetValue(id, out var item)
+ ? item
+ : new OrderPrintBatchItemOutput { OrderId = id, Success = false, Error = "未处理" })
+ .ToList();
+ return output;
+ }
+
///
/// 导出订单。
///
diff --git a/src/Oms.Domain/OrdersAggregate/Repositories/IOrderFileRepository.cs b/src/Oms.Domain/OrdersAggregate/Repositories/IOrderFileRepository.cs
index 6fe8fe5..63e8aa6 100644
--- a/src/Oms.Domain/OrdersAggregate/Repositories/IOrderFileRepository.cs
+++ b/src/Oms.Domain/OrdersAggregate/Repositories/IOrderFileRepository.cs
@@ -17,6 +17,14 @@ public interface IOrderFileRepository : IRepository
string fileType,
CancellationToken cancellationToken = default);
+ ///
+ /// 按订单 ID 集合 + 附件类型批量查询。
+ ///
+ Task> GetByOrderIdsAndTypeAsync(
+ IReadOnlyCollection orderIds,
+ string fileType,
+ CancellationToken cancellationToken = default);
+
Task SoftDeleteByOrderAndTypeAsync(
int orderId,
string fileType,
diff --git a/src/Oms.SqlSugarCore/Orders/OrderFileRepository.cs b/src/Oms.SqlSugarCore/Orders/OrderFileRepository.cs
index 1f44077..50f42ed 100644
--- a/src/Oms.SqlSugarCore/Orders/OrderFileRepository.cs
+++ b/src/Oms.SqlSugarCore/Orders/OrderFileRepository.cs
@@ -31,6 +31,22 @@ public class OrderFileRepository
x => x.OrderId == orderId && x.FileType == fileType,
cancellationToken: cancellationToken);
+ public Task> GetByOrderIdsAndTypeAsync(
+ IReadOnlyCollection orderIds,
+ string fileType,
+ CancellationToken cancellationToken = default)
+ {
+ var ids = orderIds?.Where(x => x > 0).Distinct().ToList() ?? [];
+ if (ids.Count == 0)
+ {
+ return Task.FromResult(new List());
+ }
+
+ return GetListAsync(
+ x => ids.Contains(x.OrderId) && x.FileType == fileType,
+ cancellationToken: cancellationToken);
+ }
+
public async Task SoftDeleteByOrderAndTypeAsync(
int orderId,
string fileType,