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,