| using AngleSharp.Dom; | 
| using Mapster; | 
| using System.Threading.Tasks; | 
| using WIDESEA_Common; | 
| using WIDESEA_Core; | 
| using WIDESEA_DTO; | 
| using WIDESEA_Model.Models; | 
|   | 
| namespace WIDESEA_StorageBasicService; | 
|   | 
| public class LocationInfoService : ServiceBase<DtLocationInfo, ILocationInfoRepository>, ILocationInfoService | 
| { | 
|     private readonly LogFactory LogFactory = new LogFactory(); | 
|     private readonly IUnitOfWorkManage _unitOfWorkManage; | 
|     private readonly IDt_TaskRepository _taskRepository; | 
|     private readonly IDt_TaskService _taskService; | 
|     private readonly IStockInfoRepository _stockInfoRepository; | 
|     private readonly IStockInfoDetailRepository _stockInfoDetailRepository; | 
|     private readonly IDt_WareAreaInfoRepository _wareAreaInfoRepository; | 
|     private readonly IPointStackerRelationRepository _pointStackerRelationRepository; | 
|     private readonly ITaskExecuteDetailRepository _taskExecuteDetailRepository; | 
|     private readonly ILocationStatusChangeRecordRepository _locationStatusChangeRecordRepository; | 
|     private readonly IMapper _mapper; | 
|   | 
|     public LocationInfoService(ILocationInfoRepository BaseDal, | 
|                                     IUnitOfWorkManage unitOfWorkManage, | 
|                                     IDt_TaskRepository taskRepository, | 
|                                     IStockInfoRepository stockInfoRepository, | 
|                                     IDt_WareAreaInfoRepository wareAreaInfoRepository, | 
|                                     IPointStackerRelationRepository pointStackerRelationRepository, | 
|                                     ITaskExecuteDetailRepository taskExecuteDetailRepository, | 
|                                     ILocationStatusChangeRecordRepository locationStatusChangeRecordRepository, | 
|                                     IStockInfoDetailRepository stockInfoDetailRepository, | 
|                                     IMapper mapper, | 
|                                     IDt_TaskService taskService) : base(BaseDal) | 
|     { | 
|         _unitOfWorkManage = unitOfWorkManage; | 
|         _taskRepository = taskRepository; | 
|         _stockInfoRepository = stockInfoRepository; | 
|         _wareAreaInfoRepository = wareAreaInfoRepository; | 
|         _pointStackerRelationRepository = pointStackerRelationRepository; | 
|         _taskExecuteDetailRepository = taskExecuteDetailRepository; | 
|         _locationStatusChangeRecordRepository = locationStatusChangeRecordRepository; | 
|         _stockInfoDetailRepository = stockInfoDetailRepository; | 
|         _mapper = mapper; | 
|         _taskService = taskService; | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 检查并生成移库任务或返回出库任务 | 
|     /// </summary> | 
|     /// <param name="locationID">任务号</param> | 
|     /// <returns>任务对象</returns> | 
|     public virtual async Task<Dt_Task> TransferCheckAsync(int taskNum) | 
|     { | 
|         try | 
|         { | 
|             // 根据任务号获取任务 | 
|             var outboundTask = await _taskRepository.QueryFirstAsync(x => x.TaskNum == taskNum); | 
|             if (outboundTask == null) | 
|                 return null; | 
|             LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "根据任务号获取任务", $"任务号:{taskNum},任务数据:{outboundTask}"); | 
|   | 
|             var location = await BaseDal.QueryFirstAsync(x => x.LocationCode == outboundTask.SourceAddress); | 
|             LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "获取指定库位的货位数据", $"货位号:{outboundTask.SourceAddress},货位数据:{location}"); | 
|   | 
|             // 检查是否需要进行移库 | 
|             if (CheckForInternalTransfer(location)) | 
|             { | 
|                 LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "需要移库", $"货位号:{outboundTask.SourceAddress},货位数据:{location}"); | 
|                 // 计算对应位置的相对库位 (奇数行的下一行或者偶数行的上一行) | 
|                 var newLocationID = GetRelativeLocationID(location); | 
|                 LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "需要移库,组建需要移库货位号", $"新的库位ID:{newLocationID}"); | 
|   | 
|                 // 获取新的库位的任务 | 
|                 var internalTransferTask = await _taskRepository.QueryFirstAsync(x => x.SourceAddress == newLocationID && x.Roadway == outboundTask.Roadway); | 
|   | 
|                 LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "需要移库,获取新库位的任务", $"新库位任务:{internalTransferTask}"); | 
|                 // 如果新的库位没有找到对应的任务 | 
|                 if (internalTransferTask == null) | 
|                 { | 
|                     LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "需要移库,未获取到新库位任务", $""); | 
|                     return await HandleNoTaskAtLocation(outboundTask.SourceAddress, newLocationID, outboundTask); | 
|                 } | 
|   | 
|                 LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "需要移库,获取到新库位任务直接返回", $"新库位任务:{internalTransferTask}"); | 
|                 // 直接返回一深位出库任务 | 
|                 return internalTransferTask; | 
|             } | 
|   | 
|             // 返回当前库位的出库任务 | 
|             return outboundTask; | 
|         } | 
|         catch (Exception) | 
|         { | 
|             return null; | 
|         } | 
|     } | 
|   | 
|     public override WebResponseContent UpdateData(SaveModel saveModel) | 
|     { | 
|         int id = saveModel.MainData["id"].ObjToInt(); | 
|         int status = saveModel.MainData["locationStatus"].ObjToInt(); | 
|         var location = BaseDal.QueryFirst(x => x.Id == id); | 
|   | 
|         LocationChangeRecordDto changeRecordDto = new LocationChangeRecordDto() | 
|         { | 
|             AfterStatus = status, | 
|             BeforeStatus = location.LocationStatus, | 
|             TaskNum = 0, | 
|             LocationId = id, | 
|             LocationCode = location.LocationCode, | 
|             ChangeType = (int)StatusChangeTypeEnum.ManualOperation | 
|         }; | 
|   | 
|         _locationStatusChangeRecordRepository.AddStatusChangeRecord(changeRecordDto); | 
|   | 
|         return base.UpdateData(saveModel); | 
|     } | 
|     #region 初始化库位 | 
|     public async Task<WebResponseContent> initializeLocation(int locationID) | 
|     { | 
|         WebResponseContent content = new WebResponseContent(); | 
|         try | 
|         { | 
|             DtLocationInfo? location = BaseDal.QueryData(x => x.Id == locationID).FirstOrDefault(); | 
|             int LastStatus = location.LocationStatus; | 
|             if (location == null) | 
|             { | 
|                 return content.Error("未找到货位信息!"); | 
|             } | 
|             DtStockInfo stock = _stockInfoRepository.QueryFirst(x => x.LocationId == location.Id); | 
|             if(stock == null) | 
|             { | 
|                 location.LocationStatus= (int)LocationEnum.Lock; | 
|                 BaseDal.UpdateData(location); | 
|             } | 
|             else | 
|             { | 
|                 _unitOfWorkManage.BeginTran(); | 
|                 DtStockInfo_Hty stockInfo_Hty = stock.Adapt<DtStockInfo_Hty>(); | 
|                 stockInfo_Hty.ModifyDate = DateTime.Now; | 
|                 await DeleteStockInfoAsync(stock.Id); | 
|                 List<DtStockInfoDetail> detail = _stockInfoDetailRepository.QueryData(x => x.StockId == stock.Id).ToList(); | 
|                 if (detail != null && detail.Count() > 0) | 
|                 { | 
|                     List<DtStockInfoDetail_Hty> details = detail.Adapt<List<DtStockInfoDetail_Hty>>(); | 
|                     await DeleteStockInfoDetailsAsync(detail); | 
|                     AddStockInfoDetailHty(details); | 
|                 } | 
|                 await AddStockInfoHtyAsync(stockInfo_Hty); | 
|                  | 
|   | 
|                 location.LocationStatus = (int)LocationEnum.Lock; | 
|                 BaseDal.UpdateData(location); | 
|   | 
|                 _locationStatusChangeRecordRepository.AddLocationStatusChangeRecord(location, LastStatus, (int)StatusChangeTypeEnum.ManualOperation, 0); | 
|                 _unitOfWorkManage.CommitTran(); | 
|             } | 
|             return content.OK(); | 
|         } | 
|         catch (Exception ex) | 
|         { | 
|             _unitOfWorkManage.RollbackTran(); | 
|             return content.Error(ex.Message); | 
|         } | 
|     } | 
|     #endregion | 
|   | 
|     #region 创建原始货位数据 | 
|   | 
|     /// <summary> | 
|     /// 创建原始货位数据 | 
|     /// </summary> | 
|     /// <param name="x">行</param> | 
|     /// <param name="y">列</param> | 
|     /// <param name="z">层</param> | 
|     /// <param name="locType">货位类型(1、单深,2、双深)</param> | 
|     /// <param name="areaId">库区编码</param> | 
|     /// <returns></returns> | 
|     public WebResponseContent CreateLocation(int x, int y, int z, int locType, int areaId) | 
|     { | 
|         string[] chineseNumbers = new string[] { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九" }; | 
|         var locationList = new List<DtLocationInfo>(); | 
|         for (int line = 3; line <= x; line++) | 
|         { | 
|             for (int column = 1; column <= y; column++) | 
|             { | 
|                 for (int layer = 1; layer <= z; layer++) | 
|                 { | 
|                     locationList.Add(new DtLocationInfo() | 
|                     { | 
|                         Row = line, | 
|                         Column = column, | 
|                         Layer = layer, | 
|                         LocationCode = line.ToString().PadLeft(3, '0') + '-' + column.ToString().PadLeft(3, '0') + '-' + layer.ToString().PadLeft(3, '0'), | 
|                         LocationName = ConvertToFormattedString(line, column, layer), | 
|                         LocationType = locType, | 
|                         Remark = "", | 
|                         Depth = locType > 1 ? (((line - 1) % 4) + 1) == 2 || (((line - 1) % 4) + 1) == 3 ? 1 : 2 : 1, | 
|                         RoadwayNo = locType > 1 ? $"GWSC{((line - 1) / 4) + 1}" : $"GWSC{((line - 1) / 2) + 1}", | 
|                         LocationStatus = LocationEnum.Free.ObjToInt(), | 
|                         AreaId = areaId, | 
|                         Creater = "systeam", | 
|                         EnalbeStatus = 2, | 
|                     }); | 
|                 } | 
|             } | 
|         } | 
|         var isResult = BaseDal.AddData(locationList) > 0; | 
|         if (isResult) | 
|             return new WebResponseContent().OK(); | 
|         else | 
|             return new WebResponseContent().Error(); | 
|     } | 
|   | 
|     #endregion 创建原始货位数据 | 
|   | 
|     #region 启用禁用货位 | 
|   | 
|     public WebResponseContent LocationEnable(SaveModel saveModel) | 
|     { | 
|         WebResponseContent content = new WebResponseContent(); | 
|         try | 
|         { | 
|             List<DtLocationInfo> locations = new List<DtLocationInfo>(); | 
|             int enable = Convert.ToBoolean(saveModel.Extra) ? 0 : 4; | 
|             for (int i = 0; i < saveModel.DelKeys.Count; i++) | 
|             { | 
|                 DtLocationInfo location = BaseDal.QueryData(x => x.Id == int.Parse(saveModel.DelKeys[i].ToString())).FirstOrDefault(); | 
|                 location.LocationStatus = enable; | 
|                 locations.Add(location); | 
|             } | 
|             BaseDal.UpdateData(locations); | 
|             content = WebResponseContent.Instance.OK(); | 
|         } | 
|         catch (Exception ex) | 
|         { | 
|             content = WebResponseContent.Instance.Error(ex.Message); | 
|         } | 
|         finally | 
|         { | 
|             //日志记录 | 
|         } | 
|         return content; | 
|     } | 
|   | 
|     #endregion 启用禁用货位 | 
|   | 
|     #region 内部方法 | 
|   | 
|     #region 移库方法 | 
|   | 
|     /// <summary> | 
|     /// 计算相对的库位ID | 
|     /// </summary> | 
|     /// <param name="locationID">当前库位ID</param> | 
|     /// <returns>相对的库位ID</returns> | 
|     private string GetRelativeLocationID(DtLocationInfo locationInfo) | 
|     { | 
|         int line = locationInfo.Row; | 
|   | 
|         // 计算相对的货位行值,奇数行的下一行或者偶数行的上一行 | 
|         int relativeLine = line % 2 == 1 ? line + 1 : line - 1; | 
|         LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "需要移库,计算相对行", $"原始货位行:{line},计算相对货位行:{relativeLine}"); | 
|   | 
|         // 构建新的库位ID | 
|         string[] newLocationParts = new string[] { relativeLine.ToString().PadLeft(3, '0'), locationInfo.Column.ToString(), locationInfo.Layer.ToString() }; | 
|         return string.Join("-", newLocationParts); | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 处理没有任务的库位情况 | 
|     /// </summary> | 
|     /// <param name="originalLocationID">原始库位ID</param> | 
|     /// <param name="newLocationID">新的库位ID</param> | 
|     /// <param name="outboundTask">出库任务</param> | 
|     /// <returns>生成的移库任务或原始出库任务</returns> | 
|     private async Task<Dt_Task> HandleNoTaskAtLocation(string originalLocationID, string newLocationID, Dt_Task outboundTask) | 
|     { | 
|         // 判断该位置是否有库存 | 
|         var stockInfo = await _stockInfoRepository.QueryFirstAsync(x => x.LocationCode == newLocationID); | 
|   | 
|         LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "需要移库,未获取到新库位任务", $"该位置是否有库存:{stockInfo}"); | 
|         if (stockInfo == null) | 
|         { | 
|             LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "需要移库,未获取到库存数据", $"直接返回原先出库任务:{outboundTask}"); | 
|             // 如果没有库存,直接返回当前出库任务 | 
|             return outboundTask; | 
|         } | 
|         else | 
|         { | 
|             // 如果有库存,生成移库任务 | 
|             var emptyLocation = await GetTransferLocationEmptyAsync(outboundTask.Roadway); | 
|             LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "需要移库,查找能移库货位", $"货位数据:{emptyLocation}"); | 
|             var taskNo = await _taskRepository.GetTaskNo(); | 
|             Dt_Task newTransferTask = new Dt_Task() | 
|             { | 
|                 CreateDate = DateTime.Now, | 
|                 Creater = App.User.UserName, | 
|                 CurrentAddress = originalLocationID, | 
|                 Grade = 99, | 
|                 MaterialNo = stockInfo.StockInfoDetails[0].MaterielCode, | 
|                 NextAddress = emptyLocation.LocationCode, | 
|                 PalletCode = stockInfo.PalletCode, | 
|                 Remark = "移库", | 
|                 Roadway = stockInfo.LocationInfo.RoadwayNo, | 
|                 SourceAddress = originalLocationID, | 
|                 TaskNum = taskNo, | 
|                 TargetAddress = emptyLocation.LocationCode, | 
|                 //TaskState = TaskInStatusEnum.RelocationNew.ObjToInt(), | 
|                 TaskType = TaskTypeEnum.RelocationIn.ObjToInt(), | 
|             }; | 
|   | 
|             LogFactory.GetLog("检查是否需要移库").InfoFormat(true, "需要移库,新建移库任务", $"移库任务数据:{newTransferTask}"); | 
|             return await _taskRepository.Create(newTransferTask); | 
|         } | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 根据货位是否需要移库 | 
|     /// </summary> | 
|     /// <param name="locationID">货位ID</param> | 
|     /// <returns>是否需要移库</returns> | 
|     private bool CheckForInternalTransfer(DtLocationInfo location) | 
|     { | 
|         return location.Depth == 2 ? true : false; | 
|     } | 
|   | 
|     /// <summary> | 
|     /// 根据巷道获取二深位的空库位 | 
|     /// </summary> | 
|     /// <param name="roadway">巷道</param> | 
|     /// <returns>货位对象</returns> | 
|     private async Task<DtLocationInfo> GetTransferLocationEmptyAsync(string roadway) | 
|     { | 
|         return await BaseDal.QueryFirstAsync(x => x.Depth == 2 && x.LocationStatus == LocationEnum.Free.ObjToInt() && x.RoadwayNo == roadway); | 
|   | 
|         //Db.Queryable<Dt_LocationInfo>() | 
|         //.Where(x => x.Status == LocationEnum.Free.ObjToInt()) | 
|         //.Where(x => x.Depth == 2.ToString()) | 
|         //.Where(x => x.Roadway == roadway) | 
|         //.First(); | 
|     } | 
|   | 
|     #endregion 移库方法 | 
|   | 
|     #region 创建初始货位方法 | 
|   | 
|     public static string ConvertToFormattedString(int line, int column, int layer) | 
|     { | 
|         // 将每个部分转换为目标格式 | 
|         string lineString = ConvertNumberToChineseString(line); | 
|         string columnString = ConvertNumberToChineseString(column); | 
|         string layerString = ConvertNumberToChineseString(layer); | 
|   | 
|         // 格式化输出 | 
|         return $"{lineString}行{columnString}列{layerString}层"; | 
|     } | 
|   | 
|     public static string ConvertNumberToChineseString(int number) | 
|     { | 
|         string[] chineseNumbers = { "零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十" }; | 
|   | 
|         if (number <= 10) | 
|         { | 
|             return chineseNumbers[number]; | 
|         } | 
|   | 
|         // 处理大于10的数字 | 
|         string result = string.Empty; | 
|         if (number / 10 > 1) | 
|         { | 
|             result += chineseNumbers[number / 10] + "十"; | 
|         } | 
|         else | 
|         { | 
|             result += "十"; | 
|         } | 
|   | 
|         if (number % 10 > 0) | 
|         { | 
|             result += chineseNumbers[number % 10]; | 
|         } | 
|   | 
|         return result; | 
|     } | 
|   | 
|     #endregion 创建初始货位方法 | 
|   | 
|     #region 库存移入历史 | 
|     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 DeleteStockInfoDetailsAsync(IEnumerable<DtStockInfoDetail> details) | 
|     { | 
|         var ids = details.Select(x => (object)x.Id).ToArray(); | 
|         var isStockDetailUpdated = await _stockInfoDetailRepository.DeleteDataByIdsAsync(ids); | 
|         if (!isStockDetailUpdated) | 
|         { | 
|             throw new Exception("库存详情信息更新失败"); | 
|         } | 
|     } | 
|     private void AddStockInfoDetailHty(List<DtStockInfoDetail_Hty> details) | 
|     { | 
|   | 
|         var isStockAdd = SqlSugarHelper.DbWMS.Insertable(details).ExecuteCommand(); | 
|         if (isStockAdd==0) | 
|         { | 
|             throw new Exception("库存明细历史信息添加失败"); | 
|         } | 
|     } | 
|      | 
|     #endregion | 
|   | 
|     #endregion 内部方法 | 
| } |