using Microsoft.Extensions.Configuration; using WIDESEA_Common.LocationEnum; using WIDESEA_Common.StockEnum; using WIDESEA_Common.TaskEnum; using WIDESEA_Core; using WIDESEA_Core.Helper; using WIDESEA_DTO.Task; using WIDESEA_Model.Models; namespace WIDESEA_TaskInfoService { public partial class TaskService { #region 自动出库任务 /// /// 自动创建出库任务 - 查询到期库存并创建任务 /// public async Task CreateAutoOutboundTasksAsync() { try { // 1. 查询到期库存 var expiredStocks = await _stockInfoService.Repository .QueryDataNavAsync(s => s.OutboundDate <= DateTime.Now && s.StockStatus == StockStatusEmun.入库完成.GetHashCode()); if (expiredStocks == null || !expiredStocks.Any()) { return WebResponseContent.Instance.OK("无到期库存需要处理"); } // 过滤有位置且位置有库存的记录 expiredStocks = expiredStocks .Where(s => s.LocationDetails != null && s.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode()) .ToList(); if (!expiredStocks.Any()) { return WebResponseContent.Instance.OK("无符合条件的到期库存"); } // 2. 检查已存在的任务 var palletCodes = expiredStocks.Select(s => s.PalletCode).ToList(); var existingTasks = await Repository.QueryDataAsync(t => palletCodes.Contains(t.PalletCode) && (t.TaskStatus == TaskStatusEnum.New.GetHashCode() || t.TaskStatus == TaskStatusEnum.SC_Executing.GetHashCode() || t.TaskStatus == TaskInStatusEnum.InNew.GetHashCode())); var processedPallets = existingTasks.Select(t => t.PalletCode).ToHashSet(); // 3. 筛选需要处理的库存 var stocksToProcess = expiredStocks .Where(s => !processedPallets.Contains(s.PalletCode)) .ToList(); if (!stocksToProcess.Any()) { return WebResponseContent.Instance.OK("所有到期库存已存在任务"); } // 4. 获取配置的目标地址映射 var targetAddressMap = _configuration.GetSection("AutoOutboundTask:TargetAddresses") .Get>>() ?? new Dictionary>(); // 5. 批量创建任务 var taskList = new List(); foreach (var stock in stocksToProcess) { // 根据巷道确定目标地址(优先根据 Remark 确定,Remark 为空则根据巷道配置) var targetAddress = DetermineTargetAddressByRemark( stock.Remark ?? "", stock.LocationDetails?.RoadwayNo ?? "", targetAddressMap); var task = new Dt_Task { WarehouseId = stock.WarehouseId, PalletCode = stock.PalletCode, PalletType = stock.PalletType, SourceAddress = stock.LocationCode, CurrentAddress = stock.LocationCode, NextAddress = targetAddress, TargetAddress = targetAddress, Roadway = stock.LocationDetails?.RoadwayNo ?? "", TaskType = TaskTypeEnum.Outbound.GetHashCode(), TaskStatus = TaskStatusEnum.New.GetHashCode(), Grade = 1, TaskNum = await BaseDal.GetTaskNo(), Creater = "system_auto" }; taskList.Add(task); } var transactionResult = await _unitOfWorkManage.BeginTranAsync(async () => { var addResult = await BaseDal.AddDataAsync(taskList) > 0; if (!addResult) { return WebResponseContent.Instance.Error($"批量创建任务失败,共 {taskList.Count} 个任务"); } // 任务创建成功后,同步锁定库存和货位状态,避免重复分配 var stocksToUpdate = stocksToProcess .Select(s => { s.StockStatus = StockStatusEmun.出库锁定.GetHashCode(); return s; }) .ToList(); var updateStockResult = await _stockInfoService.Repository.UpdateDataAsync(stocksToUpdate); if (!updateStockResult) { return WebResponseContent.Instance.Error($"任务创建成功,但库存状态更新失败,共 {stocksToUpdate.Count} 条"); } var locationsToUpdate = stocksToProcess .Where(s => s.LocationDetails != null) .GroupBy(s => s.LocationDetails.Id) .Select(g => { var location = g.First().LocationDetails; location.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode(); return location; }) .ToList(); if (locationsToUpdate.Any()) { var updateLocationResult = await _locationInfoService.Repository.UpdateDataAsync(locationsToUpdate); if (!updateLocationResult) { return WebResponseContent.Instance.Error($"任务创建成功,但货位状态更新失败,共 {locationsToUpdate.Count} 条"); } } return WebResponseContent.Instance.OK(); }); if (!transactionResult.Status) { return transactionResult; } // 6. 通知 WCS(异步,不影响主流程) _ = Task.Run(() => { try { var wmstaskDtos = _mapper.Map>(taskList); _httpClientHelper.Post( "http://localhost:9292/api/Task/ReceiveTask", wmstaskDtos.ToJson()); } catch (Exception ex) { // WCS 通知失败不影响任务创建,记录日志即可 Console.WriteLine($"WCS 批量通知失败,任务数量: {taskList.Count}, 错误: {ex.Message}"); } }); return WebResponseContent.Instance.OK($"成功创建 {taskList.Count} 个出库任务", taskList.Count); } catch (Exception ex) { return WebResponseContent.Instance.Error($"自动创建出库任务失败: {ex.Message}"); } } #endregion 自动出库任务 } }