pan
2025-11-30 dcdb87f1cb6cfd66d3fc01bc2248e4876c37f223
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -8,7 +8,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using WIDESEA_Common.CommonEnum;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.OrderEnum;
using WIDESEA_Common.StockEnum;
@@ -16,11 +18,15 @@
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Enums;
using WIDESEA_Core.Helper;
using WIDESEA_DTO.Allocate;
using WIDESEA_DTO.Basic;
using WIDESEA_DTO.Inbound;
using WIDESEA_DTO.Outbound;
using WIDESEA_IAllocateService;
using WIDESEA_IBasicService;
using WIDESEA_IInboundService;
using WIDESEA_IOutboundService;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
@@ -47,6 +53,10 @@
        private readonly IESSApiService _eSSApiService;
        private readonly IInvokeMESService _invokeMESService;
        private readonly IDailySequenceService _dailySequenceService;
        private readonly IAllocateService _allocateService;
        private readonly IRepository<Dt_InboundOrder> _inboundOrderRepository;
        private readonly IInboundOrderDetailService _inboundOrderDetailService;
        private readonly ILogger<OutboundPickingService> _logger;
@@ -67,7 +77,7 @@
        public OutboundPickingService(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) : base(BaseDal)
            IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_InboundOrder> inboundOrderRepository,IInboundOrderDetailService inboundOrderDetailService) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _stockInfoService = stockInfoService;
