647556386
2025-11-30 04bd9fa1060d711e0e90492c103699f50b49ba17
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs
@@ -6,11 +6,13 @@
using System.Text;
using System.Threading.Tasks;
using WIDESEA_BasicService;
using WIDESEA_Common.CommonEnum;
using WIDESEA_Common.OrderEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_DTO.Outbound;
using WIDESEA_IAllocateService;
using WIDESEA_IBasicService;
using WIDESEA_IOutboundService;
@@ -18,6 +20,7 @@
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Basic;
using WIDESEA_Model.Models.Outbound;
using static WIDESEA_OutboundService.OutboundBatchPickingService;
namespace WIDESEA_OutboundService
{
@@ -61,7 +64,7 @@
        public OutboundBatchPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService,
            IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService,
            IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService,
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService) : base(BaseDal)
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _stockInfoService = stockInfoService;
@@ -78,9 +81,325 @@
            _invokeMESService = invokeMESService;
            _dailySequenceService = dailySequenceService;
            _allocateService = allocateService;
            _outboundBatchRepository = outboundBatchRepository;
        }
        // <summary>
        /// èŽ·å–æ‰˜ç›˜çš„é”å®šä¿¡æ¯
        /// </summary>
        public async Task<List<PalletLockInfoDto>> GetPalletLockInfos(string orderNo, string palletCode)
        {
            var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                  .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                  .Select(x => new
                  {
                      x.Id,
                      x.OrderNo,
                      x.BatchNo,
                      x.MaterielCode,
                      x.CurrentBarcode,
                      x.AssignQuantity,
                      x.PickedQty,
                      x.Status,
                      x.LocationCode,
                      x.PalletCode
                  }).ToListAsync();
            var lockInfoDtos = lockInfos.Select(x => new PalletLockInfoDto
            {
                Id = x.Id,
                OrderNo = x.OrderNo,
                BatchNo = x.BatchNo,
                MaterielCode = x.MaterielCode,
                CurrentBarcode = x.CurrentBarcode,
                AssignQuantity = x.AssignQuantity,
                PickedQty = x.PickedQty,
                Status = x.Status,
                LocationCode = x.LocationCode,
                PalletCode = x.PalletCode,
                CanSplit = (x.Status == (int)OutLockStockStatusEnum.出库中 && x.AssignQuantity - x.PickedQty > 0),
                CanPick = (x.Status == (int)OutLockStockStatusEnum.出库中 && x.PickedQty < x.AssignQuantity)
            }).ToList();
            return lockInfoDtos;
        }
        #region æŸ¥è¯¢æ–¹æ³•
        /// <summary>
        /// èŽ·å–æ‰˜ç›˜çš„å·²æ‹£é€‰åˆ—è¡¨
        /// </summary>
        public async Task<List<PalletPickedInfoDto>> GetPalletPickedList(string orderNo, string palletCode)
        {
            var pickedList = await Db.Queryable<Dt_PickingRecord>()
                .Where(x => x.OrderNo == orderNo &&
                           x.PalletCode == palletCode &&
                           !x.IsCancelled)
                .Select(x => new PalletPickedInfoDto
                {
                    Id = x.Id,
                    OrderNo = x.OrderNo,
                    OrderDetailId = x.OrderDetailId,
                    PalletCode = x.PalletCode,
                    Barcode = x.Barcode,
                    MaterielCode = x.MaterielCode,
                    PickedQty = x.PickQuantity,
                    PickTime = x.PickTime,
                    Operator = x.Operator,
                    LocationCode = x.LocationCode
                })
                .ToListAsync();
            return pickedList;
        }
        /// <summary>
        /// èŽ·å–æ‰˜ç›˜çŠ¶æ€
        /// </summary>
        public async Task<PalletStatusDto> GetPalletStatus(string orderNo, string palletCode)
        {
            // èŽ·å–æ‰˜ç›˜çš„é”å®šä¿¡æ¯
            var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                .ToListAsync();
            if (!lockInfos.Any())
            {
                return new PalletStatusDto
                {
                    OrderNo = orderNo,
                    PalletCode = palletCode,
                    Status = (int)PalletStatusEnum.无任务,
                    StatusText = "无任务",
                    TotalItems = 0,
                    CompletedItems = 0,
                    PendingItems = 0
                };
            }
            var totalItems = lockInfos.Count;
            var completedItems = lockInfos.Count(x => x.Status == (int)OutLockStockStatusEnum.拣选完成);
            var pendingItems = lockInfos.Count(x => x.Status == (int)OutLockStockStatusEnum.出库中);
            var status = PalletStatusEnum.拣选中;
            if (pendingItems == 0 && completedItems > 0)
            {
                status = PalletStatusEnum.已完成;
            }
            else if (pendingItems > 0 && completedItems == 0)
            {
                status = PalletStatusEnum.未开始;
            }
            else if (pendingItems > 0 && completedItems > 0)
            {
                status = PalletStatusEnum.拣选中;
            }
            return new PalletStatusDto
            {
                OrderNo = orderNo,
                PalletCode = palletCode,
                Status = (int)status,
                StatusText = GetPalletStatusText(status),
                TotalItems = totalItems,
                CompletedItems = completedItems,
                PendingItems = pendingItems
            };
        }
        /// <summary>
        /// èŽ·å–æ‹†åŒ…ä¿¡æ¯
        /// </summary>
        public async Task<SplitPackageInfoDto> GetSplitPackageInfo(string orderNo, string palletCode, string barcode)
        {
            // æŸ¥æ‰¾é”å®šä¿¡æ¯
            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderNo == orderNo &&
                           x.PalletCode == palletCode &&
                           x.CurrentBarcode == barcode
                        //&& x.Status == (int)OutLockStockStatusEnum.出库中
                          )
                .FirstAsync();
            if (lockInfo == null)
                throw new Exception("未找到有效的锁定信息");
            // è®¡ç®—剩余可拆数量
            var remainQuantity = lockInfo.AssignQuantity - lockInfo.PickedQty;
            return new SplitPackageInfoDto
            {
                OrderNo = orderNo,
                PalletCode = palletCode,
                Barcode = barcode,
                MaterielCode = lockInfo.MaterielCode,
                RemainQuantity = remainQuantity,
                AssignQuantity = lockInfo.AssignQuantity,
                PickedQty = lockInfo.PickedQty
            };
        }
        #endregion
        #region å–走空箱逻辑
        /// <summary>
        /// å–走空箱 - æ¸…理已完成拣选的托盘数据
        /// </summary>
        public async Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                // éªŒè¯æ‰˜ç›˜æ˜¯å¦å¯ä»¥å–走(必须全部完成拣选)
                var validationResult = await ValidateEmptyPalletRemoval(orderNo, palletCode);
                if (!validationResult.IsValid)
                    return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
                var completedLocks = validationResult.Data;
                // æ¸…理锁定记录(标记为已完成)
                await CleanupCompletedLocks(completedLocks);
                // æ›´æ–°ç›¸å…³è®¢å•状态
                await UpdateOrderStatusAfterPalletRemoval(orderNo);
                // è®°å½•操作历史
                await RecordEmptyPalletRemoval(orderNo, palletCode, completedLocks);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK("取走空箱成功");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"取走空箱失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"取走空箱失败:{ex.Message}");
            }
        }
        /// <summary>
        /// éªŒè¯ç©ºç®±å–走条件
        /// </summary>
        private async Task<ValidationResult<List<Dt_OutStockLockInfo>>> ValidateEmptyPalletRemoval(string orderNo, string palletCode)
        {
            // èŽ·å–æ‰˜ç›˜çš„æ‰€æœ‰é”å®šè®°å½•
            var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                .ToListAsync();
            if (!lockInfos.Any())
                return ValidationResult<List<Dt_OutStockLockInfo>>.Error("该托盘没有锁定记录");
            // æ£€æŸ¥æ˜¯å¦æœ‰æœªå®Œæˆçš„锁定记录
            var unfinishedLocks = lockInfos.Where(x =>
                x.Status == (int)OutLockStockStatusEnum.出库中 ||
                x.Status == (int)OutLockStockStatusEnum.回库中).ToList();
            if (unfinishedLocks.Any())
            {
                var unfinishedCount = unfinishedLocks.Count;
                var unfinishedQty = unfinishedLocks.Sum(x => x.AssignQuantity - x.PickedQty);
                return ValidationResult<List<Dt_OutStockLockInfo>>.Error(
                    $"托盘还有{unfinishedCount}条未完成记录,剩余数量{unfinishedQty},不能取走空箱");
            }
            // èŽ·å–å·²å®Œæˆçš„é”å®šè®°å½•
            var completedLocks = lockInfos.Where(x =>
                x.Status == (int)OutLockStockStatusEnum.拣选完成).ToList();
            if (!completedLocks.Any())
                return ValidationResult<List<Dt_OutStockLockInfo>>.Error("该托盘没有已完成拣选的记录");
            return ValidationResult<List<Dt_OutStockLockInfo>>.Success(completedLocks);
        }
        /// <summary>
        /// æ¸…理已完成的锁定记录
        /// </summary>
        private async Task CleanupCompletedLocks(List<Dt_OutStockLockInfo> completedLocks)
        {
            foreach (var lockInfo in completedLocks)
            {
                // æ ‡è®°é”å®šè®°å½•为已取走
                lockInfo.Status = (int)OutLockStockStatusEnum.已取走;
                lockInfo.Operator = App.User.UserName;
                await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                // æ¸…理对应的库存记录状态
                await CleanupStockInfo(lockInfo);
            }
        }
        /// <summary>
        /// æ¸…理库存信息
        /// </summary>
        private async Task CleanupStockInfo(Dt_OutStockLockInfo lockInfo)
        {
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
            if (stockDetail != null && stockDetail.Status == (int)StockStatusEmun.出库完成)
            {
                stockDetail.Status = (int)StockStatusEmun.已清理;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            }
        }
        /// <summary>
        /// æ›´æ–°è®¢å•状态
        /// </summary>
        private async Task UpdateOrderStatusAfterPalletRemoval(string orderNo)
        {
            // æ£€æŸ¥è®¢å•是否所有托盘都已完成
            var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderNo == orderNo)
                .ToListAsync();
            var unfinishedPallets = allLocks
                .GroupBy(x => x.PalletCode)
                .Where(g => g.Any(x => x.Status == (int)OutLockStockStatusEnum.出库中 ||
                                      x.Status == (int)OutLockStockStatusEnum.回库中))
                .ToList();
            // å¦‚果没有未完成的托盘,更新订单状态为出库完成
            if (!unfinishedPallets.Any())
            {
                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => new Dt_OutboundOrder
                    {
                        OrderStatus = (int)OutOrderStatusEnum.出库完成,
                    })
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
            }
        }
        /// <summary>
        /// è®°å½•空箱取走历史
        /// </summary>
        private async Task RecordEmptyPalletRemoval(string orderNo, string palletCode, List<Dt_OutStockLockInfo> completedLocks)
        {
            var removalRecord = new Dt_EmptyPalletRemoval
            {
                OrderNo = orderNo,
                PalletCode = palletCode,
                RemovalTime = DateTime.Now,
                Operator = App.User.UserName,
                CompletedItemsCount = completedLocks.Count,
                TotalPickedQuantity = completedLocks.Sum(x => x.PickedQty)
            };
            await Db.Insertable(removalRecord).ExecuteCommandAsync();
        }
        #endregion
        #region åˆ†æ‰¹åˆ†æ‹£
        /// <summary>
