using JSMachine.WMS.App.Dto; using JSMachine.WMS.App.Dto.Enum; using JSMachine.WMS.App.ServiceImpl; using JSMachine.WMS.Infrastructure.Helper; using NPOI.OpenXmlFormats.Dml; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain { /// /// 放置在已有相同批次的列上 /// public class StockInByExist : StockInHandler { public override async Task CalculateWarehouse( TransportEnum startArea, FloorEnum floor, string materialBatch, List storageRacks ) { //1.查找有没有同批次待执行的任务 //2.有待执行的任务,直接进入中转区域 //3.没有待执行的任务,则采用现有逻辑获取一个同批次库位 //非中转区的任务,需要先判断是否有同批次未完成的任务,如有则需要等待,确保同批次的货物放满同一列 await _SemaphoreSlim.WaitAsync(); StorageRackDto destStorageRack = null; try { storageRacks = await base.GetStorageRackExceptStockout(floor); if (storageRacks.IsNullOrEmpty()) { LogHelper.Info("没有可用库位"); return null; } List uncompletedTasks = await _agvTaskService.GetListByExpression(p => p.CreationTime > DateTime.Now.Date && p.MateriaBatch == materialBatch && (p.TaskType == TaskTypeEnum.PdaIn || p.TaskType == TaskTypeEnum.TailIn || p.TaskType == TaskTypeEnum.TransferStockIn) && (p.TaskStatus != TaskStatusEnum.Completed) ); List sameBatchColIds = storageRacks .Where(p => p.MateriaBatch == materialBatch || (!uncompletedTasks.IsNullOrEmpty() && uncompletedTasks.Select(x => x.EndPositionCode).Contains(p.StorageRackNo)) ) .Select(p => p.ReservoirAreaColumnId) .Distinct() .ToList(); if (sameBatchColIds.IsNullOrEmpty()) { destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks); } else { List sameBatchInclude = storageRacks .Where(p => sameBatchColIds.Contains(p.ReservoirAreaColumnId)) .ToList(); var sameBatchIncludeCol = sameBatchInclude.GroupBy(p => p.ReservoirAreaColumnId); // 筛选还有空位的列(包含锁定的列) // 有未锁定的列优先取该列 // 没有未锁定的列,则任取一列做中转(如果物料所处位置已经是中转区,则不执行) var useableStorageCol = sameBatchIncludeCol .FirstOrDefault(p => p.All(x => !x.IsLock) && p.Any(x => !x.IsDisabled && string.IsNullOrEmpty(x.BarCode))); if (useableStorageCol != null && useableStorageCol.Any()) { destStorageRack = useableStorageCol .Where(p => !p.IsDisabled && string.IsNullOrEmpty(p.BarCode)) .OrderByDescending(p => p.NumSort) .First(); } else { var emptyCols = sameBatchIncludeCol.Where(p => p.Any(x => !x.IsLock && !x.IsDisabled && string.IsNullOrEmpty(x.BarCode))); if (emptyCols.Any()) { //如果仅有一个空列,则可以开一个新列,保证最多只开2列 if (emptyCols.Count() <= 1) { destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks); } else { //一楼不允许混放或进入中转区。同批次已开启两列后,等待现有任务完成并解锁列。 if (floor == FloorEnum.FirstFloor) { LogHelper.Warn("一楼同批次已开启两列且目标列被锁定,入库任务等待列解锁"); return null; } //物料已在中转区,则不可以继续在中转区生成任务 if (startArea == TransportEnum.TransitArea) { LogHelper.Warn("目标库位所在列被锁定,中转入库任调度需要等待列解锁"); return null; } else { destStorageRack = await _storageRackService.GetUsableTransferStorageRack(); if (destStorageRack == null) { LogHelper.Warn($"中转区已满,尝试放货到空白列或者和其他货物混放"); destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks); } } } } else { destStorageRack = await HandleByNext(startArea, floor, materialBatch, storageRacks); } } } if (destStorageRack != null) { // 先完成数据库锁定,确保数据一致性 await _storageRackService.Lock(destStorageRack); } return destStorageRack; } catch (Exception ex) { LogHelper.Error($"入库责任链处理异常:{ex.Message} \n {ex.StackTrace}"); return null; } finally { _SemaphoreSlim.Release(); } } } }