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.Domain.Task.Strategies; using Kean.Infrastructure.Configuration; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Linq; using System.Threading; namespace Kean.Domain.Task.CommandHandlers { /// /// 创建上架命令处理程序 /// public sealed class InfeedCommandHandler : 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; // 仓储域代理 private readonly InfeedContext _infeedContext; // 上架策略 private readonly MaterialProxy _materialProxy; /// /// 依赖注入 /// public InfeedCommandHandler( IServiceProvider serviceProvider, ICommandBus commandBus, INotification notifications, IMapper mapper, ITaskRepository taskRepository, IWarehouseRepository warehouseRepository, BarcodeInterpreter barcodeInterpreter, BarcodeValidator barcodeValidator, StockProxy stockProxy, MaterialProxy materialProxy) { _commandBus = commandBus; _notifications = notifications; _mapper = mapper; _taskRepository = taskRepository; _warehouseRepository = warehouseRepository; _barcodeInterpreter = barcodeInterpreter; _barcodeValidator = barcodeValidator; _stockProxy = stockProxy; _materialProxy = materialProxy; _infeedContext = new(serviceProvider); } /// /// 处理程序 /// public override async System.Threading.Tasks.Task Handle(InfeedCommand command, CancellationToken cancellationToken) { if (command.ValidationResult.IsValid) { // 电气异常报警(检尺异常) if (!string.IsNullOrEmpty(command.Parameters) && command.Parameters.StartsWith("errorMessage:")) { await _commandBus.Notify(null, $"{command.Parameters[13..]}", null, cancellationToken: cancellationToken); return; } // 条码格式校验 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 original = command.Original 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 (original?.Warehouse != command.Warehouse || original.AllowIn != true || original.Pallet?.Contains(pallet) == false || original.Spec?.Adaptive(command.Spec) == false) { await _commandBus.Notify(nameof(command.Original), "操作位置不正确", command.Original, cancellationToken: cancellationToken); return; } //校验 托盘规则与申请位置是否匹配 Models.Warehouse warehouse = await _warehouseRepository.GetWarehouse(original.Warehouse); if (!warehouse.Code.Contains(command.Barcode.Substring(2, 1))) { await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})不能进入({warehouse.Name})库房", command.Barcode, cancellationToken: cancellationToken); return; } if (!(await _stockProxy.GetCell(command.Barcode)).HasValue) { //@=@ 固定值 if (command.Tag == "3") { int? endDevice = original?.Id; var sGoods = await _materialProxy.GetMaterial(null, (string)pallet, true); Models.Material goods = JsonConvert.DeserializeObject(sGoods); if (goods == null) { await _commandBus.Notify(nameof(command.Original), "空托盘条码解析类型失败", command.Original, cancellationToken: cancellationToken); return; } int? goodsid = goods.Id; if (goodsid.HasValue) { command.Material = goodsid.Value; } await _stockProxy.PalletIn( command.Barcode, endDevice.Value, goodsid.Value, command.Operator, command.Tag ); } else { await _commandBus.Notify(nameof(command.Barcode), "托盘没有组盘入库,请先组盘", command.Barcode, cancellationToken: cancellationToken); return; } } else { command.Material = await _stockProxy.GetStorageMaterial(command.Barcode); bool bPallet = await _taskRepository.IsPallet(command.Material.Value); //@=@ 固定值 if (command.Tag == "3") { //if (command.Material == 5 || command.Material == 9 || command.Material == 57 || command.Material == 58 || command.Material == 60 || command.Material == 61 || command.Material == 62) if (bPallet) { } else { await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})已经有库存,无法空托盘入库", command.Barcode, cancellationToken: cancellationToken); return; } } else { //command.Material = await _stockProxy.GetStorageMaterial(command.Barcode); } //称重校验 if (command.Barcode.Substring(2,1) != "F" && command.Barcode.Substring(2, 1) != "G") { var sGoods = await _materialProxy.GetMaterial(command.Material, null, false); Models.Material goods = JsonConvert.DeserializeObject(sGoods); if (bPallet) { try { if (Math.Abs(decimal.Parse(command.Parameters) - goods.Weight.Value) / goods.Weight >= (decimal)0.1) { await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})称重({command.Parameters})与规定托盘重量({goods.Weight})不匹配", command.Barcode, cancellationToken: cancellationToken); return; } } catch { await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})称重({command.Parameters}),规定托盘重量({goods.Weight})参数有异常", command.Barcode, cancellationToken: cancellationToken); return; } } else { try { var sStockGoods = await _materialProxy.GetMaterial(null, goods.Model, false); Models.Material stockGoods = JsonConvert.DeserializeObject(sStockGoods); IEnumerable stockLines = await _taskRepository.GetLines(command.Barcode); string inputWeight = command.Parameters == null ? "0" : command.Parameters; //减去托盘重量 decimal remainingWeight = decimal.Parse(inputWeight) - stockGoods.Weight.Value; decimal unitWeight = 0; foreach (var m in stockLines) { var sMGoods = await _materialProxy.GetMaterial(m.Material, null, false); Models.Material mGoods = JsonConvert.DeserializeObject(sMGoods); remainingWeight = remainingWeight - mGoods.Weight.Value * m.Quantity; if (unitWeight == 0) { unitWeight = mGoods.Weight.Value; } else if (m.Weight.Value < unitWeight) { unitWeight = mGoods.Weight.Value; } } if (Math.Abs(remainingWeight) > unitWeight) { await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})称重({command.Parameters})与规定物料重量({goods.Weight})不匹配", command.Barcode, cancellationToken: cancellationToken); return; } } catch { await _commandBus.Notify(nameof(command.Barcode), $"托盘({command.Barcode})称重({command.Parameters}),规定物料重量({goods.Weight})参数有异常", command.Barcode, cancellationToken: cancellationToken); return; } } } } if (!command.Material.HasValue) { } // 不允许操作立库中的库存 var cell = await _stockProxy.GetCell(command.Barcode); if (cell.HasValue && await _warehouseRepository.IsCell(cell.Value) == true) { await _commandBus.Notify(nameof(command.Barcode), "不允许操作立库中的库存", command.Barcode, cancellationToken: cancellationToken); return; } //获取库存信息 string storageTag = await _taskRepository.GetBypassTag(command.Barcode); string requestNo = string.Empty; if (!string.IsNullOrEmpty(storageTag)) { try { JObject jObj = JObject.Parse(storageTag); JToken endCell = jObj.SelectToken("Cell"); if (endCell != null) { command.Destination = "device:" + Convert.ToString(jObj["Cell"]); } JToken requestNoFlg = jObj.SelectToken("requestNo"); if (requestNoFlg != null) { requestNo = Convert.ToString(jObj["requestNo"]); } } catch { } } // 获取目标位置 var destination = command.Destination switch { int i => await _warehouseRepository.GetCellById(i), long i => await _warehouseRepository.GetCellById((int)i), string s when s.StartsWith("name:") => await _warehouseRepository.GetCellByName(s[5..], command.Warehouse), string s when s.StartsWith("device:") => await _warehouseRepository.GetCellByDevice(s[7..], command.Warehouse), Cell c => c, _ => null }; // 目标位置校验 if (destination != null) { // 目标位置可用性 if (destination?.Warehouse != command.Warehouse || destination.IsEmpty != true || destination.State != CellState.Enabled || destination.Pallet?.Contains(pallet) == false || destination.Spec?.Adaptive(command.Spec) == false) { await _commandBus.Notify(nameof(command.Destination), "目标位置不可用", command.Destination, cancellationToken: cancellationToken); return; } } // 自动分配目标位置 else { if (command.Manual) { await _commandBus.Notify(nameof(command.Destination), "未指定目标位置", command.Destination, cancellationToken: cancellationToken); return; } destination = await _infeedContext.AutoDestination(pallet, original, command.Spec, command.Material); if (destination == null) { await _commandBus.Notify(nameof(command.Destination), "设备故障,请先排除故障", command.Destination, cancellationToken: cancellationToken); return; } } //车架时 if (command.Barcode.Substring(2, 1) == "F") { string originalStockBarcode = command.Barcode; command.Barcode = "LCF" + System.DateTime.Now.ToString("yyyy-MM-ddHH:mm:ss.fff").Replace("-", "").Replace(":", "").Replace(".", ""); await _taskRepository.ReSetStockBarcode(originalStockBarcode, command.Barcode); } // 时间戳 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(destination.Id); // 非手动模式下的双深避让处理 if (!command.Manual) { var avert = new AvertCommand { Cell = destination, Behavior = AvertCommand.Entering, 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); task.Type = TaskType.Infeed; task.Original = original.Id; task.Destination = destination.Id; task.RequestNo = requestNo; 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); } } } }