@@ -149,10 +468,10 @@
                    return WebResponseContent.Instance.Error("未找到分拣记录");
                // æ¢å¤é”å®šä¿¡æ¯å’Œåº“å­˜
                await RevertPickingData(pickingRecord);
                var revertResult = await RevertPickingData(pickingRecord);
                //更新批次和订单数据
                await RevertBatchAndOrderData(pickingRecord);
                // æ›´æ–°æ‰¹æ¬¡å’Œè®¢å•数据
                await RevertBatchAndOrderData(pickingRecord, revertResult);
                // æ ‡è®°åˆ†æ‹£è®°å½•为已取消
                pickingRecord.IsCancelled = true;
@@ -171,7 +490,6 @@
                return WebResponseContent.Instance.Error($"取消分拣失败:{ex.Message}");
            }
        }
        #endregion
        #region æ‰‹åŠ¨æ‹†åŒ…
@@ -214,10 +532,12 @@
        #endregion
        #region å–消拆包
        #region å–消拆包
        /// <summary>
        /// å–消拆包
        /// å–消拆包
        /// </summary>
        public async Task<WebResponseContent> CancelSplitPackage(string orderNo, string palletCode, string newBarcode)
        {
@@ -225,15 +545,29 @@
            {
                _unitOfWorkManage.BeginTran();
                // æŸ¥æ‰¾æ‹†åŒ…记录并验证
                // 1. æŸ¥æ‰¾æ‹†åŒ…记录并验证
                var validationResult = await ValidateCancelSplitRequest(orderNo, palletCode, newBarcode);
                if (!validationResult.IsValid)
                    return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
                var (splitRecord, newLockInfo, newStockDetail) = validationResult.Data;
                // æ‰§è¡Œå–消拆包逻辑
                await ExecuteCancelSplitLogic(splitRecord, newLockInfo, newStockDetail);
                // 2. æŸ¥æ‰¾åŽŸå§‹é”å®šä¿¡æ¯
                var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId);
                // 3. æ£€æŸ¥è¯¥æ¡ç æ˜¯å¦è¢«å†æ¬¡æ‹†åŒ…
                var childSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(x => x.OriginalBarcode == newBarcode && !x.IsReverted)
                    .ToListAsync();
                if (childSplitRecords.Any())
                {
                    return WebResponseContent.Instance.Error("该条码已被再次拆包,请先取消后续的拆包操作");
                }
                // 4. æ‰§è¡Œå–消拆包逻辑
                await ExecuteCancelSplitLogic(splitRecord, originalLockInfo, newLockInfo, newStockDetail);
                _unitOfWorkManage.CommitTran();
@@ -242,12 +576,331 @@
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"取消拆包失败 - OrderNo: {orderNo}, Barcode: {newBarcode}, Error: {ex.Message}");
                _logger.LogError($"取消拆包失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {newBarcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"取消拆包失败:{ex.Message}");
            }
        }
        /// <summary>
        /// æ‰§è¡Œå–消拆包逻辑
        /// </summary>
        private async Task ExecuteCancelSplitLogic(Dt_SplitPackageRecord splitRecord,
            Dt_OutStockLockInfo originalLockInfo, Dt_OutStockLockInfo newLockInfo,
            Dt_StockInfoDetail newStockDetail)
        {
            // 1. æ¢å¤åŽŸé”å®šä¿¡æ¯
            // æ³¨æ„ï¼šè¿™é‡Œéœ€è¦ç´¯åŠ ï¼Œè€Œä¸æ˜¯ç®€å•çš„èµ‹å€¼ï¼Œå› ä¸ºå¯èƒ½æœ‰å¤šæ¬¡æ‹†åŒ…
            originalLockInfo.AssignQuantity += splitRecord.SplitQty;
            originalLockInfo.OrderQuantity += splitRecord.SplitQty;
            // å¦‚果原锁定信息的状态是拣选完成,需要重新设置为出库中
            if (originalLockInfo.Status == (int)OutLockStockStatusEnum.拣选完成)
            {
                originalLockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            }
            await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
            // æ¢å¤åŽŸåº“å­˜æ˜Žç»†
            var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId);
            originalStock.StockQuantity += splitRecord.SplitQty;
            // å¦‚果原库存状态是出库完成,需要重新设置为出库锁定
            if (originalStock.Status == (int)StockStatusEmun.出库完成)
            {
                originalStock.Status = (int)StockStatusEmun.出库锁定;
            }
            await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync();
            // åˆ é™¤æ–°é”å®šä¿¡æ¯
            await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                .Where(x => x.Id == newLockInfo.Id)
                .ExecuteCommandAsync();
            // åˆ é™¤æ–°åº“存明细
            await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == newLockInfo.CurrentBarcode)
                .ExecuteCommandAsync();
            //标记拆包记录为已撤销
            splitRecord.IsReverted = true;
            splitRecord.RevertTime = DateTime.Now;
            splitRecord.RevertOperator = App.User.UserName;
            await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync();
            // æ£€æŸ¥å¹¶æ›´æ–°æ‰¹æ¬¡å’Œè®¢å•状态
            await CheckAndUpdateBatchStatus(originalLockInfo.BatchNo);
            await CheckAndUpdateOrderStatus(originalLockInfo.OrderNo);
        }
        /// <summary>
        /// éªŒè¯å–消拆包请求
        /// </summary>
        private async Task<ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateCancelSplitRequest(
            string orderNo, string palletCode, string newBarcode)
        {
            // æŸ¥æ‰¾æ‹†åŒ…记录
            var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(x => x.NewBarcode == newBarcode &&
                           x.OrderNo == orderNo &&
                           !x.IsReverted)
                .FirstAsync();
            if (splitRecord == null)
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到拆包记录");
            // æŸ¥æ‰¾æ–°é”å®šä¿¡æ¯
            var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.CurrentBarcode == newBarcode &&
                           x.PalletCode == palletCode &&
                           x.OrderNo == orderNo)
                .FirstAsync();
            if (newLockInfo == null)
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到新锁定信息");
            // æ£€æŸ¥æ–°æ¡ç æ˜¯å¦å·²è¢«åˆ†æ‹£
            var pickingRecord = await Db.Queryable<Dt_PickingRecord>()
                .Where(x => x.Barcode == newBarcode && !x.IsCancelled)
                .FirstAsync();
            if (pickingRecord != null)
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("该条码已被分拣,无法取消拆包");
            // æ£€æŸ¥æ–°æ¡ç æ˜¯å¦è¢«å†æ¬¡æ‹†åŒ…
            var childSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(x => x.OriginalBarcode == newBarcode && !x.IsReverted)
                .ToListAsync();
            if (childSplitRecords.Any())
            {
                var childBarcodes = string.Join(", ", childSplitRecords.Select(x => x.NewBarcode));
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error(
                    $"该条码已被再次拆包,生成的新条码:{childBarcodes},请先取消后续拆包");
            }
            var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == newBarcode);
            return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((splitRecord, newLockInfo, newStockDetail));
        }
        #endregion
        #region æ‰¹é‡å–消拆包链
        /// <summary>
        /// æ‰¹é‡å–消拆包链 - å–消某个条码及其所有后续拆包
        /// </summary>
        public async Task<WebResponseContent> CancelSplitPackageChain(string orderNo, string palletCode, string startBarcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                // 1. æŸ¥æ‰¾æ‰€æœ‰ç›¸å…³çš„æ‹†åŒ…记录(形成拆包链)
                var splitChain = await GetSplitPackageChain(orderNo, startBarcode);
                if (!splitChain.Any())
                    return WebResponseContent.Instance.Error("未找到拆包记录");
                // 2. æŒ‰æ‹†åŒ…顺序倒序取消(从最新的开始取消)
                var reversedChain = splitChain.OrderByDescending(x => x.SplitTime).ToList();
                foreach (var splitRecord in reversedChain)
                {
                    await CancelSingleSplitPackage(splitRecord, palletCode);
                }
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK($"成功取消拆包链,共{reversedChain.Count}次拆包操作");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"取消拆包链失败 - OrderNo: {orderNo}, StartBarcode: {startBarcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"取消拆包链失败:{ex.Message}");
            }
        }
        /// <summary>
        /// èŽ·å–æ‹†åŒ…é“¾ - æŸ¥æ‰¾æŸä¸ªæ¡ç çš„æ‰€æœ‰æ‹†åŒ…记录(包括后续拆包)
        /// </summary>
        public async Task<List<Dt_SplitPackageRecord>> GetSplitPackageChain(string orderNo, string startBarcode)
        {
            var allSplitRecords = new List<Dt_SplitPackageRecord>();
            var visitedBarcodes = new HashSet<string>(); // é˜²æ­¢å¾ªçŽ¯å¼•ç”¨
            // ä½¿ç”¨é˜Ÿåˆ—进行广度优先搜索
            var queue = new Queue<string>();
            queue.Enqueue(startBarcode);
            visitedBarcodes.Add(startBarcode);
            while (queue.Count > 0)
            {
                var currentBarcode = queue.Dequeue();
                // æŸ¥æ‰¾ä»¥å½“前条码为原条码的所有拆包记录
                var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(x => x.OriginalBarcode == currentBarcode &&
                               x.OrderNo == orderNo &&
                               !x.IsReverted)
                    .ToListAsync();
                foreach (var record in splitRecords)
                {
                    // é¿å…é‡å¤å¤„理
                    if (!visitedBarcodes.Contains(record.NewBarcode))
                    {
                        allSplitRecords.Add(record);
                        queue.Enqueue(record.NewBarcode);
                        visitedBarcodes.Add(record.NewBarcode);
                    }
                }
            }
            return allSplitRecords;
        }
        /// <summary>
        /// å–消单个拆包记录
        /// </summary>
        private async Task CancelSingleSplitPackage(Dt_SplitPackageRecord splitRecord, string palletCode)
        {
            // æŸ¥æ‰¾ç›¸å…³æ•°æ®
            var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.CurrentBarcode == splitRecord.NewBarcode &&
                           x.PalletCode == palletCode)
                .FirstAsync();
            var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == splitRecord.NewBarcode);
            var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId);
            // æ‰§è¡Œå–消逻辑
            await ExecuteCancelSplitLogic(splitRecord, originalLockInfo, newLockInfo, newStockDetail);
        }
        #endregion
        #region æ‹†åŒ…信息查询增强
        /// <summary>
        /// èŽ·å–æ‹†åŒ…é“¾ä¿¡æ¯
        /// </summary>
        public async Task<WebResponseContent> GetSplitPackageChainInfo(string orderNo, string barcode)
        {
            try
            {
                var splitChain = await GetSplitPackageChain(orderNo, barcode);
                var chainInfo = new SplitPackageChainInfoDto
                {
                    OriginalBarcode = barcode,
                    TotalSplitTimes = splitChain.Count,
                    SplitChain = splitChain.Select(x => new SplitChainItemDto
                    {
                        SplitTime = x.SplitTime,
                        OriginalBarcode = x.OriginalBarcode,
                        NewBarcode = x.NewBarcode,
                        SplitQuantity = x.SplitQty,
                        Operator = x.Operator,
                        IsReverted = x.IsReverted
                    }).ToList()
                };
                return WebResponseContent.Instance.OK("获取成功", chainInfo);
            }
            catch (Exception ex)
            {
                _logger.LogError($"获取拆包链信息失败 - OrderNo: {orderNo}, Barcode: {barcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error("获取拆包链信息失败");
            }
        }
        /// <summary>
        /// æŸ¥æ‰¾æ ¹æ¡ç 
        /// </summary>
        public async Task<string> FindRootBarcode(string orderNo, string startBarcode)
        {
            var currentBarcode = startBarcode;
            var visited = new HashSet<string>();
            while (!string.IsNullOrEmpty(currentBarcode) && !visited.Contains(currentBarcode))
            {
                visited.Add(currentBarcode);
                // æŸ¥æ‰¾å½“前条码是否是由其他条码拆包而来
                var parentRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(x => x.NewBarcode == currentBarcode &&
                               x.OrderNo == orderNo &&
                               !x.IsReverted)
                    .FirstAsync();
                if (parentRecord == null)
                {
                    // æ²¡æœ‰çˆ¶çº§æ‹†åŒ…记录,说明这是根条码
                    return currentBarcode;
                }
                currentBarcode = parentRecord.OriginalBarcode;
            }
            // å¦‚果出现循环引用,返回起始条码
            return startBarcode;
        }
        #endregion
        #region æ›´æ–°æ‰¹æ¬¡çŠ¶æ€æ£€æŸ¥
        /// <summary>
        /// æ£€æŸ¥å¹¶æ›´æ–°æ‰¹æ¬¡çŠ¶æ€
        /// </summary>
        private async Task CheckAndUpdateBatchStatus(string batchNo)
        {
            var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>()
                .FirstAsync(x => x.BatchNo == batchNo);
            if (batch != null)
            {
                // é‡æ–°è®¡ç®—批次完成数量
                var batchLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.BatchNo == batchNo)
                    .ToListAsync();
                var completedQuantity = batchLocks.Where(x => x.Status == (int)OutLockStockStatusEnum.拣选完成)
                                                 .Sum(x => x.PickedQty);
                batch.CompletedQuantity = completedQuantity;
                // æ›´æ–°æ‰¹æ¬¡çŠ¶æ€
                if (batch.CompletedQuantity >= batch.BatchQuantity)
                {
                    batch.BatchStatus = (int)BatchStatusEnum.已完成;
                }
                else if (batch.CompletedQuantity > 0)
                {
                    batch.BatchStatus = (int)BatchStatusEnum.执行中;
                }
                else
                {
                    batch.BatchStatus = (int)BatchStatusEnum.分配中;
                }
                await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync();
            }
        }
        #endregion
        #region åˆ†æ‰¹å›žåº“
