using AutoMapper; using Kean.Domain.Task.Commands; using Kean.Domain.Task.Enums; using Kean.Domain.Task.Events; using Kean.Domain.Task.Models; using Kean.Domain.Task.Repositories; using Kean.Domain.Task.SharedServices.Proxies; using Kean.Infrastructure.Configuration; using System; using System.Linq; using System.Threading; namespace Kean.Domain.Task.CommandHandlers { /// /// 创建下架命令处理程序 /// public sealed class OutfeedCommandHandler : CommandHandler { private readonly ICommandBus _commandBus; // 命令总线 private readonly INotification _notifications; // 总线通知 private readonly IMapper _mapper; // 模型映射 private readonly ITaskRepository _taskRepository; // 任务仓库 private readonly IWarehouseRepository _warehouseRepository; // 库房仓库 private readonly BarcodeInterpreter _barcodeInterpreter; // 条码解释器 private readonly BarcodeValidator _barcodeValidator; // 条码验证器 private readonly StockProxy _stockProxy; // 仓储域代理 /// /// 依赖注入 /// public OutfeedCommandHandler( ICommandBus commandBus, INotification notifications, IMapper mapper, ITaskRepository taskRepository, IWarehouseRepository warehouseRepository, BarcodeInterpreter barcodeInterpreter, BarcodeValidator barcodeValidator, StockProxy stockProxy) { _commandBus = commandBus; _notifications = notifications; _mapper = mapper; _taskRepository = taskRepository; _warehouseRepository = warehouseRepository; _barcodeInterpreter = barcodeInterpreter; _barcodeValidator = barcodeValidator; _stockProxy = stockProxy; } /// /// 处理程序 /// public override async System.Threading.Tasks.Task Handle(OutfeedCommand command, CancellationToken cancellationToken) { if (command.ValidationResult.IsValid) { // 条码格式校验 if (!await _barcodeValidator.Validate(command.Barcode)) { await _commandBus.Notify(nameof(command.Barcode), "条码格式不正确", command.Barcode, cancellationToken: cancellationToken); return; } // 任务重复 if (await _taskRepository.HasTask(command.Barcode)) { await _commandBus.Notify(nameof(command.Barcode), "托盘正在执行任务", command.Barcode, cancellationToken: cancellationToken); return; } var pallet = await _barcodeInterpreter.Interpret(command.Barcode); // 获取库存位置 var cell = await _stockProxy.GetCell(command.Barcode); if (!cell.HasValue) { await _commandBus.Notify(nameof(command.Barcode), "库存不存在", command.Barcode, cancellationToken: cancellationToken); return; } var original = await _warehouseRepository.GetCellById(cell.Value); // 可用的货位 if (original.State != CellState.Enabled) { await _commandBus.Notify(nameof(command.Barcode), "库存位置不合法", command.Barcode, cancellationToken: cancellationToken); return; } // 获取目标位置 var destinationCache = command.Destination switch { int i when i > 100000 => await _warehouseRepository.GetCacheByArea(i), int i when i < 100000 => await _warehouseRepository.GetStationById(i), long i => await _warehouseRepository.GetCacheByArea((int)i), string s when s.StartsWith("name:") => await _warehouseRepository.GetStationByName(s[5..], command.Warehouse), string s when s.StartsWith("device:") => await _warehouseRepository.GetStationByDevice(s[7..], command.Warehouse), string s when s.StartsWith("cache:") => await _warehouseRepository.GetCacheByCode(s[6..]), Station s => s, _ => null }; var destination = destinationCache; //转换为实际出库站台 if (destinationCache.Group == "Trans") { if (!destinationCache.Pallet.Contains(command.Barcode.Substring(0,3))) { await _commandBus.Notify(nameof(command.Destination), "托盘类型与出库站台不匹配,请检查出库站台", command.Destination, cancellationToken: cancellationToken); return; } destination = await _warehouseRepository.GetOutStationByCell(original.Id, destinationCache.Area); if (destination == null) { var destinationIgnorePath = await _warehouseRepository.GetOutStationByCellIgnorePath(original.Id, destinationCache.Area); if (destinationIgnorePath == null) { await _commandBus.Notify(nameof(command.Destination), "未找到出库站台", command.Destination, cancellationToken: cancellationToken); } else { await _commandBus.Notify(nameof(command.Destination), "设备故障导致无法出库", command.Destination, cancellationToken: cancellationToken); } return; } } //// 获取目标位置 //var destination = command.Destination switch //{ // int i => await _warehouseRepository.GetStationById(i), // long i => await _warehouseRepository.GetStationById((int)i), // string s when s.StartsWith("name:") => await _warehouseRepository.GetStationByName(s[5..], command.Warehouse), // string s when s.StartsWith("device:") => await _warehouseRepository.GetStationByDevice(s[7..], command.Warehouse), // Station s => s, // _ => null //}; //// 目标位置可用性 //if (destination?.Warehouse != command.Warehouse // || destination.AllowOut != true // || destination.Pallet?.Contains(pallet) == false // || destination.Spec?.Adaptive(await _stockProxy.GetSpec(command.Barcode) ?? original.Spec) == false) //{ // await _commandBus.Notify(nameof(command.Destination), "目标位置不正确", command.Destination, // cancellationToken: cancellationToken); // return; //} // 时间戳 command.Timestamp ??= DateTime.Now; // 切点事件 var event0 = _mapper.Map(command); event0.Original = original; event0.Destination = destination; await _commandBus.Trigger(event0, cancellationToken); if (!_notifications.Any()) { // 锁定库存位置 await _warehouseRepository.LockCell(original.Id); // 非手动模式下的双深干涉处理 if (!command.Manual) { var avert = new AvertCommand { Cell = original, Behavior = AvertCommand.Leaving, Priority = command.Priority, Previous = command.Previous, Timestamp = command.Timestamp }; await _commandBus.Execute(avert, cancellationToken); if (_notifications.Any()) { return; } if (avert.Id.HasValue) { command.Previous = avert.Id; } } // 创建任务 var task = _mapper.Map(command); if (task.Barcode.Substring(2, 1) == "G") { task.Type = TaskType.Outfeed; } else { task.Type = TaskType.ApplyOut; } task.Original = original.Id; task.Destination = destination.Id; Output(nameof(command.Id), await _taskRepository.CreateTask(task)); // 成功事件 var event1 = _mapper.Map(command); event1.Original = original; event1.Destination = destination; await _commandBus.Trigger(event1, cancellationToken); // 手动 if (command.Manual && !_notifications.Any()) { await _commandBus.Execute(new CompleteCommand { Id = command.Id }, cancellationToken); } } } else { await _commandBus.Notify(command.ValidationResult, cancellationToken: cancellationToken); } } } }