first commit

This commit is contained in:
2026-09-02 16:31:50 +08:00
commit a461727193
911 changed files with 692450 additions and 0 deletions
@@ -0,0 +1,153 @@
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
{
/// <summary>
/// 放置在已有相同批次的列上
/// </summary>
public class StockInByExist : StockInHandler
{
public override async Task<StorageRackDto> CalculateWarehouse(
TransportEnum startArea,
FloorEnum floor,
string materialBatch,
List<StorageRackDto> storageRacks
)
{
//1.查找有没有同批次待执行的任务
//2.有待执行的任务,直接进入中转区域
//3.没有待执行的任务,则采用现有逻辑获取一个同批次库位
//非中转区的任务,需要先判断是否有同批次未完成的任务,如有则需要等待,确保同批次的货物放满同一列
await _SemaphoreSlim.WaitAsync();
StorageRackDto destStorageRack = null;
try
{
storageRacks = await base.GetStorageRackExceptStockout(floor);
if (storageRacks.IsNullOrEmpty())
{
LogHelper.Info("没有可用库位");
return null;
}
List<AGVTaskDto> 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<long> 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<StorageRackDto> 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();
}
}
}
}
@@ -0,0 +1,67 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Infrastructure.Helper;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
{
/// <summary>
/// 和其他不同批次的货物混合放置
/// </summary>
public class StockInByMixing : StockInHandler
{
public override async Task<StorageRackDto> CalculateWarehouse(
TransportEnum startArea,
FloorEnum floor,
string materialBatch,
List<StorageRackDto> storageRacks)
{
if (storageRacks.IsNullOrEmpty())
{
storageRacks = await base.GetStorageRackExceptStockout(floor);
}
if (storageRacks.IsNullOrEmpty())
{
LogHelper.Info("没有可用库位");
return null;
}
List<StorageRackDto> wareHousesSorted = storageRacks.OrderBy(p => p.AreaNumSort).ToList();
IEnumerable<IGrouping<long, StorageRackDto>> groupByColumn = wareHousesSorted.GroupBy(p => p.ReservoirAreaColumnId);
StorageRackDto destStorageRack = null;
foreach (var columnGroup in groupByColumn)
{
bool unlockedColumn = columnGroup.All(p => !p.IsLock);
if (unlockedColumn)
{
destStorageRack = columnGroup
.Where(p => string.IsNullOrEmpty(p.BarCode))
.OrderByDescending(p => p.NumSort)
.FirstOrDefault();
if (destStorageRack != null)
break;
}
}
if (destStorageRack == null && startArea != TransportEnum.TransitArea)
{
destStorageRack = await _storageRackService.GetUsableTransferStorageRack();
if (destStorageRack == null)
{
LogHelper.Warn($"存储区和中转区均无可用的库位");
}
}
return destStorageRack;
}
}
}
@@ -0,0 +1,61 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.Infrastructure.Helper;
using NPOI.SS.Formula.Functions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
{
/// <summary>
/// 放置在一个空白列列上
/// </summary>
public class StockInByNew : StockInHandler
{
public override async Task<StorageRackDto> CalculateWarehouse(
TransportEnum startArea,
FloorEnum floor,
string materialBatch,
List<StorageRackDto> storageRacks)
{
if (storageRacks.IsNullOrEmpty())
{
storageRacks = await base.GetStorageRackExceptStockout(floor);
}
if (storageRacks.IsNullOrEmpty())
{
LogHelper.Info("没有可用库位");
return null;
}
List<StorageRackDto> storageRacksSorted = storageRacks
.OrderBy(p => p.AreaNumSort)
.ThenBy(p => p.ReservoirAreaColumnId).ToList();
var groupByColumn = storageRacksSorted.GroupBy(p => p.ReservoirAreaColumnId);
StorageRackDto destWarehouse = null;
foreach (IGrouping<long, StorageRackDto> column in groupByColumn)
{
if (column.All(p => p.IsEmpty()))
{
destWarehouse = column.OrderByDescending(p => p.NumSort).First();
break;
}
}
if (destWarehouse == null)
{
return await HandleByNext(startArea, floor,materialBatch, storageRacksSorted);
}
else
{
return destWarehouse;
}
}
}
}
@@ -0,0 +1,106 @@
using JSMachine.WMS.App.Dto;
using JSMachine.WMS.App.Dto.Enum;
using JSMachine.WMS.App.IService;
using JSMachine.WMS.Domain.Entity;
using JSMachine.WMS.Infrastructure;
using NPOI.SS.Formula.Functions;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace JSMachine.WMS.Business.StockIn.ServiceImpl.ResponsibilityChain
{
/// <summary>
/// 责任链模式。入库处理基类 。责任流动方向:StockInByExist=》StockInByNew=》StockInByMixing
/// </summary>
public abstract class StockInHandler
{
protected StockInHandler _nextHandler;
protected static IStorageRackService _storageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
protected static IAGVTaskService _agvTaskService = GlobalServericeProvidor.GetService<IAGVTaskService>();
protected static IERPTaskService _erpTaskService = GlobalServericeProvidor.GetService<IERPTaskService>();
protected static IStorageRackService StorageRackService = GlobalServericeProvidor.GetService<IStorageRackService>();
protected static readonly SemaphoreSlim _SemaphoreSlim = new(1,1);
public void SetNextHandler(StockInHandler nextHandler)
{
_nextHandler = nextHandler;
}
/// <summary>
/// 计算目标库位
/// </summary>
/// <param name="startStorageRack">起点</param>
/// <param name="materialBatch">批次</param>
/// <param name="storageRacks">所有未禁用的库位</param>
/// <returns></returns>
public abstract Task<StorageRackDto> CalculateWarehouse(
TransportEnum startArea, FloorEnum floor, string materialBatch, List<StorageRackDto> storageRacks);
/// <summary>
/// 有下一个处理者,则继续执行下一个处理者的逻辑
/// </summary>
/// <param name="startStorageRack">起点</param>
/// <param name="materialBatch">批次</param>
/// <param name="storageRacks">所有未禁用的库位</param>
/// <returns></returns>
public async Task<StorageRackDto> HandleByNext(
TransportEnum startArea,
FloorEnum floor,
string materialBatch,
List<StorageRackDto> storageRacks)
{
if (_nextHandler != null)
{
return await _nextHandler.CalculateWarehouse(startArea, floor, materialBatch, storageRacks);
}
else
{
return null;
}
}
public async Task<List<StorageRackDto>> GetStorageRackExceptStockout(FloorEnum floor)
{
List<StorageRackDto> storageRacks = await StorageRackService.GetListByExpression(p =>
p.Floor == floor &&
p.Transport == TransportEnum.NormalArea);
if(storageRacks.IsNullOrEmpty())
{
return null;
}
List<ERPTaskDto> erpTaskStockOuts = await _erpTaskService
.GetListByExpression(p =>
p.CreationTime > DateTime.Now.AddDays(-1) &&
p.TaskStatus == TaskStatusEnum.InExecution &&
(p.TaskType == TaskTypeEnum.SaleOut || p.TaskType == TaskTypeEnum.ProductionOut)
);
if (!erpTaskStockOuts.IsNullOrEmpty())
{
List<AGVTaskDto> agvTaks = await _agvTaskService.GetListByExpression(p =>
p.CreationTime > DateTime.Now.AddDays(-1) &&
erpTaskStockOuts.Select(x => x.Id).Contains(p.ErpTaskId)
);
if (!agvTaks.IsNullOrEmpty())
{
List<StorageRackDto> startStorageRacks = await StorageRackService.GetListByExpression(p =>
agvTaks.Select(x => x.StartPositionCode).Contains(p.StorageRackNo));
List<long> exceptCols = startStorageRacks.Select(p => p.ReservoirAreaColumnId).Distinct().ToList();
storageRacks.RemoveAll(p => exceptCols.Contains(p.ReservoirAreaColumnId));
}
}
return storageRacks;
}
}
}