@@ -260,7 +913,7 @@
            {
                _unitOfWorkManage.BeginTran();
                //  æŸ¥æ‰¾æ‰˜ç›˜ä¸Šæœªå®Œæˆçš„锁定记录
                // æŸ¥æ‰¾æ‰˜ç›˜ä¸Šæœªå®Œæˆçš„锁定记录(只处理出库中的记录)
                var unfinishedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == orderNo &&
                               x.PalletCode == palletCode &&
@@ -270,12 +923,12 @@
                if (!unfinishedLocks.Any())
                    return WebResponseContent.Instance.Error("该托盘没有未完成的锁定记录");
                // æŒ‰æ‰¹æ¬¡åˆ†ç»„处理
                var batchGroups = unfinishedLocks.GroupBy(x => x.BatchNo);
                // æŒ‰å‡ºåº“批次分组处理
                var batchGroups = unfinishedLocks.GroupBy(x => x.OutboundBatchNo); // ä½¿ç”¨ OutboundBatchNo
                foreach (var batchGroup in batchGroups)
                {
                    var batchNo = batchGroup.Key;
                    var outboundBatchNo = batchGroup.Key;
                    var batchLocks = batchGroup.ToList();
                    // é‡Šæ”¾åº“存和锁定记录
@@ -285,7 +938,7 @@
                    }
                    // æ›´æ–°æ‰¹æ¬¡çŠ¶æ€
                    await UpdateBatchStatusForReturn(batchNo, batchLocks);
                    await UpdateBatchStatusForReturn(outboundBatchNo, batchLocks);
                    // æ›´æ–°è®¢å•明细的已分配数量
                    await UpdateOrderDetailAfterReturn(batchLocks);
@@ -302,12 +955,11 @@
                return WebResponseContent.Instance.Error($"分批回库失败:{ex.Message}");
            }
        }
        #endregion
        #region éªŒè¯æ–¹æ³•
        private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>> ValidatePickingRequest(
    string orderNo, string palletCode, string barcode)
       string orderNo, string palletCode, string barcode)
        {
            // æŸ¥æ‰¾é”å®šä¿¡æ¯
            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
@@ -336,12 +988,12 @@
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Error(
                    $"库存数量不足,需要:{lockInfo.AssignQuantity},实际:{stockDetail.StockQuantity}");
            // ä½¿ç”¨ OutboundBatchNo æŸ¥æ‰¾æ‰¹æ¬¡
            var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>()
                .FirstAsync(x => x.BatchNo == lockInfo.BatchNo);
                .FirstAsync(x => x.BatchNo == lockInfo.OutboundBatchNo); // ä¿®æ­£ä¸º OutboundBatchNo
            return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Success((lockInfo, orderDetail, stockDetail, batch));
        }
        private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateSplitRequest(
            string orderNo, string palletCode, string originalBarcode, decimal splitQuantity)
        {
@@ -366,42 +1018,7 @@
            return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((lockInfo, stockDetail));
        }
        private async Task<ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateCancelSplitRequest(
            string orderNo, string palletCode, string newBarcode)
        {
            var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(x => x.NewBarcode == newBarcode &&
                           x.OrderNo == orderNo &&
                           !x.IsReverted)
                .FirstAsync();
            if (splitRecord == null)
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到拆包记录");
            var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.CurrentBarcode == newBarcode &&
                           x.PalletCode == palletCode &&
                           x.OrderNo == orderNo)
                .FirstAsync();
            if (newLockInfo == null)
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("未找到新锁定信息");
            // æ£€æŸ¥æ–°æ¡ç æ˜¯å¦å·²è¢«åˆ†æ‹£
            var pickingRecord = await Db.Queryable<Dt_PickingRecord>()
                .Where(x => x.Barcode == newBarcode && !x.IsCancelled)
                .FirstAsync();
            if (pickingRecord != null)
                return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("该条码已被分拣,无法取消拆包");
            var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == newBarcode);
            return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((splitRecord, newLockInfo, newStockDetail));
        }
        #endregion
        #region æ ¸å¿ƒé€»è¾‘方法