@@ -83,1651 +93,13 @@
            _logger = logger;
            _invokeMESService = invokeMESService;
            _dailySequenceService = dailySequenceService;
            _allocateService = allocateService;
            _inboundOrderRepository = inboundOrderRepository;
            _inboundOrderDetailService = inboundOrderDetailService;
        }
        #region æŸ¥è¯¢å‡ºåº“详情列表
        public async Task<List<OutStockLockListResp>> GetOutStockLockListAsync(string orderNo)
        {
            var locks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(t => t.OrderNo == orderNo)
                .ToListAsync();
            return locks.Select(t => new OutStockLockListResp
            {
                Id = t.Id,
                // TaskNum = t.TaskNum,
                PalletCode = t.PalletCode,
                CurrentBarcode = t.CurrentBarcode,
                AssignQuantity = t.AssignQuantity,
                PickedQty = t.PickedQty,
                Status = t.Status,
                //  IsSplitted = t.IsSplitted
            }).ToList();
        }
        #endregion
        public async Task<WebResponseContent> ValidateBarcode(string barcode)
        {
            try
            {
                if (string.IsNullOrEmpty(barcode))
                {
                    return WebResponseContent.Instance.Error("条码不能为空");
                }
                // æ ¹æ®æ¡ç æŸ¥è¯¢åº“存明细
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Includes(x => x.StockInfo)
                    .Where(x => x.Barcode == barcode)
                    .FirstAsync();
                if (stockDetail == null)
                {
                    return WebResponseContent.Instance.Error("条码不存在");
                }
                var result = new
                {
                    Barcode = barcode,
                    MaterielCode = stockDetail.MaterielCode,
                    BatchNo = stockDetail.BatchNo,
                    AvailableQuantity = stockDetail.StockQuantity - stockDetail.OutboundQuantity,
                    LocationCode = stockDetail.StockInfo?.LocationCode,
                    PalletCode = stockDetail.StockInfo?.PalletCode
                };
                return WebResponseContent.Instance.OK(null, result);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"条码验证失败: {ex.Message}");
            }
        }
        // æ£€æŸ¥å¹¶æ›´æ–°è®¢å•状态
        private async Task CheckAndUpdateOrderStatus(string orderNo)
        {
            var orderDetails = _stockInfoDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                      .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id) // å…³è”条件:父表 Id = å­è¡¨ OrderId
                      .Where((o, item) => item.OrderNo == orderNo) // è¿‡æ»¤çˆ¶è¡¨ OrderNo
                      .Select((o, item) => o) // åªè¿”回子表数据
                      .ToList();
            //var orderDetails = await _stockInfoDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
            //    .Where(x => x.OrderId == orderNo.ObjToInt())
            //    .ToListAsync();
            bool allCompleted = true;
            foreach (var detail in orderDetails)
            {
                if (detail.OverOutQuantity < detail.NeedOutQuantity)
                {
                    allCompleted = false;
                    break;
                }
            }
            if (allCompleted)
            {
                try
                {
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => x.OrderStatus == 2) // å·²å®Œæˆ
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                    var outboundOrder = _stockInfoService.Db.Queryable<Dt_OutboundOrder>().First(x => x.OrderNo == orderNo);
                    if (outboundOrder != null && outboundOrder.OrderStatus == OutOrderStatusEnum.出库完成.ObjToInt())
                    {
                        if (outboundOrder.OrderType == OutOrderTypeEnum.Allocate.ObjToInt().ObjToInt())//调拨出库
                        {
                        }
                        else if (outboundOrder.OrderType == OutOrderTypeEnum.ReCheck.ObjToInt()) //重检出库
                        {
                        }
                        else
                        {
                            var feedmodel = new FeedbackOutboundRequestModel
                            {
                                reqCode = Guid.NewGuid().ToString(),
                                reqTime = DateTime.Now.ToString(),
                                business_type = outboundOrder.BusinessType,
                                factoryArea = outboundOrder.FactoryArea,
                                operationType = 1,
                                Operator = outboundOrder.Operator,
                                orderNo = outboundOrder.UpperOrderNo,
                                status = outboundOrder.OrderStatus,
                                details = new List<FeedbackOutboundDetailsModel>()
                            };
                            var lists = _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>().Where(x => x.OrderNo == orderNo && x.Status == (int)OutLockStockStatusEnum.拣选完成).ToList();
                            var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.lineNo, item.Unit, item.WarehouseCode })
                               .Select(group => new FeedbackOutboundDetailsModel
                               {
                                   materialCode = group.Key.MaterielCode,
                                   lineNo = group.Key.lineNo,
                                   warehouseCode = group.Key.WarehouseCode,
                                   currentDeliveryQty = group.Sum(x => x.OrderQuantity),
                                   // warehouseCode= "1072",
                                   unit = group.Key.Unit,
                                   barcodes = group.Select(row => new WIDESEA_DTO.Outbound.BarcodesModel
                                   {
                                       barcode = row.CurrentBarcode,
                                       supplyCode = row.SupplyCode,
                                       batchNo = row.BatchNo,
                                       unit = row.Unit,
                                       qty = row.PickedQty
                                   }).ToList()
                               }).ToList();
                            feedmodel.details = groupedData;
                            var result = await _invokeMESService.FeedbackOutbound(feedmodel);
                            if (result != null && result.code == 200)
                            {
                                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>().SetColumns(x => x.ReturnToMESStatus == 1) // å·²å®Œæˆ
                                            .Where(x => x.OrderId == outboundOrder.Id).ExecuteCommandAsync();
                                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() .SetColumns(x => x.ReturnToMESStatus == 1) // å·²å®Œæˆ
                                              .Where(x => x.OrderNo == orderNo) .ExecuteCommandAsync();
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(" OutboundPickingService  FeedbackOutbound : " + ex.Message);
                }
            }
        }
        public async Task<WebResponseContent> ConfirmPicking(string orderNo, string palletCode, string barcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                // 1. éªŒè¯è¾“入参数
                if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode) || string.IsNullOrEmpty(barcode))
                {
                    throw new Exception("订单号、托盘码和条码不能为空");
                }
                // 2. æŸ¥æ‰¾å‡ºåº“锁定信息
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(it => it.OrderNo == orderNo &&
                               it.Status == (int)OutLockStockStatusEnum.出库中 &&
                               it.PalletCode == palletCode &&
                               it.CurrentBarcode == barcode &&
                               it.AssignQuantity > it.PickedQty)
                    .FirstAsync();
                if (lockInfo == null)
                {
                    lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                        .Where(it => it.CurrentBarcode == barcode &&
                                   it.Status == (int)OutLockStockStatusEnum.出库中 &&
                                   it.AssignQuantity > it.PickedQty)
                        .FirstAsync();
                    if (lockInfo == null)
                    {
                        var completedLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                            .Where(it => it.CurrentBarcode == barcode &&
                                       (it.Status == (int)OutLockStockStatusEnum.拣选完成 ||
                                        it.PickedQty >= it.AssignQuantity))
                            .FirstAsync();
                        if (completedLockInfo != null)
                            throw new Exception($"条码{barcode}已经完成分拣,不能重复分拣");
                        else
                            throw new Exception($"条码{barcode}不属于托盘{palletCode}或不存在待分拣记录");
                    }
                }
                if (lockInfo.PalletCode != palletCode)
                    throw new Exception($"条码{barcode}不属于托盘{palletCode}");
                // æ£€æŸ¥æ‹£é€‰åŽ†å²ï¼Œé˜²æ­¢é‡å¤åˆ†æ‹£
                var existingPicking = await Db.Queryable<Dt_PickingRecord>()
                    .Where(x => x.Barcode == barcode &&
                               x.OrderNo == orderNo &&
                               x.PalletCode == palletCode &&
                               x.OutStockLockId == lockInfo.Id)
                    .FirstAsync();
                if (existingPicking != null)
                {
                    throw new Exception($"条码{barcode}已经分拣过,不能重复分拣");
                }
                // èŽ·å–è®¢å•æ˜Žç»†å¹¶æ£€æŸ¥æ•°é‡é™åˆ¶
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
                if (orderDetail == null)
                    throw new Exception($"未找到订单明细,ID: {lockInfo.OrderDetailId}");
                // å…³é”®ä¿®å¤ï¼šæ£€æŸ¥ç´¯è®¡æ‹£é€‰æ•°é‡æ˜¯å¦ä¼šè¶…过订单数量
                decimal actualQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                decimal remainingOrderQty = orderDetail.NeedOutQuantity - orderDetail.OverOutQuantity;
                if (actualQty > remainingOrderQty)
                {
                    // å¦‚果分配数量大于剩余订单数量,调整实际拣选数量
                    actualQty = remainingOrderQty;
                    if (actualQty <= 0)
                    {
                        throw new Exception($"订单{orderNo}的需求数量已满足,无法继续分拣");
                    }
                    _logger.LogWarning($"调整分拣数量:原分配{lockInfo.AssignQuantity - lockInfo.PickedQty},调整为{actualQty},订单需求{orderDetail.NeedOutQuantity},已出库{orderDetail.OverOutQuantity}");
                }
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(x => x.Barcode == barcode && x.StockId == lockInfo.StockId)
                    .FirstAsync();
                if (stockDetail == null)
                    return WebResponseContent.Instance.Error("无效的条码或物料编码");
                decimal stockQuantity = stockDetail.StockQuantity;
                List<SplitResult> splitResults = new List<SplitResult>();
                Dt_OutStockLockInfo finalLockInfo = lockInfo;
                var finalBarcode = barcode;
                var finalStockId = stockDetail.Id;
                decimal actualPickedQty = actualQty;
                if (actualQty < stockQuantity)
                {
                    // æƒ…况1: åˆ†é…æ•°é‡å°äºŽåº“存数量,需要自动拆包
                    decimal remainingStockQty = stockQuantity - actualQty;
                    // æ›´æ–°åŽŸæ¡ç åº“å­˜ä¸ºå‰©ä½™æ•°é‡
                    stockDetail.StockQuantity = remainingStockQty;
                    stockDetail.OutboundQuantity = remainingStockQty;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    // ç”Ÿæˆæ–°æ¡ç ç”¨äºŽè®°å½•拣选数量
                    var seq = await _dailySequenceService.GetNextSequenceAsync();
                    string newBarcode = "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0');
                    // ä¸ºæ–°æ¡ç åˆ›å»ºå‡ºåº“锁定信息
                    var newLockInfo = new Dt_OutStockLockInfo
                    {
                        OrderNo = lockInfo.OrderNo,
                        OrderDetailId = lockInfo.OrderDetailId,
                        BatchNo = lockInfo.BatchNo,
                        MaterielCode = lockInfo.MaterielCode,
                        MaterielName = lockInfo.MaterielName,
                        StockId = lockInfo.StockId,
                        OrderQuantity = actualQty,
                        OriginalQuantity = actualQty,
                        AssignQuantity = actualQty,
                        PickedQty = actualQty,
                        LocationCode = lockInfo.LocationCode,
                        PalletCode = lockInfo.PalletCode,
                        TaskNum = lockInfo.TaskNum,
                        Status = (int)OutLockStockStatusEnum.拣选完成,
                        Unit = lockInfo.Unit,
                        SupplyCode = lockInfo.SupplyCode,
                        OrderType = lockInfo.OrderType,
                        CurrentBarcode = newBarcode,
                        OriginalLockQuantity = actualQty,
                        IsSplitted = 1,
                        ParentLockId = lockInfo.Id
                    };
                    var newLockId = await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteReturnIdentityAsync();
                    newLockInfo.Id = newLockId;
                    // è®°å½•拆包历史
                    var splitHistory = new Dt_SplitPackageRecord
                    {
                        FactoryArea = lockInfo.FactoryArea,
                        TaskNum = lockInfo.TaskNum,
                        OutStockLockInfoId = lockInfo.Id,
                        StockId = stockDetail.StockId,
                        Operator = App.User.UserName,
                        IsReverted = false,
                        OriginalBarcode = barcode,
                        NewBarcode = newBarcode,
                        SplitQty = actualQty,
                        RemainQuantity = remainingStockQty,
                        MaterielCode = lockInfo.MaterielCode,
                        SplitTime = DateTime.Now,
                        OrderNo = lockInfo.OrderNo,
                        PalletCode = lockInfo.PalletCode,
                        Status = (int)SplitPackageStatusEnum.已拣选
                    };
                    await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync();
                    // æ›´æ–°åŽŸé”å®šä¿¡æ¯ä¸ºå‰©ä½™åº“å­˜æ•°é‡
                    lockInfo.AssignQuantity = remainingStockQty;
                    lockInfo.PickedQty = 0;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                    splitResults.Add(new SplitResult
                    {
                        materialCode = lockInfo.MaterielCode,
                        supplierCode = lockInfo.SupplyCode,
                        quantityTotal = actualQty.ToString("F2"),
                        batchNumber = newBarcode,
                        batch = lockInfo.BatchNo,
                        factory = lockInfo.FactoryArea,
                        date = DateTime.Now.ToString("yyyy-MM-dd"),
                    });
                    splitResults.Add(new SplitResult
                    {
                        materialCode = lockInfo.MaterielCode,
                        supplierCode = lockInfo.SupplyCode,
                        quantityTotal = remainingStockQty.ToString("F2"),
                        batchNumber = barcode,
                        batch = lockInfo.BatchNo,
                        factory = lockInfo.FactoryArea,
                        date = DateTime.Now.ToString("yyyy-MM-dd"),
                    });
                    finalLockInfo = newLockInfo;
                    finalBarcode = newBarcode;
                }
                else if (actualQty == stockQuantity)
                {
                    // æƒ…况2: åˆ†é…æ•°é‡ç­‰äºŽåº“存数量,整包出库
                    stockDetail.StockQuantity = 0;
                    stockDetail.OutboundQuantity = 0;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    lockInfo.PickedQty += actualQty;
                    lockInfo.Status = (int)OutLockStockStatusEnum.拣选完成;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                }
                else
                {
                    // æƒ…况3: åˆ†é…æ•°é‡å¤§äºŽåº“存数量,库存整包出库
                    decimal stockOutQty = stockQuantity;
                    stockDetail.StockQuantity = 0;
                    stockDetail.OutboundQuantity = 0;
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                    decimal remainingAssignQty = actualQty - stockQuantity;
                    lockInfo.PickedQty += stockOutQty;
                    lockInfo.AssignQuantity = remainingAssignQty;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                    var relatedSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                        .Where(it => it.OriginalBarcode == barcode || it.NewBarcode == barcode)
                        .Where(it => !it.IsReverted)
                        .ToListAsync();
                    foreach (var record in relatedSplitRecords)
                    {
                        record.Status = (int)SplitPackageStatusEnum.已拣选;
                        await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
                    }
                    actualPickedQty = stockOutQty;
                }
                // å…³é”®ä¿®å¤ï¼šå†æ¬¡æ£€æŸ¥è®¢å•数量限制
                decimal newOverOutQuantity = orderDetail.OverOutQuantity + actualPickedQty;
                decimal newPickedQty = orderDetail.PickedQty + actualPickedQty;
                if (newOverOutQuantity > orderDetail.NeedOutQuantity)
                {
                    throw new Exception($"分拣后将导致已出库数量({newOverOutQuantity})超过订单需求数量({orderDetail.NeedOutQuantity})");
                }
                // æ›´æ–°è®¢å•明细
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        PickedQty = newPickedQty,
                        OverOutQuantity = newOverOutQuantity
                    })
                    .Where(it => it.Id == lockInfo.OrderDetailId)
                    .ExecuteCommandAsync();
                await CheckAndUpdateOrderStatus(orderNo);
                // æŸ¥è¯¢ä»»åŠ¡è¡¨
                var task = _taskRepository.QueryData(x => x.OrderNo == orderNo && x.PalletCode == palletCode).FirstOrDefault();
                if (finalLockInfo.Id <= 0)
                {
                    throw new Exception($"锁定信息ID无效: {finalLockInfo.Id},无法记录拣选历史");
                }
                // è®°å½•拣选历史
                var pickingHistory = new Dt_PickingRecord
                {
                    FactoryArea = finalLockInfo.FactoryArea,
                    TaskNo = task?.TaskNum ?? 0,
                    LocationCode = task?.SourceAddress ?? "",
                    StockId = finalStockId,
                    OrderNo = orderNo,
                    OrderDetailId = finalLockInfo.OrderDetailId,
                    PalletCode = palletCode,
                    Barcode = finalBarcode,
                    MaterielCode = finalLockInfo.MaterielCode,
                    PickQuantity = actualPickedQty,
                    PickTime = DateTime.Now,
                    Operator = App.User.UserName,
                    OutStockLockId = finalLockInfo.Id
                };
                await Db.Insertable(pickingHistory).ExecuteCommandAsync();
                _unitOfWorkManage.CommitTran();
                if (splitResults.Any())
                {
                    return WebResponseContent.Instance.OK("拣选确认成功,已自动拆包", new { SplitResults = splitResults });
                }
                return WebResponseContent.Instance.OK("拣选确认成功", new { SplitResults = new List<SplitResult>() });
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"ConfirmPicking失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {barcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"拣选确认失败:{ex.Message}");
            }
        }
        public async Task<WebResponseContent> CancelPicking(string orderNo, string palletCode, string barcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                // æŸ¥æ‰¾æ‹£é€‰è®°å½•
                var pickingRecord = await Db.Queryable<Dt_PickingRecord>()
                    .Where(it => it.OrderNo == orderNo &&
                               it.PalletCode == palletCode &&
                               it.Barcode == barcode)
                    .OrderByDescending(it => it.PickTime)
                    .FirstAsync();
                if (pickingRecord == null)
                    return WebResponseContent.Instance.Error("未找到对应的拣选记录");
                // æŸ¥æ‰¾å‡ºåº“锁定信息
                var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(it => it.Id == pickingRecord.OutStockLockId)
                    .FirstAsync();
                if (lockInfo == null)
                    return WebResponseContent.Instance.Error("未找到对应的出库锁定信息");
                // æ£€æŸ¥æ˜¯å¦å¯ä»¥å–消
                if (lockInfo.Status != (int)OutLockStockStatusEnum.拣选完成)
                    return WebResponseContent.Instance.Error("当前状态不允许取消分拣");
                decimal cancelQty = pickingRecord.PickQuantity;
                // èŽ·å–è®¢å•æ˜Žç»†
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == pickingRecord.OrderDetailId);
                if (orderDetail == null)
                    throw new Exception($"未找到订单明细,ID: {pickingRecord.OrderDetailId}");
                // å…³é”®ä¿®å¤ï¼šæ£€æŸ¥å–消后数量不会为负数
                decimal newOverOutQuantity = orderDetail.OverOutQuantity - cancelQty;
                decimal newPickedQty = orderDetail.PickedQty - cancelQty;
                if (newOverOutQuantity < 0 || newPickedQty < 0)
                {
                    throw new Exception($"取消分拣将导致已出库数量或已拣选数量为负数");
                }
                // å¤„理取消逻辑
                if (lockInfo.IsSplitted == 1 && lockInfo.ParentLockId.HasValue)
                {
                    await HandleSplitBarcodeCancel(lockInfo, pickingRecord, cancelQty);
                }
                else
                {
                    await HandleNormalBarcodeCancel(lockInfo, pickingRecord, cancelQty);
                }
                // æ›´æ–°è®¢å•明细
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        PickedQty = newPickedQty,
                        OverOutQuantity = newOverOutQuantity
                    })
                    .Where(it => it.Id == pickingRecord.OrderDetailId)
                    .ExecuteCommandAsync();
                // åˆ é™¤æ‹£é€‰è®°å½•
                await Db.Deleteable<Dt_PickingRecord>()
                    .Where(x => x.Id == pickingRecord.Id)
                    .ExecuteCommandAsync();
                // é‡æ–°æ£€æŸ¥è®¢å•状态
                await CheckAndUpdateOrderStatus(orderNo);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK($"取消分拣成功,恢复数量:{cancelQty}");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"CancelPicking失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {barcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"取消分拣失败:{ex.Message}");
            }
        }
        private async Task HandleSplitBarcodeCancel(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, decimal cancelQty)
        {
            // æŸ¥æ‰¾çˆ¶é”å®šä¿¡æ¯
            var parentLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.Id == lockInfo.ParentLockId.Value)
                .FirstAsync();
            if (parentLockInfo == null)
            {
                throw new Exception("未找到父锁定信息,无法取消拆包分拣");
            }
            // æ¢å¤çˆ¶é”å®šä¿¡æ¯çš„分配数量
            parentLockInfo.AssignQuantity += cancelQty;
            await _outStockLockInfoService.Db.Updateable(parentLockInfo).ExecuteCommandAsync();
            // æ¢å¤åº“å­˜
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == parentLockInfo.CurrentBarcode && x.StockId == parentLockInfo.StockId)
                .FirstAsync();
            if (stockDetail != null)
            {
                stockDetail.StockQuantity += cancelQty;
                stockDetail.OutboundQuantity += cancelQty;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            }
            // æ›´æ–°æ‹†åŒ…记录状态
            await _splitPackageService.Db.Updateable<Dt_SplitPackageRecord>()
                .SetColumns(x => new Dt_SplitPackageRecord
                {
                    Status = (int)SplitPackageStatusEnum.已撤销,
                    IsReverted = true
                })
                .Where(x => x.NewBarcode == lockInfo.CurrentBarcode && !x.IsReverted)
                .ExecuteCommandAsync();
            // åˆ é™¤æ‹†åŒ…产生的锁定信息
            await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                .Where(x => x.Id == lockInfo.Id)
                .ExecuteCommandAsync();
        }
        private async Task HandleNormalBarcodeCancel(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, decimal cancelQty)
        {
            // æ¢å¤é”å®šä¿¡æ¯
            lockInfo.PickedQty -= cancelQty;
            if (lockInfo.PickedQty < 0) lockInfo.PickedQty = 0;
            lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // æ¢å¤åº“å­˜
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == pickingRecord.Barcode && x.StockId == pickingRecord.StockId)
                .FirstAsync();
            if (stockDetail != null)
            {
                stockDetail.StockQuantity += cancelQty;
                stockDetail.OutboundQuantity += cancelQty;
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            }
        }
        public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode))
                {
                    return WebResponseContent.Instance.Error("订单号和托盘码不能为空");
                }
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                    .FirstAsync(x => x.PalletCode == palletCode);
                if (stockInfo == null)
                {
                    return WebResponseContent.Instance.Error($"未找到托盘 {palletCode} å¯¹åº”的库存信息");
                }
                var task = await GetCurrentTask(orderNo, palletCode);
                if (task == null)
                {
                    return WebResponseContent.Instance.Error("未找到对应的任务信息");
                }
                // åˆ†æžéœ€è¦å›žåº“的货物
                var returnAnalysis = await AnalyzeReturnItems(orderNo, palletCode, stockInfo.Id);
                if (!returnAnalysis.HasItemsToReturn)
                {
                    return await HandleNoReturnItems(orderNo, palletCode);
                }
                // æ‰§è¡Œå›žåº“操作
                await ExecuteReturnOperations(orderNo, palletCode, stockInfo, task, returnAnalysis);
                //创建回库任务并处理ESS
                await CreateReturnTaskAndHandleESS(orderNo, palletCode, task, returnAnalysis);
                _unitOfWorkManage.CommitTran();
                // æ›´æ–°è®¢å•状态
                await UpdateOrderStatusForReturn(orderNo);
                return WebResponseContent.Instance.OK($"回库操作成功,共回库数量:{returnAnalysis.TotalReturnQty}");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"ReturnRemaining失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"回库操作失败: {ex.Message}");
            }
        }
        #region è®¢å•状态
        private async Task UpdateOrderStatusForReturn(string orderNo)
        {
            try
            {
                var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id)
                    .Where((o, item) => item.OrderNo == orderNo)
                    .Select((o, item) => o)
                    .ToListAsync();
                bool allCompleted = true;
                foreach (var detail in orderDetails)
                {
                    if (detail.OverOutQuantity < detail.NeedOutQuantity)
                    {
                        allCompleted = false;
                        break;
                    }
                }
                var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                    .FirstAsync(x => x.OrderNo == orderNo);
                if (outboundOrder == null) return;
                // åªæœ‰å½“状态确实发生变化时才更新
                int newStatus = allCompleted ? (int)OutOrderStatusEnum.出库完成 : (int)OutOrderStatusEnum.出库中;
                if (outboundOrder.OrderStatus != newStatus)
                {
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => x.OrderStatus == newStatus)
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                    _logger.LogInformation($"回库操作更新订单状态 - OrderNo: {orderNo}, æ–°çŠ¶æ€: {newStatus}");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"UpdateOrderStatusForReturn失败 - OrderNo: {orderNo}, Error: {ex.Message}");
            }
        }
        #endregion
        #region Private Methods
        private async Task<Dt_Task> GetCurrentTask(string orderNo, string palletCode)
        {
            // å…ˆå°è¯•通过订单号和托盘号查找任务
            var task = await _taskRepository.Db.Queryable<Dt_Task>()
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                .FirstAsync();
            if (task == null)
            {
                // å¦‚果找不到,再通过托盘号查找
                task = await _taskRepository.Db.Queryable<Dt_Task>()
                    .Where(x => x.PalletCode == palletCode)
                    .FirstAsync();
            }
            return task;
        }
        private async Task<ReturnAnalysisResult> AnalyzeReturnItems(string orderNo, string palletCode, int stockId)
        {
            var result = new ReturnAnalysisResult();
            // èŽ·å–æœªåˆ†æ‹£çš„å‡ºåº“é”å®šè®°å½•
            var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.OrderNo == orderNo &&
                           it.PalletCode == palletCode &&
                           it.Status == (int)OutLockStockStatusEnum.出库中)
                .ToListAsync();
            if (remainingLocks.Any())
            {
                result.HasRemainingLocks = true;
                result.RemainingLocks = remainingLocks;
                result.RemainingLocksReturnQty = remainingLocks.Sum(x => x.AssignQuantity - x.PickedQty);
            }
            // æ£€æŸ¥æ‰˜ç›˜ä¸Šæ˜¯å¦æœ‰å…¶ä»–库存货物
            var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.StockId == stockId &&
                            (it.Status == StockStatusEmun.入库确认.ObjToInt() ||
                             it.Status == StockStatusEmun.入库完成.ObjToInt() ||
                             it.Status == StockStatusEmun.出库锁定.ObjToInt()))
                .Where(it => it.StockQuantity > 0) // æœ‰åº“存的
                .ToListAsync();
            if (palletStockGoods.Any())
            {
                result.HasPalletStockGoods = true;
                result.PalletStockGoods = palletStockGoods;
                result.PalletStockReturnQty = palletStockGoods.Sum(x => x.StockQuantity);
            }
            //  æ£€æŸ¥æ‹†åŒ…记录
            var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && !it.IsReverted)
                .ToListAsync();
            if (splitRecords.Any())
            {
                result.HasSplitRecords = true;
                result.SplitRecords = splitRecords;
                result.SplitReturnQty = await CalculateSplitReturnQuantity(splitRecords, stockId);
            }
            result.TotalReturnQty = result.RemainingLocksReturnQty + result.PalletStockReturnQty + result.SplitReturnQty;
            result.HasItemsToReturn = result.TotalReturnQty > 0;
            return result;
        }
        private async Task<decimal> CalculateSplitReturnQuantity(List<Dt_SplitPackageRecord> splitRecords, int stockId)
        {
            decimal totalQty = 0;
            var processedBarcodes = new HashSet<string>();
            foreach (var splitRecord in splitRecords)
            {
                // æ£€æŸ¥åŽŸæ¡ç 
                if (!processedBarcodes.Contains(splitRecord.OriginalBarcode))
                {
                    var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockId)
                        .FirstAsync();
                    if (originalStock != null && originalStock.StockQuantity > 0)
                    {
                        totalQty += originalStock.StockQuantity;
                        processedBarcodes.Add(splitRecord.OriginalBarcode);
                    }
                }
                // æ£€æŸ¥æ–°æ¡ç 
                if (!processedBarcodes.Contains(splitRecord.NewBarcode))
                {
                    var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockId)
                        .FirstAsync();
                    if (newStock != null && newStock.StockQuantity > 0)
                    {
                        totalQty += newStock.StockQuantity;
                        processedBarcodes.Add(splitRecord.NewBarcode);
                    }
                }
            }
            return totalQty;
        }
        private async Task<WebResponseContent> HandleNoReturnItems(string orderNo, string palletCode)
        {
            // æ£€æŸ¥æ˜¯å¦æ‰€æœ‰è´§ç‰©éƒ½å·²æ‹£é€‰å®Œæˆ
            var allPicked = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode)
                .AnyAsync(it => it.Status == (int)OutLockStockStatusEnum.拣选完成);
            if (allPicked)
            {
                return WebResponseContent.Instance.OK("所有货物已拣选完成,托盘为空");
            }
            else
            {
                return WebResponseContent.Instance.Error("没有需要回库的剩余货物");
            }
        }
        private async Task ExecuteReturnOperations(string orderNo, string palletCode, Dt_StockInfo stockInfo,
            Dt_Task task, ReturnAnalysisResult analysis)
        {
            // æƒ…况1:处理未分拣的出库锁定记录
            if (analysis.HasRemainingLocks)
            {
                await HandleRemainingLocksReturn(analysis.RemainingLocks, stockInfo.Id);
                // å…³é”®ï¼šæ›´æ–°è®¢å•明细的已拣选数量
                await UpdateOrderDetailsOnReturn(analysis.RemainingLocks);
            }
            // æƒ…况2:处理托盘上其他库存货物
            if (analysis.HasPalletStockGoods)
            {
                await HandlePalletStockGoodsReturn(analysis.PalletStockGoods);
            }
            // æƒ…况3:处理拆包记录
            if (analysis.HasSplitRecords)
            {
                await HandleSplitRecordsReturn(analysis.SplitRecords, orderNo, palletCode);
            }
            // æ›´æ–°åº“存主表状态
            await UpdateStockInfoStatus(stockInfo);
        }
        private async Task HandleRemainingLocksReturn(List<Dt_OutStockLockInfo> remainingLocks, int stockId)
        {
            var lockIds = remainingLocks.Select(x => x.Id).ToList();
            // æ›´æ–°å‡ºåº“锁定记录状态为回库中
            await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
                .SetColumns(it => new Dt_OutStockLockInfo
                {
                    Status = (int)OutLockStockStatusEnum.回库中
                })
                .Where(it => lockIds.Contains(it.Id))
                .ExecuteCommandAsync();
            // å¤„理库存记录
            foreach (var lockInfo in remainingLocks)
            {
                decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                // æŸ¥æ‰¾å¯¹åº”的库存明细
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
                    .FirstAsync();
                if (stockDetail != null)
                {
                    // æ¢å¤åº“存状态
                    stockDetail.OutboundQuantity = Math.Max(0, stockDetail.OutboundQuantity - returnQty);
                    stockDetail.Status = StockStatusEmun.入库完成.ObjToInt();
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                }
                else
                {
                    // åˆ›å»ºæ–°çš„库存记录(拆包产生的新条码)
                    var newStockDetail = new Dt_StockInfoDetail
                    {
                        StockId = lockInfo.StockId,
                        MaterielCode = lockInfo.MaterielCode,
                        MaterielName = lockInfo.MaterielName,
                        OrderNo = lockInfo.OrderNo,
                        BatchNo = lockInfo.BatchNo,
                        StockQuantity = returnQty,
                        OutboundQuantity = 0,
                        Barcode = lockInfo.CurrentBarcode,
                        InboundOrderRowNo = "",
                        Status = StockStatusEmun.入库完成.ObjToInt(),
                        SupplyCode = lockInfo.SupplyCode,
                        WarehouseCode = lockInfo.WarehouseCode,
                        Unit = lockInfo.Unit
                    };
                    await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                }
            }
        }
        private async Task UpdateOrderDetailsOnReturn(List<Dt_OutStockLockInfo> remainingLocks)
        {
            // æŒ‰è®¢å•明细分组
            var orderDetailGroups = remainingLocks.GroupBy(x => x.OrderDetailId);
            foreach (var group in orderDetailGroups)
            {
                var orderDetailId = group.Key;
                var totalReturnQty = group.Sum(x => x.AssignQuantity - x.PickedQty);
                // èŽ·å–å½“å‰è®¢å•æ˜Žç»†
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == orderDetailId);
                if (orderDetail != null)
                {
                    // è°ƒæ•´å·²æ‹£é€‰æ•°é‡å’Œå·²å‡ºåº“数量
                    decimal newPickedQty = Math.Max(0, orderDetail.PickedQty - totalReturnQty);
                    decimal newOverOutQuantity = Math.Max(0, orderDetail.OverOutQuantity - totalReturnQty);
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                        .SetColumns(it => new Dt_OutboundOrderDetail
                        {
                            PickedQty = newPickedQty,
                            OverOutQuantity = newOverOutQuantity
                        })
                        .Where(it => it.Id == orderDetailId)
                        .ExecuteCommandAsync();
                }
            }
        }
        private async Task HandlePalletStockGoodsReturn(List<Dt_StockInfoDetail> palletStockGoods)
        {
            foreach (var stockGood in palletStockGoods)
            {
                // æ¢å¤åº“存状态
                stockGood.OutboundQuantity = 0;
                stockGood.Status = StockStatusEmun.入库完成.ObjToInt();
                await _stockInfoDetailService.Db.Updateable(stockGood).ExecuteCommandAsync();
            }
        }
        private async Task HandleSplitRecordsReturn(List<Dt_SplitPackageRecord> splitRecords, string orderNo, string palletCode)
        {
            // æ›´æ–°æ‹†åŒ…记录状态
            await _splitPackageService.Db.Updateable<Dt_SplitPackageRecord>()
                .SetColumns(x => new Dt_SplitPackageRecord
                {
                    Status = (int)SplitPackageStatusEnum.已回库
                })
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode && !x.IsReverted)
                .ExecuteCommandAsync();
        }
        private async Task UpdateStockInfoStatus(Dt_StockInfo stockInfo)
        {
            // æ›´æ–°åº“存主表状态
            stockInfo.StockStatus = StockStatusEmun.入库完成.ObjToInt();
            await _stockInfoService.Db.Updateable(stockInfo).ExecuteCommandAsync();
        }
        private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, ReturnAnalysisResult analysis)
        {
            var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>()
                .FirstAsync(x => x.LocationCode == originalTask.SourceAddress);
            // åˆ†é…æ–°è´§ä½ï¼ˆå›žåº“到存储位)
            var newLocation = _locationInfoService.AssignLocation(firstLocation.LocationType);
            Dt_Task returnTask = new()
            {
                CurrentAddress = stations[originalTask.TargetAddress],
                Grade = 0,
                PalletCode = palletCode,
                NextAddress = "",
                OrderNo = originalTask.OrderNo,
                Roadway = newLocation.RoadwayNo,
                SourceAddress = stations[originalTask.TargetAddress],
                TargetAddress = newLocation.LocationCode,
                TaskStatus = TaskStatusEnum.New.ObjToInt(),
                TaskType = TaskTypeEnum.InPick.ObjToInt(),
                PalletType = originalTask.PalletType,
                WarehouseId = originalTask.WarehouseId,
            };
            // ä¿å­˜å›žåº“任务
            await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
            // åˆ é™¤åŽŸå§‹å‡ºåº“ä»»åŠ¡
              _taskRepository.Db.Deleteable(originalTask);
            // ç»™ ESS å‘送流动信号和创建任务
            await SendESSCommands(palletCode, originalTask, returnTask);
        }
        private async Task SendESSCommands(string palletCode, Dt_Task originalTask, Dt_Task returnTask)
        {
            try
            {
                // 1. å‘送流动信号
                var moveResult = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest
                {
                    slotCode = movestations[originalTask.TargetAddress],
                    containerCode = palletCode
                });
                if (moveResult)
                {
                    // 2. åˆ›å»ºå›žåº“任务
                    var essTask = new TaskModel()
                    {
                        taskType = "putaway",
                        taskGroupCode = "",
                        groupPriority = 0,
                        tasks = new List<TasksType>
                {
                    new()
                    {
                        taskCode = returnTask.TaskNum.ToString(),
                        taskPriority = 0,
                        taskDescribe = new TaskDescribeType
                        {
                            containerCode = palletCode,
                            containerType = "CT_KUBOT_STANDARD",
                            fromLocationCode = stations.GetValueOrDefault(originalTask.TargetAddress) ?? "",
                            toStationCode = "",
                            toLocationCode = returnTask.TargetAddress,
                            deadline = 0,
                            storageTag = ""
                        }
                    }
                }
                    };
                    var resultTask = await _eSSApiService.CreateTaskAsync(essTask);
                    _logger.LogInformation($"ReturnRemaining åˆ›å»ºä»»åŠ¡æˆåŠŸ: {resultTask}");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"ReturnRemaining ESS命令发送失败: {ex.Message}");
                throw new Exception($"ESS系统通信失败: {ex.Message}");
            }
        }
        #endregion
        /// <summary>
        /// å›žåº“操作
        /// </summary>
        //public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason)
        //{
        //    try
        //    {
        //        _unitOfWorkManage.BeginTran();
        //        // èŽ·å–æ‰€æœ‰æœªåˆ†æ‹£çš„å‡ºåº“é”å®šè®°å½•ï¼ŒåŒ…æ‹¬æ‹†åŒ…äº§ç”Ÿçš„è®°å½•
        //        var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
        //            .Where(it => it.OrderNo == orderNo && it.Status == (int)OutLockStockStatusEnum.出库中)
        //            .ToListAsync();
        //        var stockinfo = _stockInfoService.Db.Queryable<Dt_StockInfo>().First(x => x.PalletCode == palletCode);
        //        var tasks = new List<Dt_Task>();
        //        // æŸ¥è¯¢ä»»åŠ¡è¡¨
        //        var task = remainingLocks.Any()
        //            ? _taskRepository.QueryData(x => x.TaskNum == remainingLocks.First().TaskNum).FirstOrDefault()
        //            : _taskRepository.QueryData(x => x.PalletCode == palletCode).FirstOrDefault();
        //        if (task == null)
        //        {
        //            return WebResponseContent.Instance.Error("未找到对应的任务信息");
        //        }
        //        // æ£€æŸ¥æ‰˜ç›˜ä¸Šæ˜¯å¦æœ‰å…¶ä»–非出库货物(库存货物)
        //        var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //            .Where(it => it.StockId == stockinfo.Id &&
        //                        (it.Status == StockStatusEmun.入库确认.ObjToInt() ||
        //                         it.Status == StockStatusEmun.入库完成.ObjToInt() ||
        //                         it.Status == StockStatusEmun.出库锁定.ObjToInt()))
        //            .Where(it => it.OutboundQuantity == 0 || it.OutboundQuantity < it.StockQuantity) // æœªå®Œå…¨å‡ºåº“çš„
        //            .ToListAsync();
        //        // æ£€æŸ¥æ‹†åŒ…记录,找出需要回库的条码
        //        var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
        //            .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && !it.IsReverted)
        //            .ToListAsync();
        //        // è®¡ç®—需要回库的拆包条码
        //        var splitBarcodesToReturn = new List<string>();
        //        foreach (var splitRecord in splitRecords)
        //        {
        //            // æ£€æŸ¥åŽŸæ¡ç æ˜¯å¦è¿˜æœ‰åº“å­˜éœ€è¦å›žåº“
        //            var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //                .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockinfo.Id)
        //                .FirstAsync();
        //            if (originalStock != null && originalStock.StockQuantity > 0)
        //            {
        //                splitBarcodesToReturn.Add(splitRecord.OriginalBarcode);
        //            }
        //            // æ£€æŸ¥æ–°æ¡ç æ˜¯å¦è¿˜æœ‰åº“存需要回库
        //            var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //                .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockinfo.Id)
        //                .FirstAsync();
        //            if (newStock != null && newStock.StockQuantity > 0)
        //            {
        //                splitBarcodesToReturn.Add(splitRecord.NewBarcode);
        //            }
        //        }
        //        // å¦‚果没有需要回库的货物(既无未分拣出库货物,也无其他库存货物,也无拆包剩余货物)
        //        if (!remainingLocks.Any() && !palletStockGoods.Any() && !splitBarcodesToReturn.Any())
        //        {
        //            // æ£€æŸ¥æ˜¯å¦æ‰€æœ‰è´§ç‰©éƒ½å·²æ‹£é€‰å®Œæˆ
        //            var allPicked = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
        //                .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode)
        //                .AnyAsync(it => it.Status == (int)OutLockStockStatusEnum.拣选完成);
        //            if (allPicked)
        //            {
        //                return WebResponseContent.Instance.OK("所有货物已拣选完成,托盘为空");
        //            }
        //            else
        //            {
        //                return WebResponseContent.Instance.Error("没有需要回库的剩余货物");
        //            }
        //        }
        //        var firstlocation = _locationInfoService.Db.Queryable<Dt_LocationInfo>().First(x => x.LocationCode == task.SourceAddress);
        //        decimal totalReturnQty = 0;
        //        // æƒ…况1:处理未分拣的出库锁定记录
        //        if (remainingLocks.Any(x => x.PalletCode == palletCode))
        //        {
        //            var palletLocks = remainingLocks.Where(x => x.PalletCode == palletCode).ToList();
        //            totalReturnQty = palletLocks.Sum(x => x.AssignQuantity - x.PickedQty);
        //            if (totalReturnQty > 0)
        //            {
        //                // åˆ†é…æ–°è´§ä½
        //                var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
        //                // æ›´æ–°å‡ºåº“锁定记录状态
        //                var lockIds = palletLocks.Select(x => x.Id).ToList();
        //                await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
        //                    .SetColumns(it => new Dt_OutStockLockInfo { Status = (int)OutLockStockStatusEnum.回库中 })
        //                    .Where(it => lockIds.Contains(it.Id))
        //                    .ExecuteCommandAsync();
        //                // å¤„理库存记录
        //                foreach (var lockInfo in palletLocks)
        //                {
        //                    decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
        //                    // æ£€æŸ¥åº“存记录是否存在
        //                    var existingStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //                        .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
        //                        .FirstAsync();
        //                    if (existingStock != null)
        //                    {
        //                        // åº“存记录存在,恢复锁定数量
        //                        existingStock.OutboundQuantity = 0;
        //                        await _stockInfoDetailService.Db.Updateable(existingStock).ExecuteCommandAsync();
        //                    }
        //                    else
        //                    {
        //                        // åº“存记录不存在(可能是拆包产生的新条码),创建新的库存记录
        //                        var newStockDetail = new Dt_StockInfoDetail
        //                        {
        //                            StockId = lockInfo.StockId,
        //                            MaterielCode = lockInfo.MaterielCode,
        //                            MaterielName = lockInfo.MaterielName,
        //                            OrderNo = lockInfo.OrderNo,
        //                            BatchNo = lockInfo.BatchNo,
        //                            StockQuantity = returnQty,
        //                            OutboundQuantity = 0,
        //                            Barcode = lockInfo.CurrentBarcode,
        //                            InboundOrderRowNo = "",
        //                            Status = StockStatusEmun.入库完成.ObjToInt(),
        //                            SupplyCode = lockInfo.SupplyCode,
        //                            WarehouseCode = lockInfo.WarehouseCode,
        //                            Unit = lockInfo.Unit
        //                        };
        //                        await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
        //                    }
        //                }
        //                // åˆ›å»ºå›žåº“任务
        //                CreateReturnTask(tasks, task, palletCode, newLocation);
        //            }
        //        }
        //        // æƒ…况2:处理拆包剩余的库存货物
        //        if (splitBarcodesToReturn.Any())
        //        {
        //            decimal splitReturnQty = 0;
        //            foreach (var barcode in splitBarcodesToReturn)
        //            {
        //                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
        //                    .Where(it => it.Barcode == barcode && it.StockId == stockinfo.Id)
        //                    .FirstAsync();
        //                if (stockDetail != null && stockDetail.StockQuantity > 0)
        //                {
        //                    splitReturnQty += stockDetail.StockQuantity;
        //                    // æ¢å¤åº“存状态为入库完成
        //                    stockDetail.OutboundQuantity = 0;
        //                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
        //                }
        //            }
        //            totalReturnQty += splitReturnQty;
        //            // å¦‚果没有创建任务,创建回库任务
        //            if (!tasks.Any())
        //            {
        //                var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
        //                CreateReturnTask(tasks, task, palletCode, newLocation);
        //            }
        //        }
        //        // æƒ…况3:出库货物已分拣完,但托盘上还有其他库存货物需要回库
        //        if (palletStockGoods.Any() && !remainingLocks.Any(x => x.PalletCode == palletCode))
        //        {
        //            decimal otherReturnQty = palletStockGoods.Sum(x => x.StockQuantity - x.OutboundQuantity);
        //            totalReturnQty += otherReturnQty;
        //            // æ›´æ–°è¿™äº›åº“存货物的状态
        //            foreach (var stockGood in palletStockGoods)
        //            {
        //                stockGood.OutboundQuantity = 0;
        //                await _stockInfoDetailService.Db.Updateable(stockGood).ExecuteCommandAsync();
        //            }
        //            // å¦‚果没有创建任务,创建回库任务
        //            if (!tasks.Any())
        //            {
        //                var newLocation = _locationInfoService.AssignLocation(firstlocation.LocationType);
        //                CreateReturnTask(tasks, task, palletCode, newLocation);
        //            }
        //        }
        //        var allSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
        //            .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && !it.IsReverted)
        //            .ToListAsync();
        //        foreach (var record in allSplitRecords)
        //        {
        //            record.Status = (int)SplitPackageStatusEnum.已回库;
        //            await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
        //        }
        //        // ä¿å­˜ä»»åŠ¡ ç»™ESS下发任务
        //        if (tasks.Any())
        //        {
        //            try
        //            {
        //                await _taskRepository.Db.Insertable(tasks).ExecuteCommandAsync();
        //                var targetAddress = task.TargetAddress;
        //                _taskRepository.DeleteData(task);
        //                // ç»™ ESS æµåŠ¨ä¿¡å·å’Œåˆ›å»ºä»»åŠ¡
        //                try
        //                {
        //                    var result = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest
        //                    {
        //                        slotCode = movestations[targetAddress],
        //                        containerCode = palletCode
        //                    });
        //                    if (result)
        //                    {
        //                        TaskModel esstask = new TaskModel()
        //                        {
        //                            taskType = "putaway",
        //                            taskGroupCode = "",
        //                            groupPriority = 0,
        //                            tasks = new List<TasksType>
        //                    {
        //                        new()
        //                        {
        //                            taskCode = tasks.First().TaskNum.ToString(),
        //                            taskPriority = 0,
        //                            taskDescribe = new TaskDescribeType {
        //                                containerCode = palletCode,
        //                                containerType = "CT_KUBOT_STANDARD",
        //                                fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "",
        //                                toStationCode = "",
        //                                toLocationCode = tasks.First().TargetAddress,
        //                                deadline = 0, storageTag = ""
        //                            }
        //                        }
        //                    }
        //                        };
        //                        var resulttask = await _eSSApiService.CreateTaskAsync(esstask);
        //                        _logger.LogInformation("ReturnRemaining åˆ›å»ºä»»åŠ¡è¿”å›ž:  " + resulttask);
        //                    }
        //                }
        //                catch (Exception ex)
        //                {
        //                    _logger.LogInformation("ReturnRemaining åˆ›å»ºä»»åŠ¡è¿”å›ž catch err:  " + ex.Message);
        //                }
        //                _unitOfWorkManage.CommitTran();
        //                return WebResponseContent.Instance.OK($"回库操作成功,共回库数量:{totalReturnQty}");
        //            }
        //            catch (Exception ex)
        //            {
        //                _unitOfWorkManage.RollbackTran();
        //                return WebResponseContent.Instance.Error($"创建回库任务失败: {ex.Message}");
        //            }
        //        }
        //        _unitOfWorkManage.RollbackTran();
        //        return WebResponseContent.Instance.Error("未创建任何回库任务");
        //    }
        //    catch (Exception ex)
        //    {
        //        _unitOfWorkManage.RollbackTran();
        //        return WebResponseContent.Instance.Error($"回库操作失败: {ex.Message}");
        //    }
        //}
        /// <summary>
        /// åˆ›å»ºå›žåº“任务
        /// </summary>
        private void CreateReturnTask(List<Dt_Task> tasks, Dt_Task originalTask, string palletCode, Dt_LocationInfo newLocation)
        {
            Dt_Task newTask = new()
            {
                CurrentAddress = stations[originalTask.TargetAddress],
                Grade = 0,
                PalletCode = palletCode,
                NextAddress = "",
                OrderNo = originalTask.OrderNo,
                Roadway = newLocation.RoadwayNo,
                SourceAddress = stations[originalTask.TargetAddress],
                TargetAddress = newLocation.LocationCode,
                TaskStatus = TaskStatusEnum.New.ObjToInt(),
                TaskType = TaskTypeEnum.InPick.ObjToInt(),
                PalletType = originalTask.PalletType,
                WarehouseId = originalTask.WarehouseId,
            };
            tasks.Add(newTask);
        }
        /// <summary>
        /// æ£€æŸ¥æ‰˜ç›˜æ˜¯å¦éœ€è¦å›žåº“的辅助方法
        /// </summary>
        public async Task<bool> CheckPalletNeedReturn(string orderNo, string palletCode)
        {
            // 1. æ£€æŸ¥æ˜¯å¦æœ‰æœªåˆ†æ‹£çš„出库记录
            var hasUnpickedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && it.Status == 1)
                .AnyAsync();
            if (hasUnpickedLocks)
                return true;
            // 2. æ£€æŸ¥å‡ºåº“是否已完成但托盘还有库存货物
            var outboundFinished = !await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.PalletCode == palletCode && it.Status == 1)
                .AnyAsync();
            var stockinfo = _stockInfoService.Db.Queryable<Dt_StockInfo>().First(x => x.PalletCode == palletCode);
            var hasRemainingGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.StockId == stockinfo.Id && it.Status == StockStatusEmun.入库确认.ObjToInt())
                .Where(it => it.OutboundQuantity == 0 || it.OutboundQuantity < it.StockQuantity)
                .AnyAsync();
            return outboundFinished && hasRemainingGoods;
        }
        /// <summary>
        /// èŽ·å–æ‹†åŒ…é“¾ï¼ˆä»Žå½“å‰æ¡ç è¿½æº¯åˆ°åŽŸå§‹æ¡ç ï¼‰
        /// </summary>
        // åœ¨ GetSplitChain æ–¹æ³•中添加更严格的验证
        private async Task<List<SplitChainItem>> GetSplitChain(string currentBarcode)
        {
            var chain = new List<SplitChainItem>();
            var visited = new HashSet<string>();
            string current = currentBarcode;
            int maxDepth = 10; // é˜²æ­¢æ— é™å¾ªçޝ
            while (!string.IsNullOrEmpty(current) && maxDepth > 0)
            {
                maxDepth--;
                if (visited.Contains(current))
                {
                    _logger.LogWarning($"检测到循环引用在拆包链中: {current}");
                    break;
                }
                visited.Add(current);
                var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(it => it.NewBarcode == current && !it.IsReverted)
                    .FirstAsync();
                if (splitRecord == null)
                    break;
                // éªŒè¯æ‹†åŒ…记录的完整性
                if (string.IsNullOrEmpty(splitRecord.OriginalBarcode))
                {
                    _logger.LogError($"拆包记录 {splitRecord.Id} ç¼ºå°‘原始条码");
                    break;
                }
                var item = new SplitChainItem
                {
                    SplitRecord = splitRecord,
                    OriginalBarcode = splitRecord.OriginalBarcode,
                    NewBarcode = splitRecord.NewBarcode,
                    SplitQuantity = splitRecord.SplitQty
                };
                chain.Add(item);
                current = splitRecord.OriginalBarcode;
            }
            if (maxDepth <= 0)
            {
                _logger.LogWarning($"拆包链追溯达到最大深度: {currentBarcode}");
            }
            chain.Reverse();
            return chain;
        }
        /// <summary>
        /// å¤„理拆包链的取消分拣
        /// </summary>
        private async Task HandleSplitChainCancel(string orderNo, string palletCode, string barcode,
            decimal cancelQty, Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, List<SplitChainItem> splitChain)
        {
            if (!splitChain.Any())
                return;
            //  æ‰¾åˆ°åŽŸå§‹æ¡ç ï¼ˆé“¾çš„ç¬¬ä¸€ä¸ªï¼‰
            var originalSplitItem = splitChain.First();
            var originalBarcode = originalSplitItem.OriginalBarcode;
            // æŸ¥æ‰¾åŽŸå§‹æ¡ç çš„é”å®šä¿¡æ¯å’Œåº“å­˜
            var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.CurrentBarcode == originalBarcode && it.Status == (int)OutLockStockStatusEnum.出库中)
                .FirstAsync();
            var originalStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.Barcode == originalBarcode && it.StockId == originalLockInfo.StockId)
                .FirstAsync();
            if (originalLockInfo == null || originalStockDetail == null)
                throw new Exception("未找到原始条码的锁定信息或库存信息");
            // æ¢å¤åŽŸå§‹æ¡ç åº“å­˜ï¼ˆå°†å–æ¶ˆçš„æ•°é‡åŠ å›žåŽ»ï¼‰
            originalStockDetail.StockQuantity += cancelQty;
            originalStockDetail.OutboundQuantity += cancelQty;
            await _stockInfoDetailService.Db.Updateable(originalStockDetail).ExecuteCommandAsync();
            // æ¢å¤åŽŸå§‹æ¡ç é”å®šä¿¡æ¯
            originalLockInfo.AssignQuantity += cancelQty;
            await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync();
            //  åˆ é™¤æ‹†åŒ…链中所有新条码的锁定信息和库存记录
            var allNewBarcodes = splitChain.Select(x => x.NewBarcode).ToList();
            // åˆ é™¤é”å®šä¿¡æ¯
            await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                .Where(it => allNewBarcodes.Contains(it.CurrentBarcode))
                .ExecuteCommandAsync();
            // åˆ é™¤åº“存记录(只删除拆包产生的新条码库存,保留原始条码)
            await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
                .Where(it => allNewBarcodes.Contains(it.Barcode) && it.Barcode != originalBarcode)
                .ExecuteCommandAsync();
            // æ›´æ–°æ‹†åŒ…链中所有拆包记录状态为已拆包
            foreach (var chainItem in splitChain)
            {
                chainItem.SplitRecord.Status = (int)SplitPackageStatusEnum.已拆包;
                await _splitPackageService.Db.Updateable(chainItem.SplitRecord).ExecuteCommandAsync();
            }
            //  æ¢å¤è®¢å•明细拣选数量(使用原始锁定信息的订单明细ID)
            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => it.PickedQty == it.PickedQty - cancelQty)
                .Where(it => it.Id == originalLockInfo.OrderDetailId)
                .ExecuteCommandAsync();
            //   æ¢å¤è®¢å•状态
            await CheckAndRevertOrderStatus(orderNo);
            //  åˆ é™¤æ‹£é€‰è®°å½•
            await Db.Deleteable<Dt_PickingRecord>()
                .Where(it => it.Id == pickingRecord.Id)
                .ExecuteCommandAsync();
            ////  è®°å½•取消操作历史
            //await RecordCancelHistory(orderNo, palletCode, barcode, cancelQty, pickingRecord.Id,
            //    lockInfo.MaterielCode, "取消拆包链分拣");
        }
        /// <summary>
        /// å¤„理普通条码的取消分拣
        /// </summary>
        private async Task HandleNormalBarcodeCancel(string orderNo, string palletCode, string barcode,
            decimal cancelQty, Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord)
        {
            // 1. æŸ¥æ‰¾åº“存信息
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.Barcode == barcode && it.StockId == lockInfo.StockId)
                .FirstAsync();
            if (stockDetail == null)
                throw new Exception("未找到对应的库存信息");
            // 2. æ¢å¤åº“存数量
            if (stockDetail.StockQuantity == 0)
            {
                // æ•´åŒ…出库的情况
                stockDetail.StockQuantity = cancelQty;
                stockDetail.OutboundQuantity = cancelQty;
            }
            else
            {
                // éƒ¨åˆ†å‡ºåº“的情况
                stockDetail.StockQuantity += cancelQty;
                stockDetail.OutboundQuantity += cancelQty;
            }
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            // 3. æ¢å¤é”å®šä¿¡æ¯çŠ¶æ€
            lockInfo.AssignQuantity += cancelQty;
            lockInfo.PickedQty -= cancelQty;
            if (lockInfo.PickedQty == 0)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            }
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // 4. å¤„理相关的拆包记录状态恢复
            var relatedSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(it => it.OriginalBarcode == barcode &&
                           it.Status == (int)SplitPackageStatusEnum.已拣选)
                .ToListAsync();
            foreach (var record in relatedSplitRecords)
            {
                record.Status = (int)SplitPackageStatusEnum.已拆包;
                await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
            }
            // 5. æ¢å¤è®¢å•明细的拣选数量
            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => it.PickedQty == it.PickedQty - cancelQty)
                .Where(it => it.Id == lockInfo.OrderDetailId)
                .ExecuteCommandAsync();
            // 6. æ¢å¤è®¢å•状态
            await CheckAndRevertOrderStatus(orderNo);
            // 7. åˆ é™¤æ‹£é€‰è®°å½•
            await Db.Deleteable<Dt_PickingRecord>().Where(it => it.Id == pickingRecord.Id).ExecuteCommandAsync();
            //// 8. è®°å½•取消操作历史
            //await RecordCancelHistory(orderNo, palletCode, barcode, cancelQty, pickingRecord.Id,
            //    lockInfo.MaterielCode, "取消分拣");
        }
        /// <summary>
        /// æ£€æŸ¥å¹¶æ¢å¤è®¢å•状态
        /// </summary>
        private async Task CheckAndRevertOrderStatus(string orderNo)
        {
            var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                .Where(x => x.OrderNo == orderNo)
                .FirstAsync();
            if (order != null && order.OrderStatus == OutOrderStatusEnum.出库完成.ObjToInt())
            {
                await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                    .SetColumns(x => x.OrderStatus == OutOrderStatusEnum.出库中.ObjToInt())
                    .Where(x => x.OrderNo == orderNo)
                    .ExecuteCommandAsync();
            }
        }
        #region æŸ¥è¯¢æ–¹æ³•
        // èŽ·å–æœªæ‹£é€‰åˆ—è¡¨
        public async Task<List<Dt_OutStockLockInfo>> GetUnpickedList(string orderNo, string palletCode)
        {
@@ -1789,201 +161,2650 @@
            return summary;
        }
        #endregion
        #region æ ¸å¿ƒä¸šåŠ¡æµç¨‹
        /// <summary>
        /// èŽ·å–æ‹£é€‰åŽ†å²
        /// æ‹£é€‰
        /// </summary>
        public async Task<List<Dt_PickingRecord>> GetPickingHistory(int orderId)
        {
            // é€šè¿‡å‡ºåº“单ID查询相关的拣选历史
            // æ³¨æ„ï¼šDt_PickingRecord ä¸­æ²¡æœ‰ç›´æŽ¥å­˜å‚¨OrderId,需要通过出库单明细关联
            var detailIds = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .Where(d => d.OrderId == orderId)
                .Select(d => d.Id)
                .ToListAsync();
            return await Db.Queryable<Dt_PickingRecord>()
                .Where(p => detailIds.Contains(p.OrderDetailId))
                .OrderByDescending(p => p.PickTime)
                .ToListAsync();
        }
        /// <summary>
        /// èŽ·å–æ‰˜ç›˜çš„å‡ºåº“çŠ¶æ€ä¿¡æ¯
        /// </summary>
        public async Task<WebResponseContent> GetPalletOutboundStatus(string palletCode)
        {
            // èŽ·å–æ‰˜ç›˜çš„é”å®šä¿¡æ¯
            var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.PalletCode == palletCode)
                .ToListAsync();
            // èŽ·å–æ‰˜ç›˜åº“å­˜ä¿¡æ¯
            var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                .Includes(x => x.Details)
                .Where(x => x.PalletCode == palletCode)
                .FirstAsync();
            if (stockInfo == null)
                return WebResponseContent.Instance.Error("未找到托盘信息");
            // è®¡ç®—各种数量
            var totalStockQuantity = stockInfo.Details.Sum(x => x.StockQuantity);
            var totalOutboundQuantity = stockInfo.Details.Sum(x => x.OutboundQuantity);
            var totalLockedQuantity = lockInfos.Where(x => x.Status == (int)OutLockStockStatusEnum.出库中)
                .Sum(x => x.AssignQuantity - x.PickedQty);
            var totalPickedQuantity = lockInfos.Sum(x => x.PickedQty);
            var result = new
            {
                PalletCode = palletCode,
                LocationCode = stockInfo.LocationCode,
                StockStatus = stockInfo.StockStatus,
                TotalStockQuantity = totalStockQuantity,
                TotalOutboundQuantity = totalOutboundQuantity,
                TotalLockedQuantity = totalLockedQuantity,
                TotalPickedQuantity = totalPickedQuantity,
                AvailableQuantity = totalStockQuantity - totalOutboundQuantity,
                LockInfos = lockInfos.Select(x => new
                {
                    x.Id,
                    x.MaterielCode,
                    x.OrderDetailId,
                    x.AssignQuantity,
                    x.PickedQty,
                    x.Status,
                    x.CurrentBarcode,
                    x.IsSplitted
                }).ToList(),
                StockDetails = stockInfo.Details.Select(x => new
                {
                    x.Barcode,
                    x.MaterielCode,
                    StockQuantity = x.StockQuantity,
                    OutboundQuantity = x.OutboundQuantity,
                    AvailableQuantity = x.StockQuantity - x.OutboundQuantity
                }).ToList()
            };
            return WebResponseContent.Instance.OK(null, result);
        }
        /// <summary>
        /// ç›´æŽ¥å‡ºåº“ - æ•´ä¸ªæ‰˜ç›˜å‡ºåº“,清空库存
        /// </summary>
        public async Task<WebResponseContent> DirectOutbound(DirectOutboundRequest request)
        /// <param name="orderNo"></param>
        /// <param name="palletCode"></param>
        /// <param name="barcode"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> ConfirmPicking(string orderNo, string palletCode, string barcode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                    .Includes(x => x.Details)
                    .Where(x => x.PalletCode == request.PalletCode).FirstAsync();
                var validationResult = await ValidatePickingRequest(orderNo, palletCode, barcode);
                if (!validationResult.IsValid)
                    return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
                if (stockInfo == null)
                    return WebResponseContent.Instance.Error("未找到托盘库存信息");
                var (lockInfo, orderDetail, stockDetail) = validationResult.Data;
                // è®¡ç®—实际拣选数量
                var quantityResult = await CalculateActualPickingQuantity(lockInfo, orderDetail, stockDetail);
                if (!quantityResult.IsValid)
                    return WebResponseContent.Instance.Error(quantityResult.ErrorMessage);
                var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == request.OrderNo && x.PalletCode == request.PalletCode)
                    .ToListAsync();
                var (actualQty, adjustedReason) = quantityResult.Data;
                foreach (var lockInfo in lockInfos)
                var overPickingValidation = await ValidateOverPicking(orderDetail.Id, actualQty);
                if (!overPickingValidation.IsValid)
                {
                    if (lockInfo.Status == (int)OutLockStockStatusEnum.出库中)
                    {
                        lockInfo.PickedQty = lockInfo.AssignQuantity;
                    }
                    lockInfo.Status = (int)OutLockStockStatusEnum.已出库;
                    await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
                    var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .Where(x => x.Id == lockInfo.OrderDetailId)
                    .FirstAsync();
                    if (orderDetail != null)
                    {
                        orderDetail.OverOutQuantity += lockInfo.PickedQty;
                        orderDetail.LockQuantity -= lockInfo.PickedQty;
                        orderDetail.OrderDetailStatus = (int)OrderDetailStatusEnum.Over;
                        orderDetail.LockQuantity = 0;
                        await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                    }
                }
                var groupDetails = lockInfos.GroupBy(x => x.OrderDetailId).Select(x => new
                {
                    OrderDetailId = x.Key,
                    TotalQuantity = x.Sum(o => o.PickedQty)
                }).ToList();
                foreach (var item in groupDetails)
                {
                    var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().Where(x => x.Id == item.OrderDetailId).FirstAsync();
                    if (orderDetail != null)
                    {
                        orderDetail.OverOutQuantity = item.TotalQuantity;
                        orderDetail.LockQuantity = 0;
                        orderDetail.OrderDetailStatus = (int)OrderDetailStatusEnum.Over;
                        await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync();
                    }
                    return WebResponseContent.Instance.Error(overPickingValidation.ErrorMessage);
                }
                await CheckAndUpdateOrderStatus(request.OrderNo);
                // æ‰§è¡Œåˆ†æ‹£é€»è¾‘(只处理库存和锁定,不处理订单)
                var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, orderNo, palletCode, barcode, actualQty);
                var lockInfoIds = lockInfos.Select(x => x.Id).ToList();
                var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                    .Where(x => lockInfoIds.Contains(x.OutStockLockInfoId) &&
                               x.Status == (int)SplitPackageStatusEnum.已拆包)
                    .ToListAsync();
                // ç»Ÿä¸€æ›´æ–°è®¢å•数据(所有分支都在这里更新)
                await UpdateOrderRelatedData(orderDetail.Id, pickingResult.ActualPickedQty, orderNo);
                foreach (var record in splitRecords)
                {
                    record.Status = (int)SplitPackageStatusEnum.已拣选;
                    await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
                }
                var location = await _locationInfoService.Db.Queryable<Dt_LocationInfo>()
                    .Where(x => x.LocationCode == stockInfo.LocationCode)
                    .FirstAsync();
                if (location != null)
                {
                    location.LocationStatus = (int)LocationStatusEnum.Free;
                    await _locationInfoService.Db.Updateable(location).ExecuteCommandAsync();
                }
                foreach (var detail in stockInfo.Details)
                {
                    await _stockInfoDetailService.Db.Deleteable(detail).ExecuteCommandAsync();
                }
                await _stockInfoService.Db.Deleteable(stockInfo).ExecuteCommandAsync();
                // è®°å½•操作历史
                await RecordPickingHistory(pickingResult, orderNo, palletCode);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK("直接出库成功");
                return CreatePickingResponse(pickingResult, adjustedReason);
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error($"直接出库失败: {ex.Message}");
                _logger.LogError($"ConfirmPicking失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {barcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"拣选确认失败:{ex.Message}");
            }
        }
        /// <summary>
        /// å–消拣选
        /// </summary>
        /// <param name="orderNo"></param>
        /// <param name="palletCode"></param>
        /// <param name="barcode"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> CancelPicking(string orderNo, string palletCode, string barcode)
        {
            try
            {
                if (await IsPalletReturned(palletCode))
                {
                    return WebResponseContent.Instance.Error($"托盘{palletCode}已经回库,不能取消分拣");
                }
                _unitOfWorkManage.BeginTran();
                // å‰ç½®éªŒè¯
                var validationResult = await ValidateCancelRequest(orderNo, palletCode, barcode);
                if (!validationResult.IsValid)
                    return WebResponseContent.Instance.Error(validationResult.ErrorMessage);
                var (pickingRecord, lockInfo, orderDetail) = validationResult.Data;
                //执行取消逻辑
                await ExecuteCancelLogic(lockInfo, pickingRecord, orderDetail, orderNo);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK($"取消分拣成功");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"CancelPicking失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {barcode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"取消分拣失败:{ex.Message}");
            }
        }
        /// <summary>
        /// å›žåº“
        /// </summary>
        /// <param name="orderNo"></param>
        /// <param name="palletCode"></param>
        /// <param name="reason"></param>
        /// <returns></returns>
        public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode))
                    return WebResponseContent.Instance.Error("订单号和托盘码不能为空");
                //  èŽ·å–åº“å­˜å’Œä»»åŠ¡ä¿¡æ¯
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>().FirstAsync(x => x.PalletCode == palletCode);
                if (stockInfo == null)
                    return WebResponseContent.Instance.Error($"未找到托盘 {palletCode} å¯¹åº”的库存信息");
                var task = await GetCurrentTask(orderNo, palletCode);
                if (task == null)
                    return WebResponseContent.Instance.Error("未找到对应的任务信息");
                //分析需要回库的货物
                //var returnAnalysis = await AnalyzeReturnItems(orderNo, palletCode, stockInfo.Id);
                //if (!returnAnalysis.HasItemsToReturn)
                //    return await HandleNoReturnItems(orderNo, palletCode, task);
                var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id);
                if (!statusAnalysis.HasItemsToReturn)
                    return await HandleNoReturnItems(orderNo, palletCode, task, stockInfo.Id);
                // 4. æ£€æŸ¥æ˜¯å¦æœ‰è¿›è¡Œä¸­çš„任务
                if (statusAnalysis.HasActiveTasks)
                {
                    return WebResponseContent.Instance.Error($"托盘 {palletCode} æœ‰è¿›è¡Œä¸­çš„任务,不能执行回库操作");
                }
                //执行回库操作
                await ExecuteReturnOperations(orderNo, palletCode, stockInfo, task, statusAnalysis);
                await ReleaseAllLocksForReallocation(orderNo, palletCode, statusAnalysis);
                _unitOfWorkManage.CommitTran();
                // åˆ›å»ºå›žåº“任务
                await CreateReturnTaskAndHandleESS(orderNo, palletCode, task, TaskTypeEnum.InPick, task.PalletType);
                // æ›´æ–°è®¢å•状态(不触发MES回传)
                await UpdateOrderStatusForReturn(orderNo);
                return WebResponseContent.Instance.OK($"回库操作成功");
                //return WebResponseContent.Instance.OK($"回库操作成功,共回库数量:{statusAnalysis.TotalReturnQty}");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"ReturnRemaining失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"回库操作失败: {ex.Message}");
            }
        }
        /// <summary>
        /// ç©ºæ‰˜ç›˜å–走接口(带订单号)
        /// éªŒè¯æ‰˜ç›˜æ˜¯å¦çœŸçš„为空,清理数据,更新订单状态,创建取托盘任务
        /// </summary>
        public async Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode)
        {
            try
            {
                _unitOfWorkManage.BeginTran();
                if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode))
                    return WebResponseContent.Instance.Error("订单号和托盘码不能为空");
                // æ£€æŸ¥è®¢å•是否存在
                var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                    .Where(x => x.OrderNo == orderNo)
                    .FirstAsync();
                if (order == null)
                    return WebResponseContent.Instance.Error($"未找到订单 {orderNo}");
                //检查托盘是否存在且属于该订单
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                    .Where(x => x.PalletCode == palletCode)
                    .FirstAsync();
                if (stockInfo == null)
                    return WebResponseContent.Instance.Error($"未找到托盘 {palletCode} å¯¹åº”的库存信息");
                var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id);
                if (!statusAnalysis.CanRemove)
                {
                    if (!statusAnalysis.IsEmptyPallet)
                    {
                        return WebResponseContent.Instance.Error($"托盘 {palletCode} ä¸Šè¿˜æœ‰è´§ç‰©ï¼Œä¸èƒ½å–èµ°");
                    }
                    if (statusAnalysis.HasActiveTasks)
                    {
                        return WebResponseContent.Instance.Error($"托盘 {palletCode} è¿˜æœ‰è¿›è¡Œä¸­çš„任务,不能取走");
                    }
                }
                // æ¸…理零库存数据
                await CleanupZeroStockData(stockInfo.Id);
                // åˆ é™¤æˆ–取消相关任务
                await HandleTaskCleanup(orderNo, palletCode);
                // æ›´æ–°è®¢å•相关数据
                await UpdateOrderData(orderNo, palletCode);
                _unitOfWorkManage.CommitTran();
                _logger.LogInformation($"空托盘取走操作成功 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, æ“ä½œäºº: {App.User.UserName}");
                return WebResponseContent.Instance.OK("空托盘取走操作成功");
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"RemoveEmptyPallet失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
                return WebResponseContent.Instance.Error($"空托盘取走失败: {ex.Message}");
            }
        }
        #endregion
        #region åˆ†æ‹£ç¡®è®¤ç§æœ‰æ–¹æ³•
        private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>> ValidatePickingRequest(string orderNo, string palletCode, string barcode)
        {
            // 1. åŸºç¡€å‚数验证
            if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode) || string.IsNullOrEmpty(barcode))
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error("订单号、托盘码和条码不能为空");
            // 2. æŸ¥æ‰¾æœ‰æ•ˆçš„锁定信息
            var lockInfo = await FindValidLockInfo(orderNo, palletCode, barcode);
            if (lockInfo == null)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"未找到有效的锁定信息");
            // 3. æ£€æŸ¥è®¢å•状态
            var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                .Where(x => x.OrderNo == orderNo)
                .FirstAsync();
            if (order?.OrderStatus == (int)OutOrderStatusEnum.出库完成)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"订单{orderNo}已完成,不能继续分拣");
            // 4. èŽ·å–è®¢å•æ˜Žç»†
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
            if (orderDetail == null)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"未找到订单明细");
            // 5. æ£€æŸ¥è®¢å•明细数量
            if (orderDetail.OverOutQuantity >= orderDetail.NeedOutQuantity)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"订单明细需求数量已满足");
            // 6. èŽ·å–åº“å­˜æ˜Žç»†
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == barcode && x.StockId == lockInfo.StockId &&
                   x.Status != StockStatusEmun.入库确认.ObjToInt())
                .FirstAsync();
            if (stockDetail == null)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"无效的条码或物料编码");
            // 7. æ£€æŸ¥åº“存状态和数量
            if (stockDetail.StockQuantity <= 0)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"条码{barcode}库存不足");
            if (stockDetail.Status != StockStatusEmun.出库锁定.ObjToInt())
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"条码{barcode}状态不正确,无法分拣");
            // 8. æ£€æŸ¥æ˜¯å¦é‡å¤åˆ†æ‹£
            var existingPicking = await Db.Queryable<Dt_PickingRecord>()
                .Where(x => x.Barcode == barcode && x.OrderNo == orderNo && x.PalletCode == palletCode && x.OutStockLockId == lockInfo.Id)
                .FirstAsync();
            if (existingPicking != null)
                return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Error($"条码{barcode}已经分拣过");
            return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail)>.Success((lockInfo, orderDetail, stockDetail));
        }
        private async Task<Dt_OutStockLockInfo> FindValidLockInfo(string orderNo, string palletCode, string barcode)
        {
            // ä¼˜å…ˆæŸ¥æ‰¾ç²¾ç¡®åŒ¹é…çš„记录
            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.OrderNo == orderNo &&
                           it.Status == (int)OutLockStockStatusEnum.出库中 &&
                           it.PalletCode == palletCode &&
                           it.CurrentBarcode == barcode &&
                           it.AssignQuantity > it.PickedQty).FirstAsync();
            if (lockInfo == null)
            {
                // æŸ¥æ‰¾åŒä¸€è®¢å•下的记录
                lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(it => it.OrderNo == orderNo && it.CurrentBarcode == barcode && it.Status == (int)OutLockStockStatusEnum.出库中 && it.AssignQuantity > it.PickedQty).FirstAsync();
                if (lockInfo == null)
                {
                    // æ£€æŸ¥æ˜¯å¦å·²ç»å®Œæˆåˆ†æ‹£
                    var completedLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                        .Where(it => it.CurrentBarcode == barcode &&
                                   (it.Status == (int)OutLockStockStatusEnum.拣选完成 ||
                                    it.PickedQty >= it.AssignQuantity)).FirstAsync();
                    if (completedLockInfo != null)
                        throw new Exception($"条码{barcode}已经完成分拣,不能重复分拣");
                    else
                        return null;
                }
            }
            return lockInfo;
        }
        private async Task<ValidationResult<(decimal, string)>> CalculateActualPickingQuantity(
            Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, Dt_StockInfoDetail stockDetail)
        {
            decimal plannedQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
            decimal remainingOrderQty = orderDetail.NeedOutQuantity - orderDetail.OverOutQuantity;
            decimal stockQuantity = stockDetail.StockQuantity;
            if (plannedQty <= 0)
            {
                return ValidationResult<(decimal, string)>.Error($"计划拣选数量必须大于0,当前: {plannedQty}");
            }
            if (remainingOrderQty <= 0)
            {
                return ValidationResult<(decimal, string)>.Error($"订单剩余需求数量必须大于0,当前: {remainingOrderQty}");
            }
            if (stockQuantity <= 0)
            {
                return ValidationResult<(decimal, string)>.Error($"库存数量必须大于0,当前: {stockQuantity}");
            }
            // ä¸‰é‡æ£€æŸ¥ï¼šå–最小值
            decimal actualQty = plannedQty;
            string adjustedReason = null;
            if (plannedQty > remainingOrderQty)
            {
                actualQty = remainingOrderQty;
                adjustedReason = $"订单数量限制:从{plannedQty}调整为{actualQty}";
            }
            if (actualQty > stockQuantity)
            {
                actualQty = stockQuantity;
                adjustedReason = adjustedReason != null
                    ? $"{adjustedReason},库存数量限制:进一步调整为{actualQty}"
                    : $"库存数量限制:从{plannedQty}调整为{actualQty}";
            }
            if (actualQty <= 0)
            {
                return ValidationResult<(decimal, string)>.Error($"无法分拣:计算后的实际数量为{actualQty}");
            }
            decimal projectedOverOut = orderDetail.OverOutQuantity + actualQty;
            if (projectedOverOut > orderDetail.NeedOutQuantity)
            {
                // å¦‚果会超拣,调整为刚好满足需求的数量
                actualQty = orderDetail.NeedOutQuantity - orderDetail.OverOutQuantity;
                adjustedReason = adjustedReason != null
                    ? $"{adjustedReason},防超拣限制:最终调整为{actualQty}"
                    : $"防超拣限制:从{plannedQty}调整为{actualQty}";
            }
            if (adjustedReason != null)
            {
                _logger.LogWarning($"分拣数量调整:{adjustedReason},订单{orderDetail.NeedOutQuantity},已出库{orderDetail.OverOutQuantity},库存{stockQuantity}");
            }
            return ValidationResult<(decimal, string)>.Success((actualQty, adjustedReason));
        }
        /// <summary>
        /// ä¸“门验证是否会发生超拣
        /// </summary>
        private async Task<ValidationResult<bool>> ValidateOverPicking(int orderDetailId, decimal pickingQty)
        {
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == orderDetailId);
            if (orderDetail == null)
                return ValidationResult<bool>.Error("未找到订单明细");
            decimal projectedOverOut = orderDetail.OverOutQuantity + pickingQty;
            if (projectedOverOut > orderDetail.NeedOutQuantity)
            {
                return ValidationResult<bool>.Error(
                    $"分拣后将导致超拣:当前已出库{orderDetail.OverOutQuantity},本次分拣{pickingQty},合计{projectedOverOut},超过需求{orderDetail.NeedOutQuantity}");
            }
            return ValidationResult<bool>.Success(true);
        }
        private async Task<PickingResult> ExecutePickingLogic(
         Dt_OutStockLockInfo lockInfo, Dt_OutboundOrderDetail orderDetail, Dt_StockInfoDetail stockDetail,
         string orderNo, string palletCode, string barcode, decimal actualQty)
        {
            decimal stockQuantity = stockDetail.StockQuantity;
            var result = new PickingResult
            {
                FinalLockInfo = lockInfo,
                FinalBarcode = barcode,
                FinalStockId = stockDetail.Id,
                ActualPickedQty = actualQty
            };
            if (actualQty < stockQuantity)
            {
                await HandleSplitPacking(lockInfo, stockDetail, actualQty, stockQuantity, result);
                // æ‹†åŒ…场景返回实际拣选数量
                result.ActualPickedQty = actualQty;
            }
            else if (actualQty == stockQuantity)
            {
                await HandleFullPicking(lockInfo, stockDetail, actualQty, result);
                // æ•´åŒ…拣选返回实际拣选数量
                result.ActualPickedQty = actualQty;
            }
            else
            {
                await HandlePartialPicking(lockInfo, stockDetail, actualQty, stockQuantity, result);
                // éƒ¨åˆ†æ‹£é€‰è¿”回调整后的数量
                result.ActualPickedQty = result.ActualPickedQty; // å·²ç»åœ¨æ–¹æ³•内调整
            }
            return result;
        }
        private async Task HandleSplitPacking(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
       decimal actualQty, decimal stockQuantity, PickingResult result)
        {
            decimal remainingStockQty = stockQuantity - actualQty;
            // æ›´æ–°åŽŸæ¡ç åº“å­˜
            stockDetail.StockQuantity = remainingStockQty;
            stockDetail.OutboundQuantity = remainingStockQty;
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            // ç”Ÿæˆæ–°æ¡ç 
            string newBarcode = await GenerateNewBarcode();
            // åˆ›å»ºæ–°é”å®šä¿¡æ¯
            var newLockInfo = await CreateSplitLockInfo(lockInfo, actualQty, newBarcode);
            // è®°å½•拆包历史
            await RecordSplitHistory(lockInfo, stockDetail, actualQty, remainingStockQty, newBarcode);
            // æ›´æ–°åŽŸé”å®šä¿¡æ¯
            lockInfo.AssignQuantity = remainingStockQty;
            lockInfo.PickedQty = 0;
            lockInfo.Operator = App.User.UserName;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // è®¾ç½®ç»“æžœ
            result.FinalLockInfo = newLockInfo;
            result.FinalBarcode = newBarcode;
            result.SplitResults.AddRange(CreateSplitResults(lockInfo, actualQty, remainingStockQty, newBarcode, stockDetail.Barcode));
            _logger.LogInformation($"拆包分拣完成 - OrderDetailId: {lockInfo.OrderDetailId}, åˆ†æ‹£æ•°é‡: {actualQty}");
        }
        private async Task HandleFullPicking(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
            decimal actualQty, PickingResult result)
        {
            // 1. æ›´æ–°åº“å­˜
            stockDetail.StockQuantity = 0;
            stockDetail.OutboundQuantity = 0;
            stockDetail.Status = StockStatusEmun.出库完成.ObjToInt();
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            // 2. æ›´æ–°é”å®šä¿¡æ¯
            lockInfo.PickedQty += actualQty;
            lockInfo.Status = (int)OutLockStockStatusEnum.拣选完成;
            lockInfo.Operator = App.User.UserName;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
        }
        private async Task HandlePartialPicking(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
            decimal actualQty, decimal stockQuantity, PickingResult result)
        {
            decimal stockOutQty = stockQuantity;
            decimal remainingAssignQty = actualQty - stockQuantity;
            // 1. æ›´æ–°åº“å­˜
            stockDetail.StockQuantity = 0;
            stockDetail.OutboundQuantity = 0;
            stockDetail.Status = StockStatusEmun.出库完成.ObjToInt();
            await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
            // 2. æ›´æ–°é”å®šä¿¡æ¯
            lockInfo.PickedQty += stockOutQty;
            lockInfo.AssignQuantity = remainingAssignQty;
            lockInfo.Operator = App.User.UserName;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            // 3. æ›´æ–°æ‹†åŒ…记录状态
            await UpdateSplitRecordsStatus(stockDetail.Barcode);
            result.ActualPickedQty = stockOutQty;
        }
        private async Task UpdateOrderRelatedData(int orderDetailId, decimal pickedQty, string orderNo)
        {
            // èŽ·å–æœ€æ–°çš„è®¢å•æ˜Žç»†æ•°æ®
            var currentOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == orderDetailId);
            decimal newOverOutQuantity = currentOrderDetail.OverOutQuantity + pickedQty;
            decimal newPickedQty = currentOrderDetail.PickedQty + pickedQty;
            if (newOverOutQuantity > currentOrderDetail.NeedOutQuantity)
            {
                _logger.LogError($"防超拣检查失败 - OrderDetailId: {orderDetailId}, å·²å‡ºåº“: {newOverOutQuantity}, éœ€æ±‚: {currentOrderDetail.NeedOutQuantity}, æœ¬æ¬¡åˆ†æ‹£: {pickedQty}");
                decimal adjustedQty = currentOrderDetail.NeedOutQuantity - currentOrderDetail.OverOutQuantity;
                if (adjustedQty > 0)
                {
                    _logger.LogWarning($"自动调整分拣数量防止超拣:从{pickedQty}调整为{adjustedQty}");
                    newOverOutQuantity = currentOrderDetail.NeedOutQuantity;
                    newPickedQty = currentOrderDetail.PickedQty + adjustedQty;
                    pickedQty = adjustedQty; // æ›´æ–°å®žé™…拣选数量
                }
                else
                {
                    throw new Exception($"分拣后将导致已出库数量({newOverOutQuantity})超过订单需求数量({currentOrderDetail.NeedOutQuantity}),且无法自动调整");
                }
            }
            // æ›´æ–°è®¢å•明细数量和状态
            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => new Dt_OutboundOrderDetail
                {
                    PickedQty = newPickedQty,
                    OverOutQuantity = newOverOutQuantity,
                    OrderDetailStatus = newOverOutQuantity >= currentOrderDetail.NeedOutQuantity ?
                        OrderDetailStatusEnum.Over.ObjToInt() :
                        OrderDetailStatusEnum.Outbound.ObjToInt()
                })
                .Where(it => it.Id == orderDetailId)
                .ExecuteCommandAsync();
            // æ›´æ–°é”å®šæ•°é‡
            await UpdateOrderDetailLockQuantity(orderDetailId);
            // æ£€æŸ¥å¹¶æ›´æ–°è®¢å•状态
            await CheckAndUpdateOrderStatus(orderNo);
        }
        private async Task RecordPickingHistory(PickingResult result, string orderNo, string palletCode)
        {
            var task = await _taskRepository.Db.Queryable<Dt_Task>()
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                .FirstAsync();
            if (result.FinalLockInfo.Id <= 0)
            {
                throw new Exception($"锁定信息ID无效: {result.FinalLockInfo.Id},无法记录拣选历史");
            }
            var pickingHistory = new Dt_PickingRecord
            {
                FactoryArea = result.FinalLockInfo.FactoryArea,
                TaskNo = task?.TaskNum ?? 0,
                LocationCode = task?.SourceAddress ?? "",
                StockId = result.FinalStockId,
                OrderNo = orderNo,
                OrderDetailId = result.FinalLockInfo.OrderDetailId,
                PalletCode = palletCode,
                Barcode = result.FinalBarcode,
                MaterielCode = result.FinalLockInfo.MaterielCode,
                PickQuantity = result.ActualPickedQty,
                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  ,
            };
            await Db.Insertable(pickingHistory).ExecuteCommandAsync();
        }
        #endregion
        #region å–消分拣私有方法
        private async Task<ValidationResult<bool>> ValidateDataConsistencyBeforeCancel(CancelPickingContext context)
        {
            try
            {
                //  éªŒè¯è®¢å•明细数据
                var currentOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == context.OrderDetail.Id);
                if (currentOrderDetail.OverOutQuantity < context.PickingRecord.PickQuantity)
                    return ValidationResult<bool>.Error($"订单明细已出库数量({currentOrderDetail.OverOutQuantity})小于取消数量({context.PickingRecord.PickQuantity})");
                if (currentOrderDetail.PickedQty < context.PickingRecord.PickQuantity)
                    return ValidationResult<bool>.Error($"订单明细已拣选数量({currentOrderDetail.PickedQty})小于取消数量({context.PickingRecord.PickQuantity})");
                //  éªŒè¯é”å®šä¿¡æ¯æ•°æ®
                var currentLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .FirstAsync(x => x.Id == context.LockInfo.Id);
                if (currentLockInfo.PickedQty < context.PickingRecord.PickQuantity)
                    return ValidationResult<bool>.Error($"锁定信息已拣选数量({currentLockInfo.PickedQty})小于取消数量({context.PickingRecord.PickQuantity})");
                ////// éªŒè¯åº“存数据
                ////var currentStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                ////    .FirstAsync(x => x.Barcode == context.PickingRecord.Barcode && x.StockId == context.PickingRecord.StockId);
                ////if (currentStockDetail == null)
                ////    return ValidationResult<bool>.Error($"未找到对应的库存明细记录");
                ////if (currentStockDetail.Status == StockStatusEmun.入库确认.ObjToInt() ||
                ////    currentStockDetail.Status == StockStatusEmun.入库完成.ObjToInt())
                ////    return ValidationResult<bool>.Error($"条码{context.PickingRecord.Barcode}已经回库,无法取消分拣");
                // éªŒè¯çŠ¶æ€æµè½¬çš„åˆæ³•æ€§
                if (!await CanCancelPicking(currentLockInfo, null))
                    return ValidationResult<bool>.Error($"当前状态不允许取消分拣");
                return ValidationResult<bool>.Success(true);
            }
            catch (Exception ex)
            {
                _logger.LogError($"取消分拣数据一致性验证失败: {ex.Message}");
                return ValidationResult<bool>.Error($"数据验证失败: {ex.Message}");
            }
        }
        private async Task<bool> CanCancelPicking(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail)
        {
            // é”å®šä¿¡æ¯çŠ¶æ€æ£€æŸ¥
            if (lockInfo.Status != (int)OutLockStockStatusEnum.拣选完成)
                return false;
            ////// åº“存状态检查
            ////if (stockDetail.Status == StockStatusEmun.出库完成.ObjToInt())
            ////    return false;
            // å¦‚果是拆包记录,还需要检查父锁定信息状态
            if (lockInfo.IsSplitted == 1 && lockInfo.ParentLockId.HasValue)
            {
                var parentLock = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .FirstAsync(x => x.Id == lockInfo.ParentLockId.Value);
                if (parentLock == null || parentLock.Status == (int)OutLockStockStatusEnum.回库中)
                    return false;
            }
            return true;
        }
        private async Task<ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>> ValidateCancelRequest(string orderNo, string palletCode, string barcode)
        {
            // åŸºç¡€å‚数验证
            if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode) || string.IsNullOrEmpty(barcode))
                return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("订单号、托盘码和条码不能为空");
            // æŸ¥æ‰¾æ‹£é€‰è®°å½•
            var pickingRecord = await Db.Queryable<Dt_PickingRecord>()
                .Where(it => it.OrderNo == orderNo &&
                           it.PalletCode == palletCode &&
                           it.Barcode == barcode)
                .OrderByDescending(it => it.PickTime)
                .FirstAsync();
            if (pickingRecord == null)
                return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("未找到对应的拣选记录");
            if (pickingRecord.PickQuantity <= 0)
            {
                return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"拣选记录数量无效: {pickingRecord.PickQuantity}");
            }
            // æŸ¥æ‰¾é”å®šä¿¡æ¯
            var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.Id == pickingRecord.OutStockLockId)
                .FirstAsync();
            if (lockInfo == null)
                return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("未找到对应的出库锁定信息");
            if (lockInfo.PickedQty < pickingRecord.PickQuantity)
            {
                return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error(
                    $"取消数量({pickingRecord.PickQuantity})超过锁定信息的已拣选数量({lockInfo.PickedQty})");
            }
            // æ£€æŸ¥çŠ¶æ€æ˜¯å¦å…è®¸å–æ¶ˆ
            if (lockInfo.Status != (int)OutLockStockStatusEnum.拣选完成)
                return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("当前状态不允许取消分拣");
            var order = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().FirstAsync(x => x.OrderNo == orderNo);
            if (order?.OrderStatus == (int)OutOrderStatusEnum.出库完成)
                return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error("订单已出库完成,不允许取消分拣");
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().FirstAsync(x => x.Id == pickingRecord.OrderDetailId);
            if (orderDetail == null)
                return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"未找到订单明细,ID: {pickingRecord.OrderDetailId}");
            // æ£€æŸ¥è®¢å•明细的已拣选数量是否足够取消
            if (orderDetail.PickedQty < pickingRecord.PickQuantity)
            {
                return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"取消数量({pickingRecord.PickQuantity})超过订单明细的已拣选数量({orderDetail.PickedQty})");
            }
            // æ£€æŸ¥è®¢å•明细的已出库数量是否足够取消
            if (orderDetail.OverOutQuantity < pickingRecord.PickQuantity)
            {
                return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"取消数量({pickingRecord.PickQuantity})超过订单明细的已出库数量({orderDetail.OverOutQuantity})");
            }
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>().FirstAsync(it => it.Barcode == barcode && it.StockId == pickingRecord.StockId);
            if (stockDetail != null)
            {
                // æ£€æŸ¥åº“存状态 - å¦‚果状态是入库确认或入库完成,说明已经回库
                if (stockDetail.Status == StockStatusEmun.入库确认.ObjToInt() ||
                    stockDetail.Status == StockStatusEmun.入库完成.ObjToInt())
                {
                    return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Error($"条码{barcode}已经回库,不能取消分拣");
                }
            }
            return ValidationResult<(Dt_PickingRecord, Dt_OutStockLockInfo, Dt_OutboundOrderDetail)>.Success((pickingRecord, lockInfo, orderDetail));
        }
        /// <summary>
        /// æ£€æŸ¥æ¡ç æ˜¯å¦å·²ç»å›žåº“
        /// </summary>
        private async Task<bool> IsBarcodeReturned(string barcode, int stockId)
        {
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.Barcode == barcode && it.StockId == stockId)
                .FirstAsync();
            if (stockDetail == null)
                return false;
            // å¦‚果状态是入库确认或入库完成,说明已经回库
            return stockDetail.Status == StockStatusEmun.入库确认.ObjToInt() ||
                   stockDetail.Status == StockStatusEmun.入库完成.ObjToInt();
        }
        /// <summary>
        /// æ£€æŸ¥é”å®šä¿¡æ¯å¯¹åº”的条码是否已经回库
        /// </summary>
        private async Task<bool> IsLockInfoReturned(Dt_OutStockLockInfo lockInfo)
        {
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
                .FirstAsync();
            if (stockDetail == null)
                return false;
            return stockDetail.Status == StockStatusEmun.入库确认.ObjToInt() ||
                   stockDetail.Status == StockStatusEmun.入库完成.ObjToInt();
        }
        private async Task ExecuteCancelLogic(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord,
        Dt_OutboundOrderDetail orderDetail, string orderNo)
        {
            decimal cancelQty = pickingRecord.PickQuantity;
            // æ•°æ®ä¸€è‡´æ€§éªŒè¯
            var context = new CancelPickingContext
            {
                LockInfo = lockInfo,
                PickingRecord = pickingRecord,
                OrderDetail = orderDetail,
                OrderNo = orderNo,
                CancelQuantity = cancelQty
            };
            var validationResult = await ValidateDataConsistencyBeforeCancel(context);
            if (!validationResult.IsValid)
                throw new Exception(validationResult.ErrorMessage);
            // å¤„理不同类型的取消
            if (lockInfo.IsSplitted == 1 && lockInfo.ParentLockId.HasValue)
            {
                await HandleSplitBarcodeCancel(lockInfo, pickingRecord, cancelQty);
            }
            else
            {
                await HandleNormalBarcodeCancel(lockInfo, pickingRecord, cancelQty);
            }
            //  æ›´æ–°è®¢å•明细
            await UpdateOrderDetailOnCancel(pickingRecord.OrderDetailId, cancelQty);
            //  åˆ é™¤æ‹£é€‰è®°å½•
            var deleteResult = await Db.Deleteable<Dt_PickingRecord>()
                .Where(x => x.Id == pickingRecord.Id)
                .ExecuteCommandAsync();
            if (deleteResult <= 0)
                throw new Exception("删除拣选记录失败");
            _logger.LogInformation($"删除拣选记录 - è®°å½•ID: {pickingRecord.Id}, æ¡ç : {pickingRecord.Barcode}");
            // é‡æ–°æ£€æŸ¥è®¢å•状态
            await UpdateOrderStatusForReturn(orderNo);
        }
        private async Task HandleSplitBarcodeCancel(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, decimal cancelQty)
        {
            // æŸ¥æ‰¾çˆ¶é”å®šä¿¡æ¯
            var parentLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.Id == lockInfo.ParentLockId.Value)
                .FirstAsync();
            if (parentLockInfo == null)
                throw new Exception("未找到父锁定信息,无法取消拆包分拣");
            // æ£€æŸ¥çˆ¶æ¡ç å’Œæ‹†åŒ…条码的状态
            if (await IsLockInfoReturned(parentLockInfo))
                throw new Exception($"父条码{parentLockInfo.CurrentBarcode}已经回库,无法取消拆包分拣");
            if (await IsLockInfoReturned(lockInfo))
                throw new Exception($"拆包条码{lockInfo.CurrentBarcode}已经回库,无法取消拆包分拣");
            // æ¢å¤çˆ¶é”å®šä¿¡æ¯çš„分配数量
            parentLockInfo.AssignQuantity += cancelQty;
            parentLockInfo.Status = (int)OutLockStockStatusEnum.出库中; // æ¢å¤ä¸ºå‡ºåº“中状态
            await _outStockLockInfoService.Db.Updateable(parentLockInfo).ExecuteCommandAsync();
            // æ¢å¤çˆ¶æ¡ç åº“å­˜
            var parentStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == parentLockInfo.CurrentBarcode && x.StockId == parentLockInfo.StockId)
                .FirstAsync();
            if (parentStockDetail != null)
            {
                parentStockDetail.StockQuantity += cancelQty;
                parentStockDetail.OutboundQuantity = parentStockDetail.StockQuantity;
                parentStockDetail.Status = StockStatusEmun.出库锁定.ObjToInt();
                await _stockInfoDetailService.Db.Updateable(parentStockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"恢复父条码库存 - æ¡ç : {parentStockDetail.Barcode}, æ¢å¤æ•°é‡: {cancelQty}, æ–°åº“å­˜: {parentStockDetail.StockQuantity}");
            }
            // å¤„理拆包产生的新条码库存
            var splitStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId)
                .FirstAsync();
            if (splitStockDetail != null)
            {
                // åˆ é™¤æ‹†åŒ…产生的新条码库存记录
                await _stockInfoDetailService.Db.Deleteable(splitStockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"删除拆包新条码库存 - æ¡ç : {splitStockDetail.Barcode}");
            }
            // æ›´æ–°æ‹†åŒ…记录状态
            var updateCount = await _splitPackageService.Db.Updateable<Dt_SplitPackageRecord>()
                .SetColumns(x => new Dt_SplitPackageRecord
                {
                    Status = (int)SplitPackageStatusEnum.已撤销,
                    IsReverted = true,
                    Operator = App.User.UserName,
                    RevertTime = DateTime.Now
                })
                .Where(x => x.NewBarcode == lockInfo.CurrentBarcode && !x.IsReverted)
                .ExecuteCommandAsync();
            _logger.LogInformation($"更新拆包记录状态 - æ›´æ–°è®°å½•æ•°: {updateCount}");
            // åˆ é™¤æ‹†åŒ…产生的锁定信息
            await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
                .Where(x => x.Id == lockInfo.Id)
                .ExecuteCommandAsync();
            _logger.LogInformation($"删除拆包锁定信息 - é”å®šID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}");
        }
        private async Task HandleNormalBarcodeCancel(Dt_OutStockLockInfo lockInfo, Dt_PickingRecord pickingRecord, decimal cancelQty)
        {
            if (await IsLockInfoReturned(lockInfo))
                throw new Exception($"条码{lockInfo.CurrentBarcode}已经回库,无法取消分拣");
            // æ¢å¤é”å®šä¿¡æ¯
            lockInfo.PickedQty -= cancelQty;
            if (lockInfo.PickedQty < 0) lockInfo.PickedQty = 0;
            // åªæœ‰å½“拣选数量完全取消时才恢复状态
            if (lockInfo.PickedQty == 0)
            {
                lockInfo.Status = (int)OutLockStockStatusEnum.出库中;
            }
            lockInfo.Operator = App.User.UserName;
            await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync();
            _logger.LogInformation($"恢复锁定信息 - é”å®šID: {lockInfo.Id}, æ‰£å‡æ‹£é€‰æ•°é‡: {cancelQty}, æ–°å·²æ‹£é€‰æ•°é‡: {lockInfo.PickedQty}");
            // æ¢å¤åº“å­˜
            var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(x => x.Barcode == pickingRecord.Barcode && x.StockId == pickingRecord.StockId)
                .FirstAsync();
            if (stockDetail != null)
            {
                stockDetail.StockQuantity += cancelQty;
                stockDetail.OutboundQuantity = stockDetail.StockQuantity;
                // æ¢å¤åº“存状态
                if (stockDetail.Status == StockStatusEmun.出库完成.ObjToInt())
                {
                    stockDetail.Status = StockStatusEmun.出库锁定.ObjToInt();
                }
                await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                _logger.LogInformation($"恢复库存 - æ¡ç : {stockDetail.Barcode}, æ¢å¤æ•°é‡: {cancelQty}, " +
                                      $"新库存: {stockDetail.StockQuantity}, æ–°çŠ¶æ€: {stockDetail.Status}");
            }
            else
            {
                _logger.LogWarning($"未找到库存记录 - æ¡ç : {pickingRecord.Barcode}, åº“å­˜ID: {pickingRecord.StockId}");
            }
        }
        private async Task UpdateOrderDetailOnCancel(int orderDetailId, decimal cancelQty)
        {
            // èŽ·å–æœ€æ–°çš„è®¢å•æ˜Žç»†æ•°æ®ï¼ˆå¸¦é”ï¼‰
            var currentOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .With(SqlWith.RowLock)
                .FirstAsync(x => x.Id == orderDetailId);
            decimal newOverOutQuantity = currentOrderDetail.OverOutQuantity - cancelQty;
            decimal newPickedQty = currentOrderDetail.PickedQty - cancelQty;
            // ä¸¥æ ¼æ£€æŸ¥å–消后数量不会为负数
            if (newOverOutQuantity < 0)
                throw new Exception($"取消分拣将导致已出库数量({newOverOutQuantity})为负数");
            if (newPickedQty < 0)
                throw new Exception($"取消分拣将导致已拣选数量({newPickedQty})为负数");
            // ç¡®å®šæ–°çš„状态
            int newStatus;
            if (newOverOutQuantity >= currentOrderDetail.NeedOutQuantity)
            {
                newStatus = OrderDetailStatusEnum.Over.ObjToInt();
            }
            else if (newOverOutQuantity > 0)
            {
                newStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
            }
            else
            {
                newStatus = OrderDetailStatusEnum.New.ObjToInt();
            }
            // æ›´æ–°è®¢å•明细
            var updateResult = await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => new Dt_OutboundOrderDetail
                {
                    PickedQty = newPickedQty,
                    OverOutQuantity = newOverOutQuantity,
                    OrderDetailStatus = newStatus
                })
                .Where(it => it.Id == orderDetailId)
                .ExecuteCommandAsync();
            if (updateResult <= 0)
                throw new Exception("更新订单明细失败");
            // æ›´æ–°é”å®šæ•°é‡
            await UpdateOrderDetailLockQuantity(orderDetailId);
            _logger.LogInformation($"更新订单明细 - OrderDetailId: {orderDetailId}, " +
                                  $"扣减已出库: {cancelQty}, æ–°å·²å‡ºåº“: {newOverOutQuantity}, " +
                                  $"扣减已拣选: {cancelQty}, æ–°å·²æ‹£é€‰: {newPickedQty}, " +
                                  $"新状态: {newStatus}");
        }
        #endregion
        #region å›žåº“操作私有方法
        private async Task<Dt_StockInfo> GetStockInfo(string palletCode)
        {
            return await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                .FirstAsync(x => x.PalletCode == palletCode);
        }
        /// <summary>
        /// æ£€æŸ¥æ•´ä¸ªæ‰˜ç›˜æ˜¯å¦å·²ç»å›žåº“
        /// </summary>
        private async Task<bool> IsPalletReturned(string palletCode)
        {
            var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                .Where(x => x.PalletCode == palletCode)
                .FirstAsync();
            if (stockInfo == null)
                return false;
            // å¦‚果托盘状态是入库确认或入库完成,说明已经回库
            return stockInfo.StockStatus == StockStatusEmun.入库确认.ObjToInt() ||
                   stockInfo.StockStatus == StockStatusEmun.入库完成.ObjToInt();
        }
        private async Task<Dt_Task> GetCurrentTask(string orderNo, string palletCode)
        {
            // å…ˆå°è¯•通过订单号和托盘号查找任务
            var task = await _taskRepository.Db.Queryable<Dt_Task>()
                .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                .FirstAsync();
            if (task == null)
            {
                // å¦‚果找不到,再通过托盘号查找
                task = await _taskRepository.Db.Queryable<Dt_Task>()
                    .Where(x => x.PalletCode == palletCode)
                    .FirstAsync();
            }
            return task;
        }
        private async Task<decimal> CalculateSplitReturnQuantity(List<Dt_SplitPackageRecord> splitRecords, int stockId)
        {
            decimal totalQty = 0;
            var processedBarcodes = new HashSet<string>();
            foreach (var splitRecord in splitRecords)
            {
                if (splitRecord.Status != (int)SplitPackageStatusEnum.已撤销)
                    continue;
                // æ£€æŸ¥åŽŸæ¡ç 
                if (!processedBarcodes.Contains(splitRecord.OriginalBarcode))
                {
                    var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockId &&
                           it.Status != StockStatusEmun.出库完成.ObjToInt())
                        .FirstAsync();
                    if (originalStock != null && originalStock.StockQuantity > 0)
                    {
                        totalQty += originalStock.StockQuantity;
                        processedBarcodes.Add(splitRecord.OriginalBarcode);
                    }
                }
                // æ£€æŸ¥æ–°æ¡ç 
                if (!processedBarcodes.Contains(splitRecord.NewBarcode))
                {
                    var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                        .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockId && it.Status != StockStatusEmun.出库完成.ObjToInt())
                        .FirstAsync();
                    if (newStock != null && newStock.StockQuantity > 0)
                    {
                        totalQty += newStock.StockQuantity;
                        processedBarcodes.Add(splitRecord.NewBarcode);
                    }
                }
            }
            return totalQty;
        }
        private async Task<WebResponseContent> HandleNoReturnItems(string orderNo, string palletCode, Dt_Task originalTask, int stockInfoId)
        {
            // æ£€æŸ¥æ˜¯å¦æ‰€æœ‰è´§ç‰©éƒ½å·²æ‹£é€‰å®Œæˆ
            //var allPicked = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
            //    .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode)
            //    .AnyAsync(it => it.Status == (int)OutLockStockStatusEnum.拣选完成);
            //if (allPicked)
            //{
            //    // åˆ é™¤åŽŸå§‹å‡ºåº“ä»»åŠ¡ ç»„空盘   ç©ºç›˜å›žåº“
            //    //await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
            //    return WebResponseContent.Instance.OK("所有货物已拣选完成,托盘为空");
            //}
            //else
            //{
            //    // åˆ é™¤åŽŸå§‹å‡ºåº“ä»»åŠ¡
            //    //await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
            //    return WebResponseContent.Instance.Error("没有需要回库的剩余货物");
            //}
            try
            {
                var locationtype = 0;
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                        .Where(x => x.PalletCode == palletCode)
                        .FirstAsync();
                if (stockInfo == null)
                {
                    var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>().FirstAsync(x => x.LocationCode == originalTask.SourceAddress);
                    locationtype = firstLocation?.LocationType ?? 1;
                }
                else
                {
                    locationtype = stockInfo.LocationType;
                    _stockInfoService.DeleteData(stockInfo);
                }
                var targetAddress = originalTask.TargetAddress;
                await CleanupZeroStockData(stockInfoId);
                var emptystockInfo = new Dt_StockInfo() { PalletType = PalletTypeEnum.Empty.ObjToInt(), StockStatus = StockStatusEmun.组盘暂存.ObjToInt(), PalletCode = palletCode, LocationType = locationtype };
                emptystockInfo.Details = new List<Dt_StockInfoDetail>();
                _stockInfoService.AddMaterielGroup(emptystockInfo);
                //空托盘如何处理  è¿˜æœ‰ä¸€ä¸ªå‡ºåº“任务要处理。
                originalTask.PalletType = PalletTypeEnum.Empty.ObjToInt();
                await CreateReturnTaskAndHandleESS(orderNo, palletCode, originalTask, TaskTypeEnum.InEmpty, PalletTypeEnum.Empty.ObjToInt());
            }
            catch (Exception ex)
            {
                _logger.LogError($" HandleNoReturnItems  å¤±è´¥: {ex.Message}");
                return WebResponseContent.Instance.Error($" å›žåº“空托盘失败!");
            }
            return WebResponseContent.Instance.OK("空托盘回库任务创建成功");
        }
        private async Task ExecuteReturnOperations(string orderNo, string palletCode, Dt_StockInfo stockInfo,
            Dt_Task task, PalletStatusAnalysis analysis)
        {
            // æƒ…况1:处理未分拣的出库锁定记录
            if (analysis.HasRemainingLocks)
            {
                await HandleRemainingLocksReturn(analysis.RemainingLocks, stockInfo.Id);
                // await UpdateOrderDetailsOnReturn(analysis.RemainingLocks);
            }
            // å¤„理托盘上其他库存货物
            if (analysis.HasPalletStockGoods)
            {
                var validStockGoods = analysis.PalletStockGoods
            .Where(x => x.Status != StockStatusEmun.出库完成.ObjToInt())
            .ToList();
                if (validStockGoods.Any())
                {
                    await HandlePalletStockGoodsReturn(analysis.PalletStockGoods);
                }
                else
                {
                    _logger.LogInformation("没有有效的库存货物需要回库");
                }
            }
            // å¤„理拆包记录
            if (analysis.HasSplitRecords)
            {
                var validSplitRecords = analysis.SplitRecords
            .Where(x => x.Status != (int)SplitPackageStatusEnum.已拣选)
            .ToList();
                if (validSplitRecords.Any())
                {
                    await HandleSplitRecordsReturn(analysis.SplitRecords, orderNo, palletCode);
                }
                else
                {
                    _logger.LogInformation("没有有效的拆包记录需要处理");
                }
            }
            // æ›´æ–°åº“存主表状态
            await UpdateStockInfoStatus(stockInfo);
        }
        /// <summary>
        /// å®Œå…¨é‡Šæ”¾é”å®šï¼Œå…è®¸é‡æ–°åˆ†é…åº“å­˜
        /// </summary>
        private async Task ReleaseAllLocksForReallocation(string orderNo, string palletCode, PalletStatusAnalysis analysis)
        {
            _logger.LogInformation($"开始释放锁定以便重新分配 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
            // 1. å¤„理未分拣的出库锁定记录 - å®Œå…¨é‡Šæ”¾
            if (analysis.HasRemainingLocks)
            {
                await ReleaseRemainingLocks(analysis.RemainingLocks);
            }
            // 2. å¤„理已回库的锁定记录 - åˆ é™¤æˆ–标记为无效
            await CleanupReturnedLocks(orderNo, palletCode);
            // 3. é‡ç½®è®¢å•明细的锁定数量
            await ResetOrderDetailLockQuantities(analysis);
            _logger.LogInformation($"锁定释放完成 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
        }
        /// <summary>
        /// é‡Šæ”¾æœªåˆ†æ‹£çš„锁定记录
        /// </summary>
        private async Task ReleaseRemainingLocks(List<Dt_OutStockLockInfo> remainingLocks)
        {
            var lockIds = remainingLocks.Select(x => x.Id).ToList();
            // å°†é”å®šè®°å½•状态改为"已释放",或者直接删除
            //  æ ‡è®°ä¸ºå·²é‡Šæ”¾
            await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
                .SetColumns(it => new Dt_OutStockLockInfo
                {
                    Status = (int)OutLockStockStatusEnum.已释放, // éœ€è¦æ–°å¢žè¿™ä¸ªçŠ¶æ€
                   // ReleaseTime = DateTime.Now,
                    Operator = App.User.UserName
                })
                .Where(it => lockIds.Contains(it.Id))
                .ExecuteCommandAsync();
            //  ç›´æŽ¥åˆ é™¤ï¼ˆæ›´å½»åº•)
            // await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>()
            //     .Where(it => lockIds.Contains(it.Id))
            //     .ExecuteCommandAsync();
            _logger.LogInformation($"释放{remainingLocks.Count}条未分拣锁定记录");
        }
        /// <summary>
        /// æ¸…理已回库的锁定记录
        /// </summary>
        private async Task CleanupReturnedLocks(string orderNo, string palletCode)
        {
            // æŸ¥æ‰¾æ‰€æœ‰çŠ¶æ€ä¸ºå›žåº“ä¸­çš„é”å®šè®°å½•å¹¶é‡Šæ”¾
            var returnedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.OrderNo == orderNo &&
                           it.PalletCode == palletCode &&
                           it.Status == (int)OutLockStockStatusEnum.回库中)
                .ToListAsync();
            if (returnedLocks.Any())
            {
                var returnedLockIds = returnedLocks.Select(x => x.Id).ToList();
                await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
                    .SetColumns(it => new Dt_OutStockLockInfo
                    {
                        Status = (int)OutLockStockStatusEnum.已释放,
                        //ReleaseTime = DateTime.Now,
                        Operator = App.User.UserName
                    })
                    .Where(it => returnedLockIds.Contains(it.Id))
                    .ExecuteCommandAsync();
                _logger.LogInformation($"清理{returnedLocks.Count}条回库中锁定记录");
            }
        }
        /// <summary>
        /// é‡ç½®è®¢å•明细的锁定数量
        /// </summary>
        private async Task ResetOrderDetailLockQuantities(PalletStatusAnalysis analysis)
        {
            // æ”¶é›†æ‰€æœ‰å—影响的订单明细ID
            var affectedOrderDetailIds = new HashSet<int>();
            if (analysis.HasRemainingLocks)
            {
                foreach (var lockInfo in analysis.RemainingLocks)
                {
                    affectedOrderDetailIds.Add(lockInfo.OrderDetailId);
                }
            }
            // é‡ç½®è¿™äº›è®¢å•明细的锁定数量
            foreach (var orderDetailId in affectedOrderDetailIds)
            {
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        LockQuantity = 0, // é‡ç½®é”å®šæ•°é‡
                        OrderDetailStatus = OrderDetailStatusEnum.New.ObjToInt() // é‡ç½®çŠ¶æ€ä¸ºæ–°å»º
                    })
                    .Where(it => it.Id == orderDetailId)
                    .ExecuteCommandAsync();
            }
            _logger.LogInformation($"重置{affectedOrderDetailIds.Count}个订单明细的锁定数量");
        }
        private async Task HandleRemainingLocksReturn(List<Dt_OutStockLockInfo> remainingLocks, int stockId)
        {
            var lockIds = remainingLocks.Select(x => x.Id).ToList();
            // æ›´æ–°å‡ºåº“锁定记录状态为回库中
            await _outStockLockInfoService.Db.Updateable<Dt_OutStockLockInfo>()
                .SetColumns(it => new Dt_OutStockLockInfo
                {
                    Status = (int)OutLockStockStatusEnum.回库中,
                })
                .Where(it => lockIds.Contains(it.Id))
                .ExecuteCommandAsync();
            // å¤„理库存记录
            foreach (var lockInfo in remainingLocks)
            {
                decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty;
                if (returnQty <= 0)
                {
                    _logger.LogWarning($"锁定记录{lockInfo.Id}无需回库,分配数量: {lockInfo.AssignQuantity}, å·²æ‹£é€‰: {lockInfo.PickedQty}");
                    continue;
                }
                _logger.LogInformation($"处理锁定记录回库 - é”å®šID: {lockInfo.Id}, æ¡ç : {lockInfo.CurrentBarcode}, å›žåº“数量: {returnQty}");
                // æŸ¥æ‰¾å¯¹åº”的库存明细
                var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                    .Where(it => it.Barcode == lockInfo.CurrentBarcode && it.StockId == lockInfo.StockId)
                    .FirstAsync();
                if (stockDetail != null)
                {
                    stockDetail.StockQuantity += returnQty;
                    // æ¢å¤åº“存状态
                    stockDetail.OutboundQuantity = Math.Max(0, stockDetail.OutboundQuantity - returnQty);
                    stockDetail.Status = StockStatusEmun.入库确认.ObjToInt();
                    await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync();
                }
                else
                {
                    _logger.LogWarning($"未找到对应的库存明细 - æ¡ç : {lockInfo.CurrentBarcode}, åº“å­˜ID: {lockInfo.StockId}");
                    // åˆ›å»ºæ–°çš„库存记录
                    //var newStockDetail = new Dt_StockInfoDetail
                    //{
                    //    StockId = lockInfo.StockId,
                    //    MaterielCode = lockInfo.MaterielCode,
                    //    MaterielName = lockInfo.MaterielName,
                    //    OrderNo = lockInfo.OrderNo,
                    //    BatchNo = lockInfo.BatchNo,
                    //    StockQuantity = returnQty,
                    //    OutboundQuantity = 0,
                    //    Barcode = lockInfo.CurrentBarcode,
                    //    InboundOrderRowNo = "",
                    //    Status = StockStatusEmun.入库确认.ObjToInt(),
                    //    SupplyCode = lockInfo.SupplyCode,
                    //    WarehouseCode = lockInfo.WarehouseCode,
                    //    Unit = lockInfo.Unit,
                    //};
                    //await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync();
                }
                try
                {
                    var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                        .FirstAsync(x => x.Id == lockInfo.OrderDetailId);
                    if (orderDetail != null)
                    {
                        decimal newLockQuantity = Math.Max(0, orderDetail.LockQuantity - returnQty);
                        await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                            .SetColumns(it => new Dt_OutboundOrderDetail
                            {
                                LockQuantity = newLockQuantity
                            })
                            .Where(it => it.Id == lockInfo.OrderDetailId)
                            .ExecuteCommandAsync();
                        _logger.LogInformation($"更新订单明细锁定数量 - OrderDetailId: {lockInfo.OrderDetailId}, " +
                                             $"扣减锁定: {returnQty}, æ–°é”å®šæ•°é‡: {newLockQuantity}");
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError($"更新订单明细锁定数量失败 - OrderDetailId: {lockInfo.OrderDetailId}, Error: {ex.Message}");
                }
            }
        }
        private async Task UpdateOrderDetailsOnReturn(List<Dt_OutStockLockInfo> remainingLocks)
        {
            // æŒ‰è®¢å•明细分组
            var orderDetailGroups = remainingLocks.GroupBy(x => x.OrderDetailId);
            foreach (var group in orderDetailGroups)
            {
                var orderDetailId = group.Key;
                var totalReturnQty = group.Sum(x => x.AssignQuantity - x.PickedQty);
                // èŽ·å–å½“å‰è®¢å•æ˜Žç»†
                var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .FirstAsync(x => x.Id == orderDetailId);
                if (orderDetail != null)
                {
                    // è°ƒæ•´å·²æ‹£é€‰æ•°é‡å’Œå·²å‡ºåº“数量
                    decimal newPickedQty = Math.Max(0, orderDetail.PickedQty - totalReturnQty);
                    decimal newOverOutQuantity = Math.Max(0, orderDetail.OverOutQuantity - totalReturnQty);
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                        .SetColumns(it => new Dt_OutboundOrderDetail
                        {
                            PickedQty = newPickedQty,
                            OverOutQuantity = newOverOutQuantity,
                        })
                        .Where(it => it.Id == orderDetailId)
                        .ExecuteCommandAsync();
                }
            }
        }
        private async Task HandlePalletStockGoodsReturn(List<Dt_StockInfoDetail> palletStockGoods)
        {
            _logger.LogInformation($"回库操作:发现{palletStockGoods.Count}个库存明细需要回库,等待AGV搬运");
            foreach (var stockGood in palletStockGoods)
            {
                _logger.LogInformation($"待回库货物 - æ¡ç : {stockGood.Barcode}, æ•°é‡: {stockGood.StockQuantity}, å½“前状态: {stockGood.Status}");
                if (stockGood.Status != StockStatusEmun.出库完成.ObjToInt())
                {
                    stockGood.OutboundQuantity = 0;
                    stockGood.Status = StockStatusEmun.入库确认.ObjToInt();
                    await _stockInfoDetailService.Db.Updateable(stockGood).ExecuteCommandAsync();
                    _logger.LogInformation($"库存货物回库完成 - æ¡ç : {stockGood.Barcode}, æ–°çŠ¶æ€: {stockGood.Status}");
                }
                else
                {
                    _logger.LogWarning($"跳过已出库完成的货物 - æ¡ç : {stockGood.Barcode}");
                }
            }
        }
        private async Task HandleSplitRecordsReturn(List<Dt_SplitPackageRecord> splitRecords, string orderNo, string palletCode)
        {
            var validRecords = splitRecords.Where(x => x.Status != (int)SplitPackageStatusEnum.已拣选).ToList();
            if (!validRecords.Any())
            {
                _logger.LogInformation("没有需要回库的拆包记录");
                return;
            }
            _logger.LogInformation($"更新{validRecords.Count}条拆包记录状态为已回库");
            // æ›´æ–°æ‹†åŒ…记录状态
            await _splitPackageService.Db.Updateable<Dt_SplitPackageRecord>()
                .SetColumns(x => new Dt_SplitPackageRecord
                {
                    Status = (int)SplitPackageStatusEnum.已回库,
                    Operator = App.User.UserName
                })
                .Where(x => validRecords.Select(r => r.Id).Contains(x.Id))
                .ExecuteCommandAsync();
        }
        private async Task UpdateStockInfoStatus(Dt_StockInfo stockInfo)
        {
            _logger.LogInformation($"回库操作:托盘{stockInfo.PalletCode}等待AGV回库搬运");
            // æ›´æ–°åº“存主表状态
            stockInfo.StockStatus = StockStatusEmun.入库确认.ObjToInt();
            await _stockInfoService.Db.Updateable(stockInfo).ExecuteCommandAsync();
        }
        /// <summary>
        /// åˆ›å»ºå›žåº“任务
        /// </summary>
        /// <param name="orderNo"></param>
        /// <param name="palletCode"></param>
        /// <param name="originalTask"></param>
        /// <param name="analysis"></param>
        /// <returns></returns>
        private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, TaskTypeEnum taskTypeEnum, int palletType)
        {
            var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>()
                .FirstAsync(x => x.LocationCode == originalTask.SourceAddress);
            // åˆ†é…æ–°è´§ä½
            var newLocation = _locationInfoService.AssignLocation(firstLocation.LocationType);
            Dt_Task returnTask = new()
            {
                CurrentAddress = stations[originalTask.TargetAddress],
                Grade = 0,
                PalletCode = palletCode,
                NextAddress = "",
                // OrderNo = originalTask.OrderNo,
                OrderNo = orderNo,
                Roadway = newLocation.RoadwayNo,
                SourceAddress = stations[originalTask.TargetAddress],
                TargetAddress = newLocation.LocationCode,
                TaskStatus = TaskStatusEnum.New.ObjToInt(),
                TaskType = taskTypeEnum.ObjToInt(),
                PalletType = palletType,
                WarehouseId = originalTask.WarehouseId
            };
            // ä¿å­˜å›žåº“任务
            await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync();
            var targetAddress = originalTask.TargetAddress;
            // åˆ é™¤åŽŸå§‹å‡ºåº“ä»»åŠ¡
            _taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.自动完成);
            // await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync();
            // ç»™ ESS å‘送流动信号和创建任务
            await SendESSCommands(palletCode, targetAddress, returnTask);
        }
        /// <summary>
        /// ç»™ESS下任务
        /// </summary>
        /// <param name="palletCode"></param>
        /// <param name="targetAddress"></param>
        /// <param name="returnTask"></param>
        /// <returns></returns>
        /// <exception cref="Exception"></exception>
        private async Task SendESSCommands(string palletCode, string targetAddress, Dt_Task returnTask)
        {
            try
            {
                // 1. å‘送流动信号
                var moveResult = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest
                {
                    slotCode = movestations[targetAddress],
                    containerCode = palletCode
                });
                //if (moveResult)
                //{
                // 2. åˆ›å»ºå›žåº“任务
                var essTask = new TaskModel()
                {
                    taskType = "putaway",
                    taskGroupCode = "",
                    groupPriority = 0,
                    tasks = new List<TasksType>{  new() {
                            taskCode = returnTask.TaskNum.ToString(),
                            taskPriority = 0,
                            taskDescribe = new TaskDescribeType
                            {
                                containerCode = palletCode,
                                containerType = "CT_KUBOT_STANDARD",
                                fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "",
                                toStationCode = "",
                                toLocationCode = returnTask.TargetAddress,
                                deadline = 0,
                                storageTag = ""
                            }
                        } }
                };
                var resultTask = await _eSSApiService.CreateTaskAsync(essTask);
                _logger.LogInformation($"ReturnRemaining åˆ›å»ºä»»åŠ¡æˆåŠŸ: {resultTask}");
                //}
            }
            catch (Exception ex)
            {
                _logger.LogError($"ReturnRemaining ESS命令发送失败: {ex.Message}");
                throw new Exception($"ESS系统通信失败: {ex.Message}");
            }
        }
        #endregion
        #region è®¢å•状态管理
        private async Task CheckAndUpdateOrderStatus(string orderNo)
        {
            try
            {
                var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id)
                    .Where((o, item) => item.OrderNo == orderNo)
                    .Select((o, item) => o)
                    .ToListAsync();
                bool allCompleted = true;
                bool hasPartial = false;
                bool hasLocked = false;
                foreach (var detail in orderDetails)
                {
                    if (detail.OverOutQuantity < detail.NeedOutQuantity)
                    {
                        allCompleted = false;
                    }
                    if (detail.OrderDetailStatus == OrderDetailStatusEnum.Outbound.ObjToInt())
                    {
                        hasPartial = true;
                    }
                    //if (detail.OrderDetailStatus == OrderDetailStatusEnum.Locked.ObjToInt())
                    //{
                    //    hasLocked = true;
                    //}
                }
                var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                    .FirstAsync(x => x.OrderNo == orderNo);
                if (outboundOrder == null) return;
                int newStatus;
                if (allCompleted)
                {
                    newStatus = (int)OutOrderStatusEnum.出库完成;
                }
                else if (hasPartial )
                {
                    newStatus = (int)OutOrderStatusEnum.出库中;
                }
                else
                {
                    newStatus = (int)OutOrderStatusEnum.未开始;
                }
                if (outboundOrder.OrderStatus != newStatus)
                {
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                        .SetColumns(x => new Dt_OutboundOrder
                        {
                            OrderStatus = newStatus,
                            Operator = App.User.UserName,
                        })
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                    _logger.LogInformation($"订单状态更新 - OrderNo: {orderNo}, æ—§çŠ¶æ€: {outboundOrder.OrderStatus}, æ–°çŠ¶æ€: {newStatus}");
                    // åªæœ‰æ­£å¸¸åˆ†æ‹£å®Œæˆæ—¶æ‰å‘MES反馈
                    if (allCompleted && newStatus == (int)OutOrderStatusEnum.出库完成)
                    {
                        await HandleOrderCompletion(outboundOrder, orderNo);
                    }
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"CheckAndUpdateOrderStatus失败 - OrderNo: {orderNo}, Error: {ex.Message}");
            }
        }
        /// <summary>
        /// æ›´æ–°è®¢å•明细状态(基于已出库数量)
        /// </summary>
        private async Task UpdateOrderDetailStatus(int orderDetailId)
        {
            var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .FirstAsync(x => x.Id == orderDetailId);
            if (orderDetail == null) return;
            int newStatus = orderDetail.OrderDetailStatus;
            if (orderDetail.OverOutQuantity >= orderDetail.NeedOutQuantity)
            {
                // å·²å‡ºåº“数量 >= éœ€æ±‚数量,标记为完成
                newStatus = OrderDetailStatusEnum.Over.ObjToInt();
            }
            else if (orderDetail.OverOutQuantity > 0)
            {
                // æœ‰éƒ¨åˆ†å‡ºåº“,但未完成
                newStatus = OrderDetailStatusEnum.Outbound.ObjToInt();
            }
            else if (orderDetail.LockQuantity > 0)
            {
                // æœ‰é”å®šæ•°é‡ï¼Œä½†æœªå‡ºåº“
                //newStatus = OrderDetailStatusEnum.Locked.ObjToInt();
            }
            else
            {
                // æ–°å»ºçŠ¶æ€
                newStatus = OrderDetailStatusEnum.New.ObjToInt();
            }
            // åªæœ‰çŠ¶æ€å‘ç”Ÿå˜åŒ–æ—¶æ‰æ›´æ–°
            if (orderDetail.OrderDetailStatus != newStatus)
            {
                await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                    .SetColumns(it => new Dt_OutboundOrderDetail
                    {
                        OrderDetailStatus = newStatus,
                    })
                    .Where(it => it.Id == orderDetailId)
                    .ExecuteCommandAsync();
            }
        }
        /// <summary>
        /// æ›´æ–°è®¢å•明细锁定数量
        /// </summary>
        private async Task UpdateOrderDetailLockQuantity(int orderDetailId)
        {
            var totalLockedQty = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(x => x.OrderDetailId == orderDetailId &&
                           x.Status == (int)OutLockStockStatusEnum.出库中)
                .SumAsync(x => x.AssignQuantity - x.PickedQty);
            await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                .SetColumns(it => new Dt_OutboundOrderDetail
                {
                    LockQuantity = totalLockedQty
                })
                .Where(it => it.Id == orderDetailId)
                .ExecuteCommandAsync();
        }
        private async Task UpdateOrderStatusForReturn(string orderNo)
        {
            try
            {
                var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                    .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id)
                    .Where((o, item) => item.OrderNo == orderNo)
                    .Select((o, item) => o)
                    .ToListAsync();
                bool allCompleted = true;
                foreach (var detail in orderDetails)
                {
                    if (detail.OverOutQuantity < detail.NeedOutQuantity)
                    {
                        allCompleted = false;
                        break;
                    }
                }
                var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                    .FirstAsync(x => x.OrderNo == orderNo);
                if (outboundOrder == null) return;
                int newStatus = allCompleted ? (int)OutOrderStatusEnum.出库完成 : (int)OutOrderStatusEnum.出库中;
                if (outboundOrder.OrderStatus != newStatus)
                {
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                          .SetColumns(x => new Dt_OutboundOrder
                          {
                              OrderStatus = newStatus,
                              Operator = App.User.UserName,
                          })
                        .Where(x => x.OrderNo == orderNo)
                        .ExecuteCommandAsync();
                    _logger.LogInformation($"回库操作更新订单状态 - OrderNo: {orderNo}, æ–°çŠ¶æ€: {newStatus}");
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"UpdateOrderStatusForReturn失败 - OrderNo: {orderNo}, Error: {ex.Message}");
            }
        }
        private async Task HandleOrderCompletion(Dt_OutboundOrder outboundOrder, string orderNo)
        {
            // è°ƒæ‹¨å‡ºåº“和重检出库不需要反馈MES
            if (outboundOrder.OrderType == OutOrderTypeEnum.Allocate.ObjToInt())
            {
                var allocate = _allocateService.Repository.QueryData(x => x.UpperOrderNo == outboundOrder.UpperOrderNo).First();
                var allocatefeedmodel = new AllocateDto
                {
                    ReqCode = Guid.NewGuid().ToString(),
                    ReqTime = DateTime.Now.ToString(),
                    BusinessType = "3",
                    FactoryArea = outboundOrder.FactoryArea,
                    OperationType = 1,
                    Operator = App.User.UserName,
                    OrderNo = outboundOrder.UpperOrderNo,
                   // documentsNO = outboundOrder.OrderNo,
                   // status = outboundOrder.OrderStatus,
                    fromWarehouse = allocate?.FromWarehouse ?? "",
                    toWarehouse = allocate?.ToWarehouse ?? "",
                    Details = new List<AllocateDtoDetail>()
                };
                // åªèŽ·å–å·²æ‹£é€‰å®Œæˆçš„é”å®šè®°å½•
                var lists = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == orderNo && x.Status == (int)OutLockStockStatusEnum.拣选完成)
                    .ToListAsync();
                var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.lineNo, item.Unit, item.WarehouseCode })
                   .Select(group => new AllocateDtoDetail
                   {
                       MaterialCode = group.Key.MaterielCode,
                       LineNo = group.Key.lineNo,
                       WarehouseCode = group.Key.WarehouseCode,
                       Qty = group.Sum(x => x.PickedQty),
                       Unit = group.Key.Unit,
                       Barcodes = group.Select(row => new BarcodeInfo
                       {
                           Barcode = row.CurrentBarcode,
                           SupplyCode = row.SupplyCode,
                           BatchNo = row.BatchNo,
                           Unit = row.Unit,
                           Qty = row.PickedQty
                       }).ToList()
                   }).ToList();
                allocatefeedmodel.Details = groupedData;
                var result = await _invokeMESService.FeedbackAllocate(allocatefeedmodel);
                if (result != null && result.code == 200)
                {
                    await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                           .SetColumns(x => x.ReturnToMESStatus == 1)
                           .Where(x => x.OrderId == outboundOrder.Id).ExecuteCommandAsync();
                    await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                          .SetColumns(x => new Dt_OutboundOrder
                          {
                              ReturnToMESStatus = 1,
                              Operator = App.User.UserName,
                          }).Where(x => x.OrderNo == orderNo).ExecuteCommandAsync();
                }
            }
            else if (outboundOrder.OrderType == OutOrderTypeEnum.ReCheck.ObjToInt())
            {
            }
            else
            {
                try
                {
                    var feedmodel = new FeedbackOutboundRequestModel
                    {
                        reqCode = Guid.NewGuid().ToString(),
                        reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                        business_type = outboundOrder.BusinessType,
                        factoryArea = outboundOrder.FactoryArea,
                        operationType = 1,
                        Operator = App.User.UserName,
                        orderNo = outboundOrder.UpperOrderNo,
                        documentsNO = outboundOrder.OrderNo,
                        status = outboundOrder.OrderStatus,
                        details = new List<FeedbackOutboundDetailsModel>()
                    };
                    // åªèŽ·å–å·²æ‹£é€‰å®Œæˆçš„é”å®šè®°å½•
                    var lists = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                        .Where(x => x.OrderNo == orderNo && x.Status == (int)OutLockStockStatusEnum.拣选完成)
                        .ToListAsync();
                    var groupedData = lists.GroupBy(item => new { item.MaterielCode, item.lineNo, item.Unit, item.WarehouseCode })
                       .Select(group => new FeedbackOutboundDetailsModel
                       {
                           materialCode = group.Key.MaterielCode,
                           lineNo = group.Key.lineNo,
                           warehouseCode = group.Key.WarehouseCode,
                           qty = group.Sum(x => x.PickedQty),
                           currentDeliveryQty = group.Sum(x => x.PickedQty),
                           unit = group.Key.Unit,
                           barcodes = group.Select(row => new WIDESEA_DTO.Outbound.BarcodesModel
                           {
                               barcode = row.CurrentBarcode,
                               supplyCode = row.SupplyCode,
                               batchNo = row.BatchNo,
                               unit = row.Unit,
                               qty = row.PickedQty
                           }).ToList()
                       }).ToList();
                    feedmodel.details = groupedData;
                    var result = await _invokeMESService.FeedbackOutbound(feedmodel);
                    if (result != null && result.code == 200)
                    {
                        await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>()
                            .SetColumns(x => x.ReturnToMESStatus == 1)
                            .Where(x => x.OrderId == outboundOrder.Id)
                            .ExecuteCommandAsync();
                        await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>()
                              .SetColumns(x => new Dt_OutboundOrder
                              {
                                  ReturnToMESStatus = 1,
                                  Operator = App.User.UserName,
                              })
                            .Where(x => x.OrderNo == orderNo)
                            .ExecuteCommandAsync();
                    }
                    _logger.LogError($"FeedbackOutbound成功 - OrderNo: {orderNo}, {JsonSerializer.Serialize(result)}");
                }
                catch (Exception ex)
                {
                    _logger.LogError($"FeedbackOutbound失败 - OrderNo: {orderNo}, Error: {ex.Message}");
                }
            }
        }
        #endregion
        #region ç©ºæ‰˜ç›˜
        /// <summary>
        /// æ¸…理零库存数据
        /// </summary>
        private async Task CleanupZeroStockData(int stockId)
        {
            try
            {
                // 1. åˆ é™¤åº“存数量为0的明细记录
                var deleteDetailCount = await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>()
                    .Where(x => x.StockId == stockId && x.StockQuantity == 0 && (x.Status == StockStatusEmun.出库完成.ObjToInt() || x.Status ==
                                          StockStatusEmun.入库完成.ObjToInt()))
                    .ExecuteCommandAsync();
                await _stockInfoService.Db.Deleteable<Dt_StockInfo>()
                   .Where(x => x.Id == stockId).ExecuteCommandAsync();
                _logger.LogInformation($"清理零库存明细记录 - StockId: {stockId}, åˆ é™¤è®°å½•æ•°: {deleteDetailCount}");
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"清理零库存数据失败 - StockId: {stockId}, Error: {ex.Message}");
                // æ³¨æ„ï¼šæ¸…理失败不应该影响主流程
            }
        }
        /// <summary>
        /// å¤„理任务清理(按订单和托盘)
        /// </summary>
        private async Task HandleTaskCleanup(string orderNo, string palletCode)
        {
            try
            {
                // 1. æŸ¥æ‰¾æ‰€æœ‰ä¸Žè¯¥è®¢å•和托盘相关的任务
                var tasks = await _taskRepository.Db.Queryable<Dt_Task>().Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode).ToListAsync();
                if (tasks.Any())
                {
                    foreach (var task in tasks)
                    {
                        task.TaskStatus = (int)TaskStatusEnum.Finish;
                    }
                    // await _taskRepository.Db.Updateable(tasks).ExecuteCommandAsync();
                    _taskRepository.DeleteAndMoveIntoHty(tasks, OperateTypeEnum.自动完成);
                    _logger.LogInformation($"完成{tasks.Count}个托盘任务 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
                }
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"处理任务清理失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
                throw new Exception($"任务清理失败: {ex.Message}");
            }
        }
        /// <summary>
        /// æ›´æ–°è®¢å•相关数据
        /// </summary>
        private async Task UpdateOrderData(string orderNo, string palletCode)
        {
            try
            {
                // æ£€æŸ¥è®¢å•是否还有其他托盘在处理中
                var otherActivePallets = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                    .Where(x => x.OrderNo == orderNo &&
                               x.PalletCode != palletCode &&
                             (x.Status == (int)OutLockStockStatusEnum.出库中 || x.Status == (int)OutLockStockStatusEnum.回库中))
                    .AnyAsync();
                var otherActiveTasks = await _taskRepository.Db.Queryable<Dt_Task>()
                    .Where(x => x.OrderNo == orderNo &&
                               x.PalletCode != palletCode
                    // && x.TaskStatus.In((int)TaskStatusEnum.待执行, (int)TaskStatusEnum.执行中)
                     )
                    .AnyAsync();
                // å¦‚果没有其他托盘在处理,检查订单是否应该完成
                if (!otherActivePallets && !otherActiveTasks)
                {
                    await CheckAndUpdateOrderCompletion(orderNo);
                }
                else
                {
                    _logger.LogInformation($"订单 {orderNo} è¿˜æœ‰å…¶ä»–托盘在处理,不更新订单状态");
                }
                // 3. æ›´æ–°æ‹£é€‰è®°å½•状态(可选)
                await UpdatePickingRecordsStatus(orderNo, palletCode);
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"更新订单数据失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
                throw new Exception($"更新订单数据失败: {ex.Message}");
            }
        }
        /// <summary>
        /// æ£€æŸ¥å¹¶æ›´æ–°è®¢å•完成状态
        /// </summary>
        private async Task CheckAndUpdateOrderCompletion(string orderNo)
        {
            var orderDetails = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>()
                .LeftJoin<Dt_OutboundOrder>((o, item) => o.OrderId == item.Id)
                .Where((o, item) => item.OrderNo == orderNo)
                .Select((o, item) => o)
                .ToListAsync();
            bool allCompleted = true;
            foreach (var detail in orderDetails)
            {
                if (detail.OverOutQuantity < detail.NeedOutQuantity)
                {
                    allCompleted = false;
                    break;
                }
            }
            var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                .FirstAsync(x => x.OrderNo == orderNo);
            if (outboundOrder != null && allCompleted && outboundOrder.OrderStatus != (int)OutOrderStatusEnum.出库完成)
            {
                outboundOrder.OrderStatus = (int)OutOrderStatusEnum.出库完成;
                outboundOrder.Operator = App.User.UserName;
                await _outboundOrderService.Db.Updateable(outboundOrder).ExecuteCommandAsync();
                _logger.LogInformation($"订单 {orderNo} å·²æ ‡è®°ä¸ºå‡ºåº“完成");
                // å‘MES反馈订单完成(如果需要)
                await HandleOrderCompletion(outboundOrder, orderNo);
            }
        }
        /// <summary>
        /// æ›´æ–°æ‹£é€‰è®°å½•状态
        /// </summary>
        private async Task UpdatePickingRecordsStatus(string orderNo, string palletCode)
        {
            try
            {
                // å¯ä»¥å°†ç›¸å…³çš„æ‹£é€‰è®°å½•标记为已完成
                var pickingRecords = await Db.Queryable<Dt_PickingRecord>()
                    .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode)
                    .ToListAsync();
                // è¿™é‡Œå¯ä»¥æ ¹æ®éœ€è¦æ›´æ–°æ‹£é€‰è®°å½•的状态字段
                // ä¾‹å¦‚:pickingRecord.Status = (int)PickingStatusEnum.已完成;
                _logger.LogInformation($"找到{pickingRecords.Count}条拣选记录 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"更新拣选记录状态失败: {ex.Message}");
            }
        }
        #endregion
        #region è¾…助方法
        /// <summary>
        /// ç»Ÿä¸€åˆ†æžæ‰˜ç›˜çŠ¶æ€ - è¿”回托盘的完整状态信息
        /// </summary>
        private async Task<PalletStatusAnalysis> AnalyzePalletStatus(string orderNo, string palletCode, int stockId)
        {
            var result = new PalletStatusAnalysis
            {
                OrderNo = orderNo,
                PalletCode = palletCode,
                StockId = stockId
            };
            // åˆ†æžæœªåˆ†æ‹£çš„出库锁定记录
            var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>()
                .Where(it => it.OrderNo == orderNo &&
                           it.PalletCode == palletCode &&
                           it.Status == (int)OutLockStockStatusEnum.出库中)
                .ToListAsync();
            if (remainingLocks.Any())
            {
                result.HasRemainingLocks = true;
                result.RemainingLocks = remainingLocks;
                result.RemainingLocksReturnQty = remainingLocks.Sum(x => x.AssignQuantity - x.PickedQty);
                _logger.LogInformation($"发现{remainingLocks.Count}条未分拣锁定记录,总数量: {result.RemainingLocksReturnQty}");
            }
            // åˆ†æžæ‰˜ç›˜ä¸Šçš„库存货物
            var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>()
                .Where(it => it.StockId == stockId &&
                     (it.Status == StockStatusEmun.入库确认.ObjToInt() ||
                      it.Status == StockStatusEmun.入库完成.ObjToInt() ||
                      it.Status == StockStatusEmun.出库锁定.ObjToInt()))
                .Where(it => it.StockQuantity > 0)
                .ToListAsync();
            if (palletStockGoods.Any())
            {
                result.HasPalletStockGoods = true;
                result.PalletStockGoods = palletStockGoods;
                result.PalletStockReturnQty = palletStockGoods.Sum(x => x.StockQuantity);
                _logger.LogInformation($"发现{palletStockGoods.Count}个库存货物,总数量: {result.PalletStockReturnQty}");
                // è®°å½•详细状态分布
                var statusGroups = palletStockGoods.GroupBy(x => x.Status);
                foreach (var group in statusGroups)
                {
                    _logger.LogInformation($"库存状态{group.Key}: {group.Count()}个货物,数量: {group.Sum(x => x.StockQuantity)}");
                }
            }
            //分析拆包记录
            var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(it => it.OrderNo == orderNo &&
                           it.PalletCode == palletCode &&
                           !it.IsReverted && it.Status != (int)SplitPackageStatusEnum.已拣选 &&
                           it.Status != (int)SplitPackageStatusEnum.已回库)
                .ToListAsync();
            if (splitRecords.Any())
            {
                result.HasSplitRecords = true;
                result.SplitRecords = splitRecords;
                result.SplitReturnQty = await CalculateSplitReturnQuantity(splitRecords, stockId);
                _logger.LogInformation($"发现{splitRecords.Count}条未拣选拆包记录,总数量: {result.SplitReturnQty}");
            }
            // 4. è®¡ç®—总回库数量和空托盘状态
            result.TotalReturnQty = result.RemainingLocksReturnQty + result.PalletStockReturnQty + result.SplitReturnQty;
            result.HasItemsToReturn = result.TotalReturnQty > 0;
            result.IsEmptyPallet = !result.HasItemsToReturn;
            // 5. æ£€æŸ¥æ˜¯å¦æœ‰è¿›è¡Œä¸­çš„任务
            result.HasActiveTasks = await _taskRepository.Db.Queryable<Dt_Task>()
                .Where(x => x.OrderNo == orderNo && x.TaskType == TaskTypeEnum.InPick.ObjToInt() &&
                           x.PalletCode == palletCode &&
                           x.TaskStatus == (int)TaskStatusEnum.New)
                .AnyAsync();
            _logger.LogInformation($"托盘状态分析完成 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}, " +
                                  $"总回库数量: {result.TotalReturnQty}, æ˜¯å¦ç©ºæ‰˜ç›˜: {result.IsEmptyPallet}, " +
                                  $"有进行中任务: {result.HasActiveTasks}");
            return result;
        }
        /// <summary>
        /// æ£€æŸ¥æ‰˜ç›˜æ˜¯å¦ä¸ºç©º
        /// </summary>
        private async Task<bool> IsPalletEmpty(string orderNo, string palletCode)
        {
            try
            {
                // èŽ·å–åº“å­˜ä¿¡æ¯
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                    .Where(x => x.PalletCode == palletCode)
                    .FirstAsync();
                if (stockInfo == null)
                    return false;
                // ä½¿ç”¨ç»Ÿä¸€çš„状态分析
                var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id);
                return statusAnalysis.IsEmptyPallet;
            }
            catch (Exception ex)
            {
                _logger.LogWarning($"检查托盘是否为空失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
                return false;
            }
        }
        /// <summary>
        /// æ£€æŸ¥å¹¶å¤„理空托盘
        /// </summary>
        private async Task<bool> CheckAndHandleEmptyPallet(string orderNo, string palletCode)
        {
            try
            {
                // 1. èŽ·å–åº“å­˜ä¿¡æ¯
                var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>()
                    .Where(x => x.PalletCode == palletCode)
                    .FirstAsync();
                if (stockInfo == null)
                {
                    _logger.LogWarning($"未找到托盘 {palletCode} çš„库存信息");
                    return false;
                }
                // 2. ä½¿ç”¨ç»Ÿä¸€çš„状态分析
                var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id);
                // 3. æ£€æŸ¥æ˜¯å¦ä¸ºç©ºæ‰˜ç›˜ä¸”没有进行中的任务
                if (!statusAnalysis.IsEmptyPallet || statusAnalysis.HasActiveTasks)
                {
                    return false;
                }
                _logger.LogInformation($"检测到空托盘,开始自动处理 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
                //// æ¸…理零库存数据
                //await CleanupZeroStockData(stockInfo.Id);
                //// æ›´æ–°åº“存主表状态为空托盘
                //await UpdateStockInfoAsEmpty(stockInfo);
                //// å¤„理出库锁定记录
                //await HandleOutStockLockRecords(orderNo, palletCode);
                //// å¤„理任务状态
                //await HandleTaskStatusForEmptyPallet(orderNo, palletCode);
                //// æ›´æ–°è®¢å•数据
                //await UpdateOrderDataForEmptyPallet(orderNo, palletCode);
                ////记录操作历史
                //await RecordAutoEmptyPalletOperation(orderNo, palletCode);
                _logger.LogInformation($"空托盘自动处理完成 - è®¢å•: {orderNo}, æ‰˜ç›˜: {palletCode}");
                return true;
            }
            catch (Exception ex)
            {
                _logger.LogError($"自动处理空托盘失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}");
                return false;
            }
        }
        private async Task<string> GenerateNewBarcode()
        {
            var seq = await _dailySequenceService.GetNextSequenceAsync();
            return "WSLOT" + DateTime.Now.ToString("yyyyMMdd") + seq.ToString()?.PadLeft(5, '0');
        }
        private async Task<Dt_OutStockLockInfo> CreateSplitLockInfo(Dt_OutStockLockInfo originalLock, decimal quantity, string newBarcode)
        {
            var newLockInfo = new Dt_OutStockLockInfo
            {
                OrderNo = originalLock.OrderNo,
                OrderDetailId = originalLock.OrderDetailId,
                BatchNo = originalLock.BatchNo,
                MaterielCode = originalLock.MaterielCode,
                MaterielName = originalLock.MaterielName,
                StockId = originalLock.StockId,
                OrderQuantity = quantity,
                OriginalQuantity = quantity,
                AssignQuantity = quantity,
                PickedQty = quantity,
                LocationCode = originalLock.LocationCode,
                PalletCode = originalLock.PalletCode,
                TaskNum = originalLock.TaskNum,
                Status = (int)OutLockStockStatusEnum.拣选完成,
                Unit = originalLock.Unit,
                SupplyCode = originalLock.SupplyCode,
                OrderType = originalLock.OrderType,
                CurrentBarcode = newBarcode,
                OriginalLockQuantity = quantity,
                IsSplitted = 1,
                ParentLockId = originalLock.Id,
                Operator = App.User.UserName,
                FactoryArea = originalLock.FactoryArea,
                lineNo = originalLock.lineNo,
                WarehouseCode = originalLock.WarehouseCode,
                BarcodeQty=originalLock.BarcodeQty,
                BarcodeUnit=originalLock.BarcodeUnit,
            };
            var newLockId = await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteReturnIdentityAsync();
            newLockInfo.Id = newLockId;
            return newLockInfo;
        }
        private async Task RecordSplitHistory(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail,
            decimal splitQty, decimal remainQty, string newBarcode)
        {
            var splitHistory = new Dt_SplitPackageRecord
            {
                FactoryArea = lockInfo.FactoryArea,
                TaskNum = lockInfo.TaskNum,
                OutStockLockInfoId = lockInfo.Id,
                StockId = stockDetail.StockId,
                Operator = App.User.UserName,
                IsReverted = false,
                OriginalBarcode = stockDetail.Barcode,
                NewBarcode = newBarcode,
                SplitQty = splitQty,
                RemainQuantity = remainQty,
                MaterielCode = lockInfo.MaterielCode,
                SplitTime = DateTime.Now,
                OrderNo = lockInfo.OrderNo,
                PalletCode = lockInfo.PalletCode,
                Status = (int)SplitPackageStatusEnum.已拣选
            };
            await _splitPackageService.Db.Insertable(splitHistory).ExecuteCommandAsync();
        }
        private List<SplitResult> CreateSplitResults(Dt_OutStockLockInfo lockInfo, decimal splitQty, decimal remainQty, string newBarcode, string originalBarcode)
        {
            return new List<SplitResult>
        {
            new SplitResult
            {
                materialCode = lockInfo.MaterielCode,
                supplierCode = lockInfo.SupplyCode,
                quantityTotal = splitQty.ToString("F2"),
                batchNumber = newBarcode,
                batch = lockInfo.BatchNo,
                factory = lockInfo.FactoryArea,
                date = DateTime.Now.ToString("yyyy-MM-dd"),
            },
            new SplitResult
            {
                materialCode = lockInfo.MaterielCode,
                supplierCode = lockInfo.SupplyCode,
                quantityTotal = remainQty.ToString("F2"),
                batchNumber = originalBarcode,
                batch = lockInfo.BatchNo,
                factory = lockInfo.FactoryArea,
                date = DateTime.Now.ToString("yyyy-MM-dd"),
            }
        };
        }
        private async Task UpdateSplitRecordsStatus(string barcode)
        {
            var relatedSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>()
                .Where(it => it.OriginalBarcode == barcode || it.NewBarcode == barcode)
                .Where(it => !it.IsReverted)
                .ToListAsync();
            foreach (var record in relatedSplitRecords)
            {
                record.Status = (int)SplitPackageStatusEnum.已拣选;
                await _splitPackageService.Db.Updateable(record).ExecuteCommandAsync();
            }
        }
        private async Task<int> GenerateTaskNumber()
        {
            return await _dailySequenceService.GetNextSequenceAsync();
        }
        private WebResponseContent CreatePickingResponse(PickingResult result, string adjustedReason)
        {
            //if (result.SplitResults.Any())
            //{
            //    var responseData = new { SplitResults = result.SplitResults, AdjustedReason = "" };
            //    if (!string.IsNullOrEmpty(adjustedReason))
            //    {
            //        responseData = new { SplitResults = result.SplitResults, AdjustedReason = adjustedReason };
            //    }
            //    return WebResponseContent.Instance.OK("拣选确认成功,已自动拆包", responseData);
            //}
            //if (!string.IsNullOrEmpty(adjustedReason))
            //{
            //    return WebResponseContent.Instance.OK($"拣选确认成功({adjustedReason})");
            //}
            //return WebResponseContent.Instance.OK("拣选确认成功");
            if (result.SplitResults.Any())
            {
                return WebResponseContent.Instance.OK("拣选确认成功,已自动拆包", new { SplitResults = result.SplitResults });
            }
            return WebResponseContent.Instance.OK("拣选确认成功", new { SplitResults = new List<SplitResult>() });
        }
        #region è™šæ‹Ÿå‡ºå…¥åº“
        public WebResponseContent GetAvailablePurchaseOrders()
        {
            List<Dt_InboundOrder> InOders = _inboundOrderRepository.QueryData().Where(x => x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt()).ToList();
            List<string> InOderCodes = InOders.Select(x => x.UpperOrderNo).ToList();
            return WebResponseContent.Instance.OK("成功",data: InOderCodes);
        }
        public WebResponseContent GetAvailablePickingOrders()
        {
            List<Dt_OutboundOrder> outOders = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().Where(x => x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt()).ToList();
            List<string> outOderCodes = outOders.Select(x => x.UpperOrderNo).ToList();
            return WebResponseContent.Instance.OK("成功", data: outOderCodes);
        }
        public WebResponseContent BarcodeValidate(NoStockOutModel noStockOut)
        {
            try
            {
                Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOut.inOder && x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt()).Includes(x => x.Details).First();
                if(inboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到采购单:{noStockOut.inOder}");
                }
                var matchedDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == noStockOut.barCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                if (matchedDetail == null)
                {
                    return WebResponseContent.Instance.Error($"在采购单 {noStockOut.inOder} ä¸­æœªæ‰¾åˆ°æ¡ç ä¸º {noStockOut.barCode} çš„æ˜Žç»†ã€‚");
                }
                matchedDetail.NoStockOutQty = 0;
                Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOut.outOder && x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt()).Includes(x => x.Details).First();
                if (outboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到出库单:{noStockOut.inOder}");
                }
                var matchedCode = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == matchedDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                if (matchedCode == null)
                {
                    return WebResponseContent.Instance.Error($"在出库单的物料编码中未找到与采购单中的{matchedDetail.MaterielCode} å¯¹åº”的物料。");
                }
                matchedCode.NoStockOutQty = 0;
                //剩余入库数量即虚拟出入库剩余可出数量
                decimal outQuantity = matchedDetail.OrderQuantity - matchedDetail.ReceiptQuantity;
                if(outQuantity == 0)
                {
                    return WebResponseContent.Instance.Error($"该采购单中的条码对应的可出数量为0");
                }
                if (matchedCode.OrderQuantity < outQuantity)
                {
                    return WebResponseContent.Instance.Error($"该采购单中的条码对应的可出数量超出出库单出库数量{matchedDetail.OrderQuantity - matchedCode.OrderQuantity},不满足整包出库");
                }
                //单据出库锁定数量
                matchedDetail.NoStockOutQty += outQuantity;
                matchedCode.NoStockOutQty += outQuantity;
                if ((matchedCode.LockQuantity + matchedCode.NoStockOutQty) > matchedCode.OrderQuantity)
                {
                   return WebResponseContent.Instance.Error($"出库单明细数量溢出{matchedCode.LockQuantity - matchedCode.OrderQuantity}");
                }
                matchedDetail.OrderDetailStatus = OrderDetailStatusEnum.Inbounding.ObjToInt();
                matchedCode.OrderDetailStatus = OrderDetailStatusEnum.AssignOver.ObjToInt();
                _unitOfWorkManage.BeginTran();
                _inboundOrderDetailService.UpdateData(matchedDetail);
                _outboundOrderDetailService.UpdateData(matchedCode);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK();
            }
            catch(Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        public WebResponseContent DeleteBarcode(NoStockOutModel noStockOut)
        {
            try
            {
                Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOut.inOder && x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt()).Includes(x => x.Details).First();
                if (inboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到采购单:{noStockOut.inOder}");
                }
                var matchedDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == noStockOut.barCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                if (matchedDetail == null)
                {
                    return WebResponseContent.Instance.Error($"在采购单 {noStockOut.inOder} ä¸­æœªæ‰¾åˆ°æ¡ç ä¸º {noStockOut.barCode} çš„æ˜Žç»†ã€‚");
                }
                matchedDetail.NoStockOutQty = 0;
                if(matchedDetail.ReceiptQuantity==0 && matchedDetail.OverInQuantity==0)
                {
                    matchedDetail.OrderDetailStatus = OrderDetailStatusEnum.New.ObjToInt();
                }
                Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOut.outOder && x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt()).Includes(x => x.Details).First();
                if (outboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到出库单:{noStockOut.inOder}");
                }
                var matchedCode = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == matchedDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                if (matchedCode == null)
                {
                    return WebResponseContent.Instance.Error($"在出库单的物料编码中未找到与采购单中的{matchedDetail.MaterielCode} å¯¹åº”的物料。");
                }
                matchedCode.NoStockOutQty = 0;
                if (matchedCode.LockQuantity == 0 && matchedCode.OverOutQuantity == 0)
                {
                    matchedCode.OrderDetailStatus = OrderDetailStatusEnum.New.ObjToInt();
                }
                _unitOfWorkManage.BeginTran();
                _inboundOrderDetailService.UpdateData(matchedDetail);
                _outboundOrderDetailService.UpdateData(matchedCode);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK();
            }
            catch(Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        public WebResponseContent NoStockOutSubmit(NoStockOutSubmit noStockOutSubmit)
        {
            try
            {
                Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOutSubmit.InOderSubmit && x.OrderStatus != InOrderStatusEnum.入库完成.ObjToInt()).Includes(x => x.Details).First();
                if (inboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到采购单:{noStockOutSubmit.InOderSubmit}");
                }
                Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOutSubmit.OutOderSubmit && x.OrderStatus != OutOrderStatusEnum.出库完成.ObjToInt()).Includes(x => x.Details).First();
                if (outboundOrder == null)
                {
                    return WebResponseContent.Instance.Error($"未找到出库单:{noStockOutSubmit.OutOderSubmit}");
                }
                List<Dt_InboundOrderDetail> inboundOrderDetails = new List<Dt_InboundOrderDetail>();
                List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
                foreach (var BarCode in noStockOutSubmit.BarCodeSubmit)
                {
                   var inboundOrderDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == BarCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                    if(inboundOrderDetail == null)
                    {
                        return WebResponseContent.Instance.Error($"在采购单 {noStockOutSubmit.InOderSubmit} ä¸­æœªæ‰¾åˆ°æ¡ç ä¸º {BarCode} çš„æ˜Žç»†ã€‚");
                    }
                    var outboundOrderDetail = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == inboundOrderDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt());
                    if (outboundOrderDetail == null)
                    {
                        return WebResponseContent.Instance.Error($"在出库单的物料编码中未找到与采购单中的{inboundOrderDetail.MaterielCode} å¯¹åº”的物料。");
                    }
                    inboundOrderDetail.ReceiptQuantity += inboundOrderDetail.NoStockOutQty;
                    inboundOrderDetail.OverInQuantity = inboundOrderDetail.ReceiptQuantity;
                    inboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt();
                    inboundOrderDetails.Add(inboundOrderDetail);
                    outboundOrderDetail.LockQuantity += outboundOrderDetail.NoStockOutQty;
                    outboundOrderDetail.OverOutQuantity = outboundOrderDetail.LockQuantity;
                    if(outboundOrderDetail.OrderQuantity == outboundOrderDetail.OverOutQuantity)
                    {
                        outboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt();
                    }
                    outboundOrderDetails.Add(outboundOrderDetail);
                }
                //判断入库单据明细是否全部是完成状态
                int e = inboundOrder.Details.Count();
                int w = inboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                bool inoderOver = inboundOrder.Details.Count() == inboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                if (inoderOver)
                {
                    inboundOrder.OrderStatus = InOrderStatusEnum.入库完成.ObjToInt();
                }
                else
                {
                    inboundOrder.OrderStatus = InOrderStatusEnum.入库中.ObjToInt();
                }
                //判断出库单据明细是否全部是完成状态
                bool outOderOver = outboundOrder.Details.Count() == outboundOrder.Details.Where(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count();
                if (outOderOver)
                {
                    outboundOrder.OrderStatus = OutOrderStatusEnum.出库完成.ObjToInt();
                }
                else
                {
                    outboundOrder.OrderStatus = OutOrderStatusEnum.出库中.ObjToInt();
                }
                //数据处理
                _unitOfWorkManage.BeginTran();
                _inboundOrderDetailService.UpdateData(inboundOrderDetails);
                _outboundOrderDetailService.UpdateData(outboundOrderDetails);
                _inboundOrderRepository.UpdateData(inboundOrder);
                _outboundOrderService.UpdateData(outboundOrder);
                _unitOfWorkManage.CommitTran();
                return WebResponseContent.Instance.OK();
            }
            catch(Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        #endregion
        #endregion
    }
    #region æ”¯æŒç±»å®šä¹‰
    public class ValidationResult<T>
    {
        public bool IsValid { get; set; }
        public T Data { get; set; }
        public string ErrorMessage { get; set; }
        public static ValidationResult<T> Success(T data)
        {
            return new ValidationResult<T>
            {
                IsValid = true,
                Data = data
            };
        }
        public static ValidationResult<T> Error(string message)
        {
            return new ValidationResult<T>
            {
                IsValid = false,
                ErrorMessage = message
            };
        }
    }
    #region è¿”回
    /// <summary>
    /// æ‹†åŒ…链项
    /// </summary>
    public class SplitChainItem
    public class PickingResult
    {
        public Dt_SplitPackageRecord SplitRecord { get; set; }
        public string OriginalBarcode { get; set; }
        public string NewBarcode { get; set; }
        public decimal SplitQuantity { get; set; }
        public Dt_OutStockLockInfo FinalLockInfo { get; set; }
        public string FinalBarcode { get; set; }
        public int FinalStockId { get; set; }
        public decimal ActualPickedQty { get; set; }
        public List<SplitResult> SplitResults { get; set; } = new List<SplitResult>();
    }
    public  class ReturnAnalysisResult
    public class ReturnAnalysisResult
    {
        public bool HasItemsToReturn { get; set; }
        public bool HasRemainingLocks { get; set; }
@@ -1997,7 +2818,56 @@
        public List<Dt_StockInfoDetail> PalletStockGoods { get; set; } = new List<Dt_StockInfoDetail>();
        public List<Dt_SplitPackageRecord> SplitRecords { get; set; } = new List<Dt_SplitPackageRecord>();
    }
    public class PalletStatusAnalysis
    {
        public string OrderNo { get; set; }
        public string PalletCode { get; set; }
        public int StockId { get; set; }
        // å›žåº“相关属性
        public bool HasItemsToReturn { get; set; }
        public bool HasRemainingLocks { get; set; }
        public bool HasPalletStockGoods { get; set; }
        public bool HasSplitRecords { get; set; }
        public decimal RemainingLocksReturnQty { get; set; }
        public decimal PalletStockReturnQty { get; set; }
        public decimal SplitReturnQty { get; set; }
        public decimal TotalReturnQty { get; set; }
        public List<Dt_OutStockLockInfo> RemainingLocks { get; set; } = new List<Dt_OutStockLockInfo>();
        public List<Dt_StockInfoDetail> PalletStockGoods { get; set; } = new List<Dt_StockInfoDetail>();
        public List<Dt_SplitPackageRecord> SplitRecords { get; set; } = new List<Dt_SplitPackageRecord>();
        // ç©ºæ‰˜ç›˜ç›¸å…³å±žæ€§
        public bool IsEmptyPallet { get; set; }
        public bool HasActiveTasks { get; set; }
        // ä¾¿åˆ©æ–¹æ³•
        public bool CanReturn => HasItemsToReturn && !HasActiveTasks;
        public bool CanRemove => IsEmptyPallet && !HasActiveTasks;
    }
    public class PickingContext
    {
        public string OrderNo { get; set; }
        public string PalletCode { get; set; }
        public string Barcode { get; set; }
        public string Operator { get; set; }
        public Dt_OutStockLockInfo LockInfo { get; set; }
        public Dt_OutboundOrderDetail OrderDetail { get; set; }
        public Dt_StockInfoDetail StockDetail { get; set; }
        public decimal ActualQuantity { get; set; }
        public string AdjustedReason { get; set; }
    }
    public class CancelPickingContext
    {
        public string OrderNo { get; set; }
        public string PalletCode { get; set; }
        public string Barcode { get; set; }
        public string Operator { get; set; }
        public decimal CancelQuantity { get; set; }
        public Dt_PickingRecord PickingRecord { get; set; }
        public Dt_OutStockLockInfo LockInfo { get; set; }
        public Dt_OutboundOrderDetail OrderDetail { get; set; }
    }
    #endregion
}