using Mapster; using Masuit.Tools; using SixLabors.Fonts.Tables.AdvancedTypographic; using SqlSugar; using System; using System.Text.RegularExpressions; using System.Threading.Tasks; using WIDESEA_Cache; using WIDESEA_Core.Const; using WIDESEA_DTO.Location; using WIDESEA_DTO.WMS; using WIDESEA_IServices; using WIDESEA_Model.Models; using WIDESEAWCS_BasicInfoRepository; using WIDESEAWCS_QuartzJob.Models; namespace WIDESEA_StorageTaskServices; public partial class Dt_TaskService : ServiceBase, IDt_TaskService { private readonly LogFactory LogFactory = new LogFactory(); private readonly IUnitOfWorkManage _unitOfWorkManage; private readonly IStockInfoRepository _stockInfoRepository; private readonly IStockInfoDetailRepository _stockInfoDetailRepository; private readonly IDt_Task_HtyRepository _task_HtyRepository; private readonly IMapper _mapper; private readonly ILocationInfoRepository _locationRepository; private readonly ITaskExecuteDetailRepository _taskExecuteDetailRepository; private readonly ILocationStatusChangeRecordRepository _locationStatusChangeRecordRepository; private readonly IBoxingInfoRepository _boxingInfoRepository; //组盘 private readonly IDt_AreaInfoRepository _areaInfoRepository; //区域 private readonly IDt_StationManagerRepository _stationManagerRepository; private readonly ISys_ConfigService _configService; private readonly ISimpleCacheService _simpleCacheService; public Dt_TaskService(IDt_TaskRepository BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoRepository stockInfoRepository, IDt_Task_HtyRepository task_HtyRepository, IMapper mapper, ILocationInfoRepository locationRepository, ITaskExecuteDetailRepository taskExecuteDetailRepository, ILocationStatusChangeRecordRepository locationStatusChangeRecordRepository, IBoxingInfoRepository boxingInfoRepository, IDt_AreaInfoRepository areaInfoRepository, IStockInfoDetailRepository stockInfoDetailRepository, IDt_StationManagerRepository stationManagerRepository, ISys_ConfigService configService, ISimpleCacheService simpleCacheService) : base(BaseDal) { _unitOfWorkManage = unitOfWorkManage; _stockInfoRepository = stockInfoRepository; _task_HtyRepository = task_HtyRepository; _mapper = mapper; _locationRepository = locationRepository; _taskExecuteDetailRepository = taskExecuteDetailRepository; _locationStatusChangeRecordRepository = locationStatusChangeRecordRepository; _boxingInfoRepository = boxingInfoRepository; _areaInfoRepository = areaInfoRepository; _stockInfoDetailRepository = stockInfoDetailRepository; _stationManagerRepository = stationManagerRepository; _configService = configService; _simpleCacheService = simpleCacheService; } #region 外部接口方法 #region 出库任务完成 public async Task CompleteOutboundTaskAsync(Dt_Task task, DtStockInfo stock) { WebResponseContent content = new WebResponseContent(); try { } catch (Exception ex) { return content.Error(ex.Message); } return content; } private async Task DeleteStockInfoAsync(int stockId) { var isStockUpdated = await _stockInfoRepository.DeleteDataByIdAsync(stockId); if (!isStockUpdated) { throw new Exception("库存信息更新失败"); } } private async Task AddStockInfoHtyAsync(DtStockInfo_Hty dtStock) { var isStockAdd = await SqlSugarHelper.DbWMS.InsertNav(dtStock).IncludesAllFirstLayer().ExecuteCommandAsync(); if (!isStockAdd) { throw new Exception("库存历史信息添加失败"); } } private async Task UpdateLocationAsync(DtLocationInfo info) { var isStockUpdated = await _locationRepository.UpdateDataAsync(info); if (!isStockUpdated) { throw new Exception("库位信息更新失败"); } } private async Task DeleteStockInfoDetailsAsync(IEnumerable details) { var ids = details.Select(x => (object)x.Id).ToArray(); var isStockDetailUpdated = await _stockInfoDetailRepository.DeleteDataByIdsAsync(ids); if (!isStockDetailUpdated) { throw new Exception("库存详情信息更新失败"); } } private async Task DeleteTaskAsync(int taskId) { var isTaskUpdated = await BaseDal.DeleteDataByIdAsync(taskId); if (!isTaskUpdated) { throw new Exception("任务信息更新失败"); } } private async Task AddTaskHtyAsync(Dt_Task_Hty taskHty) { var isTaskAdd = await _task_HtyRepository.AddDataAsync(taskHty) > 0; if (!isTaskAdd) { throw new Exception("历史任务信息添加失败"); } } #endregion 出库任务完成 #region 移库任务完成 /// /// 移库任务完成 /// /// 任务数据合集 /// 返回结果集 public async Task CompleteTransferTaskAsync(Dt_Task task, DtStockInfo stock) { WebResponseContent content = new WebResponseContent(); try { // 更新货位和库存信息 (DtStockInfo updateStock, DtLocationInfo locationInForm, DtLocationInfo locationInfoTo) = UpdateStockLocation(stock, task); var taskHty = CreateHistoricalTask(task); LogFactory.GetLog("任务完成").InfoFormat(true, "移库任务完成", $"货位地址:{task.TargetAddress},修改后库存数据:{JsonConvert.SerializeObject(updateStock)},原先货位数据:{locationInForm}"); // 执行数据库事务 bool isResult = false; if (isResult) content.OK("移库任务完成成功"); else content.Error("移库任务完成失败"); } catch (Exception err) { Console.WriteLine(err.Message.ToString()); } return content; } #endregion 移库任务完成 #region 入库任务完成 /// /// 完成入库任务 /// /// 任务数据合集 /// 返回结果集 public async Task CompleteInboundTaskAsync(Dt_Task task) { // 初始化响应内容 WebResponseContent content = new WebResponseContent(); try { } catch (Exception ex) { content.Error(ex.Message); } return content; } #endregion 入库任务完成 #region 任务完成 /// /// 完成任务 /// /// 任务编号 /// 返回结果集 public async Task CompleteAsync(int taskNum) { // 初始化响应内容 WebResponseContent content = new WebResponseContent(); // 提取任务数据 LogFactory.GetLog("任务完成").InfoFormat(true, "提取任务数据", $"任务号:{taskNum}"); // 验证任务是否存在 var task = await GetByTaskNum(taskNum); if (task == null) { return content.Error("任务不存在"); } LogFactory.GetLog("任务完成").InfoFormat(true, "验证任务是否存在", JsonConvert.SerializeObject(task)); // 验证库存是否存在 var stock = await _stockInfoRepository.QueryFirstNavAsync(x => x.PalletCode == task.PalletCode); // 根据任务类型调用相应的完成任务方法 switch (task.TaskType) { case (int)TaskInboundTypeEnum.Inbound: case (int)TaskInboundTypeEnum.InTray: case (int)TaskInboundTypeEnum.InNG: case (int)TaskInboundTypeEnum.InQuality: LogFactory.GetLog("任务完成").InfoFormat(true, "入库任务", ""); return await CompleteInboundTaskAsync(task); case (int)TaskOutboundTypeEnum.OutTray: case (int)TaskOutboundTypeEnum.Outbound: case (int)TaskOutboundTypeEnum.OutNG: LogFactory.GetLog("任务完成").InfoFormat(true, "出库任务", ""); return await CompleteOutboundTaskAsync(task, stock); case (int)TaskRelocationTypeEnum.Relocation: return await CompleteTransferTaskAsync(task, stock); default: return content.Error("任务类型不存在"); } } #endregion 任务完成 #region 请求任务入库 public async Task RequestTaskAsync(RequestTaskDto input) { WebResponseContent content = new WebResponseContent(); try { } catch (Exception err) { // 更详细的异常处理,可以根据异常类型记录不同的错误日志等 content.Error(err.Message); Console.WriteLine(err.Message); LogFactory.GetLog($"请求入库异常").Info(true, $"异常信息【{err.Message}】异常行【{err.StackTrace}】"); } return content; } /// /// 更新任务货位 /// /// /// public async Task UpdateExistingTask(RequestTaskDto input) { WebResponseContent content = new WebResponseContent(); try { var task = await BaseDal.QueryFirstAsync(x => x.PalletCode == input.PalletCode); if (task == null) return content.Error($"暂未找到【{input.PalletCode}】的任务"); return content = await UpdateExistingTask(input, task); } catch (Exception err) { return content.Error(err.Message); } } /// /// 空托盘入库申请 /// /// /// public async Task RequestTrayInTaskAsync(RequestTaskDto input) { WebResponseContent content = new WebResponseContent(); try { content.OK(); } catch (Exception ex) { content.Error(ex.Message); } return content; } #endregion 请求任务入库 #region 请求出库(实盘&空盘) /// /// 请求托盘任务 /// /// 目标位置 /// 托盘类型(1:实盘,2:空盘) /// 区域编码 /// 巷道编码集合 /// 返回结果集 public async Task RequestTrayOutTaskAsync(string position, int tag, string areaCode, List areaCodes, string productionLine) { WebResponseContent content = new WebResponseContent(); try { // 返回成功响应 return content.OK(); } catch (Exception ex) { // 记录异常信息并抛出 LogFactory.GetLog("请求托盘任务").Error(true, ex); ConsoleHelper.WriteErrorLine("请求空/实托盘任务" + ex.Message + "\r\n" + ex.StackTrace); return content.Error(ex.Message); } } /// /// 创建任务实例 /// private Dt_Task CreateTask(DtStockInfo stockInfo, string position, int tag) { return new Dt_Task { Grade = tag == 104 ? (stockInfo.LocationInfo.RoadwayNo.Contains("CWSC") ? 1 : 2) : (stockInfo.LocationInfo.RoadwayNo.Contains("CWSC") ? 2 : 1), Roadway = stockInfo.LocationInfo.RoadwayNo, TargetAddress = position, Dispatchertime = DateTime.Now, MaterialNo = "", NextAddress = position, OrderNo = null, PalletCode = stockInfo.PalletCode, SourceAddress = stockInfo.LocationCode, CurrentAddress = stockInfo.LocationCode, TaskState = (int)TaskOutStatusEnum.OutNew, TaskType = tag, TaskNum = BaseDal.GetTaskNo().Result, Creater = "System", // 修正拼写错误 CreateDate = DateTime.Now, TaskId = 0, ProductionLine = stockInfo.ProductionLine, ProcessCode = stockInfo.ProcessCode, }; } /// /// 创建任务DTO /// private WMSTaskDTO CreateTaskDTO(Dt_Task task) { return new WMSTaskDTO { TaskNum = task.TaskNum.Value, Grade = task.Grade.Value, PalletCode = task.PalletCode, RoadWay = task.Roadway, SourceAddress = task.SourceAddress, TargetAddress = task.TargetAddress, TaskState = task.TaskState.Value, Id = 0, TaskType = task.TaskType, ProductionLine = task.ProductionLine, }; } #endregion 请求出库(实盘&空盘) #region 任务状态更改 /// /// 更新任务状态&出库解盘 /// /// /// /// public async Task UpdateTaskStatus(int taskNum, int taskState) { WebResponseContent content = new WebResponseContent(); try { var task = await BaseDal.QueryFirstAsync(x => x.TaskNum == taskNum); if (task == null) return content.Error("未找到任务"); if (taskState == (int)TaskOutStatusEnum.Line_OutFinish || taskState == (int)TaskInStatusEnum.SC_InFinish) { var taskHty = CreateHistoricalTask(task); await _unitOfWorkManage.UseTranAsync(async () => { var asb = await BaseDal.DeleteDataByIdAsync(task.TaskId); var asbHty = await _task_HtyRepository.AddDataAsync(taskHty) > 0; if (asb && asbHty) content.OK(); else throw new Exception(); }); content.OK(); } else { task.TaskState = taskState; var asb = await BaseDal.UpdateDataAsync(task); if (asb) content.OK(); else content.Error(); } } catch (Exception ex) { content.Error(ex.Message); } return content; } #endregion 任务状态更改 #endregion 外部接口方法 #region 内部调用方法 public override WebResponseContent DeleteData(object[] key) { WebResponseContent content = new WebResponseContent(); // 创建历史任务实例模型 try { foreach (var item in key) { Dt_Task task = BaseDal.QueryFirst(x => x.TaskId == Convert.ToInt32(key)); if (task == null) { return content.Error("未找到任务信息!"); } var taskHtyNG = CreateHistoricalTask(task, true); // 添加历史任务 var isTaskHtyAdd = _task_HtyRepository.AddData(taskHtyNG) > 0; // 删除任务数据 var isTaskDelete = BaseDal.Delete(task.TaskId); } return content.OK("删除成功!"); } catch (Exception ex) { return content.Error("删除任务异常:" + ex.Message); } } /// /// 根据任务号获取任务 /// /// /// public async Task GetByTaskNum(int taskNum) { return await BaseDal.QueryFirstAsync(x => x.TaskNum == taskNum); } #endregion 内部调用方法 #region private 内部方法 /// /// 创建历史任务记录 /// /// /// private Dt_Task_Hty CreateHistoricalTask(Dt_Task task, bool isHand = false) { // 更新任务状态 task.TaskState = task.TaskType > 199 ? (int)TaskInStatusEnum.InFinish : (int)TaskOutStatusEnum.OutFinish; task.CurrentAddress = task.NextAddress; // 创建历史任务 var taskHty = _mapper.Map(task); taskHty.FinishTime = DateTime.Now; taskHty.TaskId = 0; taskHty.OperateType = isHand ? (int)OperateTypeEnum.人工删除 : App.User.UserName != null ? (int)OperateTypeEnum.人工完成 : (int)OperateTypeEnum.自动完成; taskHty.SourceId = task.TaskId; if (isHand) { taskHty.Creater = App.User.UserName != null ? App.User.UserName : "System"; } return taskHty; } /// /// 更新库存位置 /// /// 库存对象 /// 目标位置 // 更新库存和位置信息 private (DtStockInfo, DtLocationInfo, DtLocationInfo) UpdateStockLocation(DtStockInfo stock, Dt_Task task) { //修改来源库位和 目标库位状态 var fromLocation = _locationRepository.QueryFirst(x => x.LocationCode == task.SourceAddress && x.RoadwayNo == task.Roadway); fromLocation.LocationStatus = LocationEnum.Free.ObjToInt(); var toLocation = _locationRepository.QueryFirst(x => x.LocationCode == task.SourceAddress && x.RoadwayNo == task.Roadway); toLocation.LocationStatus = LocationEnum.InStock.ObjToInt(); // 将库存位置设置为目标位置 stock.LocationCode = task.TargetAddress; // 返回更新后的库存和位置信息 return (stock, fromLocation, toLocation); } #region 任务请求方法 private static readonly SemaphoreSlim _semaphoreUpdate = new SemaphoreSlim(1, 1); // 更新任务货位 private async Task UpdateExistingTask(RequestTaskDto input, Dt_Task task) { await _semaphoreUpdate.WaitAsync(); try { if (task == null) { return new WebResponseContent().Error("任务对象为空"); } try { // 创建WebResponseContent对象 var content = new WebResponseContent(); var location = await GetEmptyLocation(task.Roadway); if (location == null) { return content.Error("无法获取货位信息"); } string toAddress = location.LocationCode; int taskState = (int)TaskInStatusEnum.Line_InFinish; int beforeStatus = location.LocationStatus; // 更新货位信息 location.LocationStatus = (int)LocationEnum.Lock; // 更新任务信息 MapTaskProperties(task, input, toAddress, taskState); // 开始事务 var isResult = await UpdateTaskAsync(task, location, beforeStatus); if (!isResult) { _unitOfWorkManage.RollbackTran(); return content.Error("更新任务失败"); } // 提交事务 _unitOfWorkManage.CommitTran(); return content.OK(data: task); } catch (Exception ex) { // 回滚事务 _unitOfWorkManage.RollbackTran(); // 这里可以添加日志记录 return new WebResponseContent().Error($"更新任务时发生错误: {ex.Message}"); } } catch (Exception) { throw; } finally { _semaphoreUpdate.Release(); } } private void MapTaskProperties(Dt_Task task, RequestTaskDto input, string toAddress, int taskState) { task.CurrentAddress = input.Position; task.TargetAddress = toAddress; task.NextAddress = toAddress; task.TaskState = taskState; } // 修改任务 private async Task UpdateTaskAsync(Dt_Task task, DtLocationInfo location, int beforeStatus) { bool isResult = await BaseDal.UpdateDataAsync(task); bool isTaskDetail = await _taskExecuteDetailRepository.AddDetailAsync(task, false, TaskDescription.GetTaskUpdateDescription(task.PalletCode, task.CurrentAddress, task.TargetAddress, TaskInStatusEnum.Line_InFinish.GetIntegralRuleTypeEnumDesc())); LocationChangeRecordDto changeRecordDto = new LocationChangeRecordDto() { AfterStatus = location.LocationStatus, BeforeStatus = beforeStatus, TaskNum = task.TaskNum.Value, LocationId = location.Id, LocationCode = location.LocationCode, ChangeType = (int)StatusChangeTypeEnum.AutomaticStorage, }; bool isUpdateChange = _locationStatusChangeRecordRepository.AddStatusChangeRecord(changeRecordDto); bool isUpdateLo = await _locationRepository.UpdateDataAsync(location); return isResult && isUpdateLo && isTaskDetail; } /// /// 创建新任务 /// /// 请求模型 /// 巷道 /// 标识(0-入库,1-空托盘入库,2-NG入库,3-出库) /// private async Task CreateNewTask(RequestTaskDto input, string productionLine, string processCode, List process = null, int flag = 0) { WebResponseContent content = new WebResponseContent(); // 获取目标地址 //string ToAddress = await GetRoadWayAsync(process); string ToAddress = string.Empty; if (flag < 2) ToAddress = await GetRoadWayAsync(process); else ToAddress = process[0]; if (string.IsNullOrEmpty(ToAddress)) { return content.Error("无法获取目标地址"); } // 创建新任务实例 var task = new Dt_Task { CurrentAddress = input.Position, Grade = 1, Roadway = ToAddress, TargetAddress = ToAddress, Dispatchertime = DateTime.Now, MaterialNo = "", NextAddress = ToAddress, OrderNo = null, PalletCode = input.PalletCode, SourceAddress = input.Position, TaskState = flag == 3 ? (int)TaskOutStatusEnum.OutNew : (int)TaskInStatusEnum.InNew, TaskType = flag == 0 ? (int)TaskInboundTypeEnum.Inbound : flag == 1 ? (int)TaskInboundTypeEnum.InTray : flag == 2 ? (int)TaskInboundTypeEnum.InNG : (int)TaskOutboundTypeEnum.Outbound, TaskNum = await BaseDal.GetTaskNo(), Creater = "System", ProductionLine = productionLine, ProcessCode = processCode }; // 尝试添加新任务 var taskId = await BaseDal.AddDataAsync(task); bool isResult = taskId > 0; if (isResult) { task.TaskId = taskId; isResult = await _taskExecuteDetailRepository.AddDetailAsync(task, false, TaskDescription.GetTaskUpdateDescription(input.PalletCode, input.Position, ToAddress, TaskInStatusEnum.InNew.GetIntegralRuleTypeEnumDesc())); //var location = _locationRepository.QueryFirst(x => x.RoadwayNo == task.Roadway && x.LocationCode == task.TargetAddress); //location.LocationStatus = (int)LocationEnum.Lock; //var isLocation = _locationRepository.UpdateData(location); if (isResult) { // 创建WMS任务 WMSTaskDTO taskDTO = new WMSTaskDTO() { TaskNum = task.TaskNum.Value, Grade = 1, PalletCode = task.PalletCode, RoadWay = task.Roadway, SourceAddress = task.SourceAddress, TargetAddress = task.TargetAddress, TaskState = task.TaskState.Value, Id = 0, TaskType = task.TaskType, ProductionLine = task.ProductionLine }; content.OK(data: taskDTO); } else content.Error("添加任务失败"); } else content.Error("添加任务失败"); return content; } private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private static List locationCaches = new List(); /// /// 获取货位号 /// /// public async Task GetEmptyLocation(string roadWay) { await _semaphore.WaitAsync(); try { List removeItems = locationCaches.Where(x => (DateTime.Now - x.DateTime).TotalMinutes > 5).ToList();//查询添加静态变量超过5分钟的货位 int count = removeItems.Count; for (int i = 0; i < count; i++) { locationCaches.Remove(removeItems[i]);//移除查询添加静态变量超过5分钟的货位 } List lockLocations = locationCaches.Select(x => x.LocationCode).ToList(); List LocationInfoList = await _locationRepository.QueryDataAsync(x => x.RoadwayNo == roadWay && x.LocationStatus == (int)LocationEnum.Free && x.EnalbeStatus == EnableEnum.Enable.ObjToInt()); List LocationInfoResult = new List(); foreach (DtLocationInfo item in LocationInfoList) { DtLocationInfo locationItem = new DtLocationInfo(); if (item.Depth == 2) { locationItem = _locationRepository.QueryFirst(x => x.Column == item.Column && x.RoadwayNo == item.RoadwayNo && x.Layer == item.Layer && x.Depth != item.Depth && x.Row != item.Row && (SqlFunc.Abs(x.Row - item.Row) == 1) && x.LocationStatus == (int)LocationEnum.Free && x.EnalbeStatus == EnableEnum.Enable.ObjToInt()); if (locationItem != null) { LocationInfoResult.Add(item); } } else { locationItem = _locationRepository.QueryFirst(x => x.Column == item.Column && x.RoadwayNo == item.RoadwayNo && x.Layer == item.Layer && x.Depth != item.Depth && x.Row != item.Row && (SqlFunc.Abs(x.Row - item.Row) == 1) && x.LocationStatus == (int)LocationEnum.InStock && x.EnalbeStatus == EnableEnum.Enable.ObjToInt()); if (locationItem != null) { LocationInfoResult.Add(item); } } } if (LocationInfoResult.Count < 2) { throw new Exception("当前空闲货位不足!"); } return LocationInfoResult.Where(x => !lockLocations.Contains(x.LocationCode)).OrderByDescending(x => x.Depth).ThenBy(x => x.Column).ThenBy(x => x.Layer).FirstOrDefault(); } catch (Exception ex) { throw new Exception(ex.Message); } finally { _semaphore.Release(); } } /// /// 根据巷道获取巷道或站台 /// /// public async Task GetRoadWayAsync(List process) { var deviceCode = await SqlSugarHelper.DbWCS.Queryable() .Where(x => x.DeviceStatus == 1.ToString() && process.Contains(x.DeviceCode)) .Select(x => x.DeviceCode).ToListAsync(); var minGroup = _locationRepository.QueryData(x => deviceCode.Contains(x.RoadwayNo) && x.LocationStatus == (int)LocationEnum.Free) .GroupBy(x => x.RoadwayNo) .OrderByDescending(g => g.Count()) // 根据每个组的元素数量排序 .ToList(); // 取出数量最多的组 Dictionary result = new Dictionary(); foreach (var item in minGroup) { var number = BaseDal.QueryData(x => x.TargetAddress == item.Key && x.TaskType.GetTaskTypeGroup() == TaskTypeGroup.InboundGroup).Count(); result.Add(item.Key, item.Count() - number); } string minRoadwayNo = result.OrderByDescending(x => x.Value).FirstOrDefault().Key; // 数量最多的组的Key return minRoadwayNo; } #endregion 任务请求方法 #endregion private 内部方法 }