@@ -442,7 +1059,17 @@
                .FirstAsync(x => x.Id == pickingRecord.OutStockLockId);
            lockInfo.PickedQty -= pickingRecord.PickQuantity;
            lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            // æ ¹æ®æ‹£é€‰æ•°é‡åˆ¤æ–­çŠ¶æ€
            if (lockInfo.PickedQty <= 0)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            }
            else if (lockInfo.PickedQty < lockInfo.AssignQuantity)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            }
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // æ¢å¤åº“å­˜
@@ -451,7 +1078,13 @@
            stockDetail.StockQuantity += pickingRecord.PickQuantity;
            stockDetail.OutboundQuantity -= pickingRecord.PickQuantity;
            stockDetail.Status = (int)StockStatusEmun.出库锁定;
            // æ¢å¤åº“存状态
            if (stockDetail.StockQuantity > 0)
            {
                stockDetail.Status = (int)StockStatusEmun.出库锁定;
            }
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            return new RevertPickingResult
@@ -460,9 +1093,8 @@
                StockDetail = stockDetail
            };
        }
        private async Task<SplitResultDto> ExecuteSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
            decimal splitQuantity, string palletCode)
     decimal splitQuantity, string palletCode)
        {
            // ç”Ÿæˆæ–°æ¡ç 
            string newBarcode = await GenerateNewBarcode();
@@ -473,13 +1105,17 @@
                StockId = stockDetail.StockId,
                MaterielCode = stockDetail.MaterielCode,
                OrderNo = stockDetail.OrderNo,
                BatchNo = stockDetail.BatchNo,
                BatchNo = stockDetail.BatchNo, // ç‰©æ–™æ‰¹æ¬¡
                StockQuantity = splitQuantity,
                OutboundQuantity = 0,
                Barcode = newBarcode,
                Status = (int)StockStatusEmun.出库锁定,
                SupplyCode = stockDetail.SupplyCode,
                Unit = stockDetail.Unit
                Unit = stockDetail.Unit,
                BarcodeQty = stockDetail.BarcodeQty,
                BarcodeUnit = stockDetail.BarcodeUnit,
                BusinessType = stockDetail.BusinessType,
                InboundOrderRowNo = stockDetail.InboundOrderRowNo,
            };
            await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
@@ -487,23 +1123,36 @@
            stockDetail.StockQuantity -= splitQuantity;
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            // åˆ›å»ºæ–°é”å®šä¿¡æ¯
            // åˆ›å»ºæ–°é”å®šä¿¡æ¯ - ä½¿ç”¨æ­£ç¡®çš„ OutboundBatchNo
            var newLockInfo = new Dt_OutStockLockInfo
            {
                OrderNo = lockInfo.OrderNo,
                OrderDetailId = lockInfo.OrderDetailId,
                OutboundBatchNo = lockInfo.OutboundBatchNo,
                OutboundBatchNo = lockInfo.OutboundBatchNo, // ä½¿ç”¨ OutboundBatchNo
                MaterielCode = lockInfo.MaterielCode,
                MaterielName = lockInfo.MaterielName,
                StockId = lockInfo.StockId,
                OrderQuantity = splitQuantity,
                AssignQuantity = splitQuantity,
                PickedQty = 0,
                LocationCode = lockInfo.LocationCode,
                PalletCode = palletCode,
                PalletCode = lockInfo.PalletCode,
                TaskNum = lockInfo.TaskNum,
                Status = (int)OutLockStockStatusEnum.出库中,
                Unit = lockInfo.Unit,
                SupplyCode = lockInfo.SupplyCode,
                OrderType = lockInfo.OrderType,
                CurrentBarcode = newBarcode,
                IsSplitted = 1,
                ParentLockId = lockInfo.Id,
                Operator = App.User.UserName,
                FactoryArea = lockInfo.FactoryArea,
                lineNo = lockInfo.lineNo,
                WarehouseCode = lockInfo.WarehouseCode,
                BarcodeQty = lockInfo.BarcodeQty,
                BarcodeUnit = lockInfo.BarcodeUnit,
            };
            await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync();
            // æ›´æ–°åŽŸé”å®šä¿¡æ¯
@@ -516,7 +1165,6 @@
            return new SplitResultDto { NewBarcode = newBarcode };
        }
        private async Task ExecuteCancelSplitLogic(Dt_SplitPackageRecord splitRecord, Dt_OutStockLockInfo newLockInfo, Dt_StockInfoDetail newStockDetail)
        {
            // æ¢å¤åŽŸåº“å­˜
@@ -574,15 +1222,28 @@
            await CheckAndUpdateOrderStatus(orderNo);
        }
        private async Task RevertBatchAndOrderData(Dt_PickingRecord pickingRecord)
        private async Task RevertBatchAndOrderData(Dt_PickingRecord pickingRecord, RevertPickingResult revertResult)
        {
            // æ¢å¤æ‰¹æ¬¡å®Œæˆæ•°é‡
            var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>()
                .FirstAsync(x => x.BatchNo == pickingRecord.BatchNo);
                .FirstAsync(x => x.BatchNo == revertResult.LockInfo.OutboundBatchNo); // ä½¿ç”¨ OutboundBatchNo
            batch.CompletedQuantity -= pickingRecord.PickQuantity;
            batch.BatchStatus = (int)BatchStatusEnum.执行中;
            await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync();
            if (batch != null)
            {
                batch.CompletedQuantity -= pickingRecord.PickQuantity;
                // é‡æ–°è®¡ç®—批次状态
                if (batch.CompletedQuantity <= 0)
                {
                    batch.BatchStatus = (int)BatchStatusEnum.分配中;
                }
                else if (batch.CompletedQuantity < batch.BatchQuantity)
                {
                    batch.BatchStatus = (int)BatchStatusEnum.执行中;
                }
                await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync();
            }
            // æ¢å¤è®¢å•明细
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
@@ -595,46 +1256,61 @@
            // é‡æ–°æ£€æŸ¥è®¢å•状态
            await CheckAndUpdateOrderStatus(pickingRecord.OrderNo);
        }
        private async Task ReleaseLockAndStock(Dt_OutStockLockInfo lockInfo)
        {
            // æ¢å¤åº“存状态
            // æ¢å¤åº“存状态 - å›žåº“后库存变为可用状态
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId);
            if (stockDetail != null)
            {
                // å›žåº“后库存状态恢复为入库完成(可用状态)
                stockDetail.Status = (int)StockStatusEmun.入库完成;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            }
            // æ›´æ–°é”å®šè®°å½•状态为回库
            // æ›´æ–°é”å®šè®°å½•状态为已回库
            lockInfo.Status = (int)OutLockStockStatusEnum.已回库;
            lockInfo.Operator = App.User.UserName;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
        }
        private async Task UpdateBatchStatusForReturn(string batchNo, List<Dt_OutStockLockInfo> returnedLocks)
        /// <summary>
        /// æ›´æ–°æ‰¹æ¬¡çŠ¶æ€ï¼ˆå›žåº“ï¼‰
        /// </summary>
        private async Task UpdateBatchStatusForReturn(string outboundBatchNo, List<Dt_OutStockLockInfo> returnedLocks)
        {
            var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>()
                .FirstAsync(x => x.BatchNo == batchNo);
                .FirstAsync(x => x.BatchNo == outboundBatchNo);
            // è®¡ç®—回库数量
            var returnedQty = returnedLocks.Sum(x => x.AssignQuantity - x.PickedQty);
            batch.CompletedQuantity -= returnedQty;
            if (batch.CompletedQuantity <= 0)
            if (batch != null)
            {
                batch.BatchStatus = (int)BatchStatusEnum.已回库;
            }
            else
            {
                batch.BatchStatus = (int)BatchStatusEnum.执行中;
            }
                // è®¡ç®—回库数量(未拣选的部分)
                var returnedQty = returnedLocks.Sum(x => x.AssignQuantity - x.PickedQty);
                batch.CompletedQuantity -= returnedQty;
            batch.Operator = App.User.UserName;
            await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync();
                // æ›´æ–°æ‰¹æ¬¡çŠ¶æ€
                if (batch.CompletedQuantity <= 0)
                {
                    batch.BatchStatus = (int)BatchStatusEnum.已回库;
                }
                else if (batch.CompletedQuantity < batch.BatchQuantity)
                {
                    batch.BatchStatus = (int)BatchStatusEnum.执行中;
                }
                else
                {
                    batch.BatchStatus = (int)BatchStatusEnum.已完成;
                }
                batch.Operator = App.User.UserName;
                await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync();
            }
        }
        /// <summary>
        /// æ›´æ–°è®¢å•明细(回库后)
        /// </summary>
        private async Task UpdateOrderDetailAfterReturn(List<Dt_OutStockLockInfo> returnedLocks)
        {
            var orderDetailGroups = returnedLocks.GroupBy(x => x.OrderDetailId);
@@ -644,13 +1320,16 @@
                var orderDetailId = group.Key;
                var returnedQty = group.Sum(x => x.AssignQuantity - x.PickedQty);
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(x => x.AllocatedQuantity == x.AllocatedQuantity - returnedQty)
                    .Where(x => x.Id == orderDetailId)
                    .ExecuteCommandAsync();
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == orderDetailId);
                if (orderDetail != null)
                {
                    orderDetail.AllocatedQuantity -= returnedQty;
                    await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                }
            }
        }
        #endregion
        #region è¾…助方法
@@ -693,6 +1372,13 @@
                PickTime = DateTime.Now,
                Operator = App.User.UserName,
                OutStockLockId = result.FinalLockInfo.Id,
                BarcodeUnit = result.FinalLockInfo.BarcodeUnit,
                BarcodeQty = result.FinalLockInfo.BarcodeQty,
                BatchNo = result.FinalLockInfo.BatchNo,
                lineNo = result.FinalLockInfo.lineNo,
                SupplyCode = result.FinalLockInfo.SupplyCode,
                WarehouseCode = result.FinalLockInfo.WarehouseCode,
                //  IsCancelled = false
            };
@@ -716,8 +1402,19 @@
                .Where(x => x.OrderNo == orderNo)
                .ExecuteCommandAsync();
        }
        private string GetPalletStatusText(PalletStatusEnum status)
        {
            return status switch
            {
                PalletStatusEnum.未开始 => "未开始",
                PalletStatusEnum.拣选中 => "拣选中",
                PalletStatusEnum.已完成 => "已完成",
                PalletStatusEnum.无任务 => "无任务",
                _ => "未知"
            };
        }
        #endregion
        #region DTOç±»