From 0e214856df682998cd52df74c851502c571ba183 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期二, 28 四月 2026 23:22:23 +0800
Subject: [PATCH] feat(出库任务): 优化二深位出库的移库逻辑并添加事务支持
---
Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs | 676 ++++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 490 insertions(+), 186 deletions(-)
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
index 8e323e3..d7aee6d 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
@@ -1,33 +1,67 @@
-锘縰sing SqlSugar;
+using Mapster;
+using MapsterMapper;
using WIDESEA_Common.CommonEnum;
using WIDESEA_Common.LocationEnum;
+using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_Core.Utilities;
using WIDESEA_DTO.Basic;
+using WIDESEA_DTO.Task;
using WIDESEA_IBasicService;
+using WIDESEA_IRecordService;
using WIDESEA_Model.Models;
namespace WIDESEA_BasicService
{
+ /// <summary>
+ /// 璐т綅淇℃伅鏈嶅姟瀹炵幇绫�
+ /// </summary>
public partial class LocationInfoService : ServiceBase<Dt_LocationInfo, IRepository<Dt_LocationInfo>>, ILocationInfoService
{
- public IRepository<Dt_LocationInfo> Repository => BaseDal;
- public IRepository<Dt_Task> _taskRepository { get; }
+ private readonly IMapper _mapper;
+ private readonly IRepository<Dt_Task> _taskRepository;
+ private readonly IRepository<Dt_StockInfo> _stockInfoRepository;
+ private readonly IRecordService _recordService;
+ private readonly IRepository<Dt_Warehouse> _warehouseRepository;
+ private readonly IUnitOfWorkManage _unitOfWorkManage;
- public IRepository<Dt_StockInfo> _stockInfoRepository { get; set; }
-
- public LocationInfoService(IRepository<Dt_LocationInfo> BaseDal, IRepository<Dt_Task> taskRepository, IRepository<Dt_StockInfo> stockInfoRepository) : base(BaseDal)
+ /// <summary>
+ /// 鏋勯�犲嚱鏁�
+ /// </summary>
+ /// <param name="baseDal">鍩虹鏁版嵁璁块棶瀵硅薄</param>
+ /// <param name="taskRepository">浠诲姟浠撳偍</param>
+ /// <param name="stockInfoRepository">搴撳瓨淇℃伅浠撳偍</param>
+ /// <param name="unitOfWorkManage">宸ヤ綔鍗曞厓绠$悊鍣�</param>
+ public LocationInfoService(
+ IRepository<Dt_LocationInfo> baseDal,
+ IRepository<Dt_Task> taskRepository,
+ IRepository<Dt_StockInfo> stockInfoRepository,
+ IRepository<Dt_Warehouse> warehouseRepository,
+ IMapper mapper,
+ IRecordService recordService,
+ IUnitOfWorkManage unitOfWorkManage) : base(baseDal)
{
_taskRepository = taskRepository;
_stockInfoRepository = stockInfoRepository;
+ _mapper = mapper;
+ _recordService = recordService;
+ _warehouseRepository = warehouseRepository;
+ _unitOfWorkManage = unitOfWorkManage;
}
+
+ /// <summary>
+ /// 鑾峰彇璐т綅淇℃伅浠撳偍
+ /// </summary>
+ public IRepository<Dt_LocationInfo> Repository => BaseDal;
/// <summary>
/// 鎵归噺鍚敤璐т綅
/// </summary>
+ /// <param name="keys">璐т綅涓婚敭鏁扮粍</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
public WebResponseContent LocationEnableStatus(int[] keys)
{
var locationInfos = Repository.QueryData(x => keys.Contains(x.Id));
@@ -39,6 +73,8 @@
/// <summary>
/// 鎵归噺绂佺敤璐т綅
/// </summary>
+ /// <param name="keys">璐т綅涓婚敭鏁扮粍</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
public WebResponseContent LocationDisableStatus(int[] keys)
{
var locationInfos = Repository.QueryData(x => keys.Contains(x.Id));
@@ -50,16 +86,22 @@
/// <summary>
/// 鍗曚釜鍚敤璐т綅
/// </summary>
+ /// <param name="key">璐т綅涓婚敭</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
public WebResponseContent LocationEnableStatus(int key) => LocationEnableStatus(new[] { key });
/// <summary>
/// 鍗曚釜绂佺敤璐т綅
/// </summary>
+ /// <param name="key">璐т綅涓婚敭</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
public WebResponseContent LocationDisableStatus(int key) => LocationDisableStatus(new[] { key });
/// <summary>
/// 鍒濆鍖栬揣浣�
/// </summary>
+ /// <param name="dto">鍒濆鍖栬揣浣嶆暟鎹紶杈撳璞�</param>
+ /// <returns>鎿嶄綔缁撴灉</returns>
public WebResponseContent InitializationLocation(InitializationLocationDTO dto)
{
try
@@ -93,6 +135,426 @@
}
}
+ /// <summary>
+ /// 鏍规嵁宸烽亾鑾峰彇绌洪棽璐т綅淇℃伅
+ /// 鎺掑簭绛栫暐锛氭繁搴︿紭鍏堬紙浜屾繁浣嶄紭鍏堬級锛屽叾娆℃寜灞傘�佸垪銆佽鍗囧簭
+ /// </summary>
+ /// <param name="roadwayNo">宸烽亾缂栧彿</param>
+ /// <returns>绌洪棽璐т綅淇℃伅锛屽鏋滅┖闂茶揣浣嶄笉瓒冲垯杩斿洖null</returns>
+ public async Task<Dt_LocationInfo?> GetLocationInfo(string roadwayNo)
+ {
+ // HC 宸烽亾浣跨敤 Capacity 绫诲瀷锛屽叾浠栧贩閬撲娇鐢� ShelfCapacity 绫诲瀷
+ var locationType = roadwayNo.Contains("HC")
+ ? (int)LocationTypeEnum.Capacity
+ : (int)LocationTypeEnum.ShelfCapacity;
+
+ var enableStatus = EnableStatusEnum.Normal.GetHashCode();
+ var freeStatus = LocationStatusEnum.Free.GetHashCode();
+
+ // 鏁版嵁搴撶 COUNT 妫�鏌ョ┖闂茶揣浣嶆暟閲忥紙浠呰繑鍥炰竴涓暟瀛楋紝鏃犳暟鎹紶杈撳紑閿�锛�
+ var freeCount = await BaseDal.Db.Queryable<Dt_LocationInfo>()
+ .Where(x => x.EnableStatus == enableStatus
+ && x.RoadwayNo == roadwayNo
+ && x.LocationStatus == freeStatus
+ && x.LocationType == locationType)
+ .CountAsync();
+
+ // 绌洪棽璐т綅涓嶈冻鏈�浣庝繚鐣欐暟閲忔椂杩斿洖null锛岄伩鍏嶅皢宸烽亾鍒嗛厤鑰楀敖
+ const int minFreeLocationThreshold = 5;
+ if (freeCount < minFreeLocationThreshold) return null;
+
+ // 鏁版嵁搴撶鎺掑簭鍙栫涓�鏉★紙鍙紶杈撳崟琛屾暟鎹級
+ return await BaseDal.Db.Queryable<Dt_LocationInfo>()
+ .Where(x => x.EnableStatus == enableStatus
+ && x.RoadwayNo == roadwayNo
+ && x.LocationStatus == freeStatus
+ && x.LocationType == locationType)
+ .OrderByDescending(x => x.Depth)
+ .OrderBy(x => x.Layer)
+ .OrderBy(x => x.Column)
+ .OrderBy(x => x.Row)
+ .FirstAsync();
+ }
+
+ /// <summary>
+ /// 鏍规嵁宸烽亾鍜岃揣浣嶇紪鐮佽幏鍙栫┖闂茶揣浣嶄俊鎭�
+ /// </summary>
+ /// <param name="roadwayNo">宸烽亾缂栧彿</param>
+ /// <param name="locationCode">璐т綅缂栫爜</param>
+ /// <returns>璐т綅淇℃伅锛屽鏋滄湭鎵惧埌鍒欒繑鍥瀗ull</returns>
+ public async Task<Dt_LocationInfo?> GetLocationInfo(string roadwayNo, string locationCode)
+ {
+ return await BaseDal.QueryFirstAsync(x => x.RoadwayNo == roadwayNo && x.LocationCode == locationCode);
+ }
+
+ /// <summary>
+ /// 鏍规嵁璐т綅缂栫爜鑾峰彇璐т綅淇℃伅
+ /// </summary>
+ /// <param name="locationCode">璐т綅缂栫爜</param>
+ /// <returns>璐т綅淇℃伅</returns>
+ public async Task<Dt_LocationInfo> GetLocationInfoAsync(string locationCode)
+ {
+ return await BaseDal.QueryFirstAsync(x => x.LocationCode == locationCode);
+ }
+
+ /// <summary>
+ /// 鏍规嵁璐т綅ID鑾峰彇璐т綅淇℃伅
+ /// </summary>
+ /// <param name="id">璐т綅id</param>
+ /// <returns>璐т綅淇℃伅</returns>
+ public async Task<Dt_LocationInfo> GetLocationInfoAsync(int id)
+ {
+ return await BaseDal.QueryFirstAsync(x => x.Id == id);
+ }
+
+ /// <summary>
+ /// 鏇存柊璐т綅淇℃伅
+ /// </summary>
+ /// <param name="locationInfo">璐т綅淇℃伅瀵硅薄</param>
+ /// <returns>鏇存柊鏄惁鎴愬姛</returns>
+ public async Task<bool> UpdateLocationInfoAsync(Dt_LocationInfo locationInfo)
+ {
+ var beforeLocation = await BaseDal.QueryFirstAsync(x => x.Id == locationInfo.Id);
+ var result = await BaseDal.UpdateDataAsync(locationInfo);
+ if (!result)
+ return false;
+
+ return beforeLocation == null
+ || await _recordService.AddLocationChangeRecordAsync(beforeLocation, locationInfo, LocationChangeType.HandUpdate, remark: "璐т綅鏇存柊");
+ }
+
+ public override WebResponseContent UpdateData(Dt_LocationInfo entity)
+ {
+ var beforeLocation = BaseDal.QueryFirst(x => x.Id == entity.Id);
+ var result = base.UpdateData(entity);
+ if (!result.Status || beforeLocation == null)
+ return result;
+
+ var saveRecordResult = _recordService.AddLocationChangeRecordAsync(beforeLocation, entity, LocationChangeType.HandUpdate, remark: "璐т綅鏇存柊").GetAwaiter().GetResult();
+ return saveRecordResult ? result : WebResponseContent.Instance.Error("璐т綅鐘舵�佸彉鏇磋褰曚繚瀛樺け璐�");
+ }
+
+ public override WebResponseContent UpdateData(List<Dt_LocationInfo> entities)
+ {
+ var beforeLocations = entities
+ .Select(entity => BaseDal.QueryFirst(x => x.Id == entity.Id))
+ .Where(location => location != null)
+ .ToDictionary(location => location!.Id, location => location!);
+
+ var result = base.UpdateData(entities);
+ if (!result.Status)
+ return result;
+
+ foreach (var entity in entities)
+ {
+ if (!beforeLocations.TryGetValue(entity.Id, out var beforeLocation))
+ continue;
+
+ var saveRecordResult = _recordService.AddLocationChangeRecordAsync(beforeLocation, entity, LocationChangeType.HandUpdate, remark: "鎵归噺璐т綅鏇存柊").GetAwaiter().GetResult();
+ if (!saveRecordResult)
+ return WebResponseContent.Instance.Error("璐т綅鐘舵�佸彉鏇磋褰曚繚瀛樺け璐�");
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// 妫�鏌ュ苟鐢熸垚绉诲簱浠诲姟鎴栬繑鍥炲嚭搴撲换鍔�
+ /// </summary>
+ /// <param name="taskNum">浠诲姟鍙�</param>
+ /// <returns>浠诲姟淇℃伅</returns>
+ public async Task<WebResponseContent> TransferCheckAsync(int taskNum)
+ {
+ var content = new WebResponseContent();
+ try
+ {
+ // 鏍规嵁浠诲姟鍙疯幏鍙栦换鍔�
+ var outboundTask = await _taskRepository.QueryFirstAsync(x => x.TaskNum == taskNum);
+ if (outboundTask == null)
+ return content.Error("浠诲姟涓嶅瓨鍦�");
+
+ var location = await BaseDal.QueryFirstAsync(x => x.LocationCode == outboundTask.SourceAddress && x.RoadwayNo == outboundTask.Roadway);
+ if (location == null)
+ return content.Error("鏈壘鍒版簮璐т綅淇℃伅");
+
+ // 妫�鏌ユ槸鍚﹂渶瑕佽繘琛岀Щ搴擄紙浜屾繁浣嶅嚭搴撻渶瑕佸厛绉昏蛋涓�娣变綅锛�
+ if (CheckForInternalTransfer(location))
+ {
+ // 璁$畻瀵瑰簲浣嶇疆鐨勭浉瀵瑰簱浣嶏紙濂囨暟琛岀殑涓嬩竴琛屾垨鑰呭伓鏁拌鐨勪笂涓�琛岋級
+ var relativeLocationCode = GetRelativeLocationID(location);
+
+ // 鏌ユ壘鐩稿搴撲綅涓婄殑娲昏穬浠诲姟锛堟帓闄ゅ凡瀹屾垚銆佸凡鍙栨秷銆佸紓甯哥瓑缁堟�侊級
+ var activeTask = await _taskRepository.QueryFirstAsync(x =>
+ x.SourceAddress == relativeLocationCode
+ && x.Roadway == outboundTask.Roadway
+ && (x.TaskStatus == TaskOutStatusEnum.OutNew.GetHashCode()));
+
+ // 濡傛灉鐩稿搴撲綅娌℃湁娲昏穬浠诲姟锛屽皾璇曞垱寤虹Щ搴撲换鍔�
+ if (activeTask == null)
+ {
+ return await HandleNoTaskAtLocation(relativeLocationCode, outboundTask);
+ }
+
+ // 鐩存帴杩斿洖涓�娣变綅鍑哄簱浠诲姟
+ return content.OK("鑾峰彇鍒颁竴娣变綅鍑哄簱浠诲姟", activeTask);
+ }
+
+ // 杩斿洖褰撳墠搴撲綅鐨勫嚭搴撲换鍔�
+ return content.OK("褰撳墠鍑哄簱浠诲姟", outboundTask);
+ }
+ catch (Exception ex)
+ {
+ return content.Error($"鍙戠敓閿欒:{ex.Message}");
+ }
+ }
+
+ #region 绉佹湁鏂规硶
+
+ /// <summary>
+ /// 璁$畻鐩稿鐨勫簱浣岻D
+ /// </summary>
+ /// <param name="locationInfo">璐т綅淇℃伅</param>
+ /// <returns>鐩稿鐨勫簱浣岻D</returns>
+ private static string GetRelativeLocationID(Dt_LocationInfo locationInfo)
+ {
+ int line = locationInfo.Row;
+
+ // 璁$畻鐩稿鐨勮揣浣嶈鍊硷紝濂囨暟琛岀殑涓嬩竴琛屾垨鑰呭伓鏁拌鐨勪笂涓�琛�
+ int relativeLine = line % 2 == 1 ? line + 1 : line - 1;
+
+ // 鏋勫缓鏂扮殑搴撲綅ID
+ string[] newLocationParts = new string[] { relativeLine.ToString().PadLeft(3, '0'), locationInfo.Column.ToString().PadLeft(3, '0'), locationInfo.Layer.ToString().PadLeft(3, '0') };
+ return string.Join("-", newLocationParts);
+ }
+
+ /// <summary>
+ /// 澶勭悊娌℃湁娲昏穬浠诲姟鐨勫簱浣嶆儏鍐�
+ /// 鍒ゆ柇鏄惁鏈夊簱瀛橈紝鏈夊垯鐢熸垚绉诲簱浠诲姟锛屾棤鍒欑洿鎺ヨ繑鍥炲嚭搴撲换鍔�
+ /// </summary>
+ /// <param name="newLocationID">鐩稿搴撲綅缂栫爜</param>
+ /// <param name="outboundTask">鍘熷鍑哄簱浠诲姟</param>
+ /// <returns>鎿嶄綔缁撴灉锛堝寘鍚Щ搴撲换鍔℃垨鍘熷鍑哄簱浠诲姟锛�</returns>
+ private async Task<WebResponseContent> HandleNoTaskAtLocation(string newLocationID, Dt_Task outboundTask)
+ {
+ // 鍒ゆ柇璇ヤ綅缃槸鍚︽湁搴撳瓨
+ var stockInfo = await _stockInfoRepository.QueryDataNavFirstAsync(x =>
+ x.LocationCode == newLocationID &&
+ (x.StockStatus == StockStatusEmun.鍏ュ簱瀹屾垚.GetHashCode() || x.StockStatus == StockStatusEmun.绌烘墭鐩樺簱瀛�.GetHashCode()) &&
+ x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode());
+ if (stockInfo == null)
+ {
+ // 濡傛灉娌℃湁搴撳瓨锛岀洿鎺ヨ繑鍥炲綋鍓嶅嚭搴撲换鍔�
+ return WebResponseContent.Instance.OK("褰撳墠鍑哄簱浠诲姟", outboundTask);
+ }
+
+ // 鏈夊簱瀛樻椂锛屽湪浜嬪姟涓垱寤虹Щ搴撲换鍔″苟閿佸畾鐩稿叧璧勬簮
+ return await _unitOfWorkManage.BeginTranAsync(async () =>
+ {
+ // 浜嬪姟鍐呭啀娆$‘璁ゆ病鏈夋椿璺冧换鍔★紙闃叉骞跺彂閲嶅鍒涘缓绉诲簱浠诲姟锛�
+ var existingTask = await _taskRepository.QueryFirstAsync(x =>
+ x.SourceAddress == newLocationID
+ && x.Roadway == outboundTask.Roadway
+ && (x.TaskStatus == TaskOutStatusEnum.OutNew.GetHashCode()));
+ if (existingTask != null)
+ {
+ return WebResponseContent.Instance.OK("鑾峰彇鍒板凡鏈変换鍔�", existingTask);
+ }
+
+ // 鑾峰彇鐩爣绌哄簱浣�
+ var emptyLocation = await GetTransferLocationEmptyAsync(outboundTask.Roadway);
+ if (emptyLocation == null)
+ {
+ return WebResponseContent.Instance.Error("鏈壘鍒板彲鐢ㄧ殑绌哄簱浣嶇敤浜庣Щ搴�");
+ }
+
+ var taskNo = await _taskRepository.GetTaskNo();
+ var newTransferTask = new Dt_Task
+ {
+ CreateDate = DateTime.Now,
+ Creater = App.User.UserName ?? "system",
+ CurrentAddress = newLocationID,
+ Grade = 99,
+ NextAddress = emptyLocation.LocationCode,
+ PalletCode = stockInfo.PalletCode,
+ Remark = "绉诲簱",
+ Roadway = stockInfo.LocationDetails.RoadwayNo,
+ SourceAddress = newLocationID,
+ TaskNum = taskNo,
+ TargetAddress = emptyLocation.LocationCode,
+ TaskType = TaskRelocationTypeEnum.Relocation.GetHashCode(),
+ TaskStatus = TaskRelocationStatusEnum.RelocationNew.GetHashCode(),
+ WarehouseId = stockInfo.WarehouseId
+ };
+
+ var createdTask = await _taskRepository.Db.Insertable(newTransferTask).ExecuteReturnEntityAsync();
+ var beforeStock = CloneStockSnapshot(stockInfo);
+ var beforeSourceLocation = stockInfo.LocationDetails == null ? null : CloneLocationSnapshot(stockInfo.LocationDetails);
+ var beforeTargetLocation = CloneLocationSnapshot(emptyLocation);
+
+ // 鍒涘缓绉诲簱浠诲姟鍚庯紝绔嬪嵆閿佸畾搴撳瓨鍜岀浉鍏宠揣浣�
+ stockInfo.StockStatus = StockStatusEmun.绉诲簱閿佸畾.GetHashCode();
+ var updateStockResult = await _stockInfoRepository.UpdateDataAsync(stockInfo);
+
+ var locationsToUpdate = new List<Dt_LocationInfo>();
+ if (stockInfo.LocationDetails != null)
+ {
+ stockInfo.LocationDetails.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
+ locationsToUpdate.Add(stockInfo.LocationDetails);
+ }
+
+ emptyLocation.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
+ locationsToUpdate.Add(emptyLocation);
+
+ var updateLocationResult = await BaseDal.UpdateDataAsync(locationsToUpdate);
+ if (!updateStockResult || !updateLocationResult)
+ {
+ return WebResponseContent.Instance.Error("鍒涘缓绉诲簱浠诲姟鍚庢洿鏂板簱瀛樼姸鎬佹垨璐т綅鐘舵�佸け璐�");
+ }
+
+ var saveStockRecordResult = await _recordService.AddStockChangeRecordAsync(
+ beforeStock,
+ stockInfo,
+ StockChangeTypeEnum.Relocation,
+ createdTask.TaskNum,
+ createdTask.OrderNo,
+ "绉诲簱浠诲姟棰勫崰搴撳瓨");
+ if (!saveStockRecordResult)
+ {
+ return WebResponseContent.Instance.Error("鍒涘缓绉诲簱浠诲姟鍚庤褰曞簱瀛樺彉鏇村け璐�");
+ }
+
+ if (beforeSourceLocation != null && stockInfo.LocationDetails != null)
+ {
+ var saveSourceLocationRecordResult = await _recordService.AddLocationChangeRecordAsync(
+ beforeSourceLocation,
+ stockInfo.LocationDetails,
+ LocationChangeType.RelocationAssignLocation,
+ createdTask.TaskNum,
+ createdTask.OrderNo,
+ null,
+ "绉诲簱浠诲姟閿佸畾婧愯揣浣�");
+ if (!saveSourceLocationRecordResult)
+ {
+ return WebResponseContent.Instance.Error("鍒涘缓绉诲簱浠诲姟鍚庤褰曟簮璐т綅鍙樻洿澶辫触");
+ }
+ }
+
+ var saveTargetLocationRecordResult = await _recordService.AddLocationChangeRecordAsync(
+ beforeTargetLocation,
+ emptyLocation,
+ LocationChangeType.RelocationAssignLocation,
+ createdTask.TaskNum,
+ createdTask.OrderNo,
+ null,
+ "绉诲簱浠诲姟閿佸畾鐩爣璐т綅");
+ if (!saveTargetLocationRecordResult)
+ {
+ return WebResponseContent.Instance.Error("鍒涘缓绉诲簱浠诲姟鍚庤褰曠洰鏍囪揣浣嶅彉鏇村け璐�");
+ }
+
+ return WebResponseContent.Instance.OK("鑾峰彇鍒扮Щ搴撲换鍔�", createdTask);
+ });
+ }
+
+ private static Dt_LocationInfo CloneLocationSnapshot(Dt_LocationInfo location)
+ {
+ return new Dt_LocationInfo
+ {
+ Id = location.Id,
+ WarehouseId = location.WarehouseId,
+ LocationCode = location.LocationCode,
+ LocationName = location.LocationName,
+ RoadwayNo = location.RoadwayNo,
+ Row = location.Row,
+ Column = location.Column,
+ Layer = location.Layer,
+ Depth = location.Depth,
+ LocationType = location.LocationType,
+ LocationStatus = location.LocationStatus,
+ EnableStatus = location.EnableStatus,
+ Remark = location.Remark
+ };
+ }
+
+ private static Dt_StockInfo CloneStockSnapshot(Dt_StockInfo stockInfo)
+ {
+ return new Dt_StockInfo
+ {
+ Id = stockInfo.Id,
+ PalletCode = stockInfo.PalletCode,
+ PalletType = stockInfo.PalletType,
+ LocationId = stockInfo.LocationId,
+ LocationCode = stockInfo.LocationCode,
+ WarehouseId = stockInfo.WarehouseId,
+ StockStatus = stockInfo.StockStatus,
+ Remark = stockInfo.Remark,
+ OutboundDate = stockInfo.OutboundDate,
+ Details = stockInfo.Details?.Select(detail => new Dt_StockInfoDetail
+ {
+ Id = detail.Id,
+ StockId = detail.StockId,
+ MaterielCode = detail.MaterielCode,
+ MaterielName = detail.MaterielName,
+ OrderNo = detail.OrderNo,
+ BatchNo = detail.BatchNo,
+ ProductionDate = detail.ProductionDate,
+ EffectiveDate = detail.EffectiveDate,
+ SerialNumber = detail.SerialNumber,
+ StockQuantity = detail.StockQuantity,
+ OutboundQuantity = detail.OutboundQuantity,
+ Status = detail.Status,
+ Unit = detail.Unit,
+ InboundOrderRowNo = detail.InboundOrderRowNo,
+ Remark = detail.Remark
+ }).ToList()
+ };
+ }
+
+ /// <summary>
+ /// 妫�鏌ヨ揣浣嶆槸鍚﹂渶瑕佺Щ搴�
+ /// </summary>
+ /// <param name="location">璐т綅淇℃伅</param>
+ /// <returns>鏄惁闇�瑕佺Щ搴�</returns>
+ private static bool CheckForInternalTransfer(Dt_LocationInfo location)
+ {
+ return location.Depth == 2;
+ }
+
+ /// <summary>
+ /// 鏍规嵁宸烽亾鑾峰彇绌哄簱浣嶇敤浜庣Щ搴擄紙浼樺厛浜屾繁浣嶏紝鏃犲垯闄嶇骇鍙栦竴娣变綅锛�
+ /// </summary>
+ /// <param name="roadway">宸烽亾缂栧彿</param>
+ /// <returns>璐т綅瀵硅薄锛屾湭鎵惧埌鍒欒繑鍥瀗ull</returns>
+ private async Task<Dt_LocationInfo?> GetTransferLocationEmptyAsync(string roadway)
+ {
+ var freeStatus = LocationStatusEnum.Free.GetHashCode();
+
+ // 浼樺厛鑾峰彇浜屾繁浣嶇┖搴撲綅
+ var location = await BaseDal.QueryFirstAsync(x =>
+ x.Depth == 2
+ && x.LocationStatus == freeStatus
+ && x.RoadwayNo == roadway);
+
+ // 浜屾繁浣嶆棤绌洪棽鏃堕檷绾ц幏鍙栦竴娣变綅
+ if (location == null)
+ {
+ location = await BaseDal.QueryFirstAsync(x =>
+ x.Depth == 1
+ && x.LocationStatus == freeStatus
+ && x.RoadwayNo == roadway);
+ }
+
+ return location;
+ }
+
+ /// <summary>
+ /// 璁$畻娣卞害
+ /// </summary>
+ /// <param name="row">琛屽彿</param>
+ /// <param name="maxRow">鏈�澶ц鏁�</param>
+ /// <param name="maxDepth">鏈�澶ф繁搴�</param>
+ /// <param name="currentDepth">褰撳墠娣卞害</param>
+ /// <returns>璁$畻鍚庣殑娣卞害</returns>
private static int CalculateDepth(int row, int maxRow, int maxDepth, int currentDepth)
{
int mod = row % maxRow;
@@ -102,11 +564,27 @@
return currentDepth + 1;
}
- private static Dt_LocationInfo CreateLocationInfo(string roadwayNo, int row, int col, int layer, int depth)
+ /// <summary>
+ /// 鍒涘缓璐т綅淇℃伅
+ /// </summary>
+ /// <param name="roadwayNo">宸烽亾缂栧彿</param>
+ /// <param name="row">琛屽彿</param>
+ /// <param name="col">鍒楀彿</param>
+ /// <param name="layer">灞傛暟</param>
+ /// <param name="depth">娣卞害</param>
+ /// <returns>璐т綅淇℃伅瀵硅薄</returns>
+ private Dt_LocationInfo CreateLocationInfo(string roadwayNo, int row, int col, int layer, int depth)
{
+ var warehouse = _warehouseRepository.QueryData(x => x.WarehouseCode == roadwayNo).FirstOrDefault();
+
+ if (warehouse == null)
+ {
+ throw new InvalidOperationException($"鏈壘鍒板贩閬撶紪鍙蜂负 {roadwayNo} 鐨勪粨搴撲俊鎭�");
+ }
+
return new Dt_LocationInfo
{
- WarehouseId = 0,
+ WarehouseId = warehouse.WarehouseId,
Row = row,
Column = col,
Layer = layer,
@@ -115,186 +593,12 @@
EnableStatus = EnableStatusEnum.Normal.GetHashCode(),
LocationStatus = LocationStatusEnum.Free.GetHashCode(),
LocationType = LocationTypeEnum.Undefined.GetHashCode(),
- LocationCode = $"{roadwayNo}-{row:D3}-{col:D3}-{layer:D3}-{depth:D2}",
+ //LocationCode = $"{roadwayNo}-{row:D3}-{col:D3}-{layer:D3}",
+ LocationCode = $"{row:D3}-{col:D3}-{layer:D3}",
LocationName = $"{roadwayNo}宸烽亾{row:D3}琛寋col:D3}鍒梴layer:D3}灞倇depth:D2}娣�"
};
}
-
-
- /// <summary>
- /// 鑾峰彇绌洪棽璐т綅淇℃伅(鏍规嵁宸烽亾鏌ヨ)
- /// </summary>
- public async Task<Dt_LocationInfo?> GetLocationInfo(string roadwayNo)
- {
- var locations = await BaseDal.QueryDataAsync(x =>
- x.EnableStatus == EnableStatusEnum.Normal.GetHashCode() &&
- x.RoadwayNo == roadwayNo &&
- x.LocationStatus == LocationStatusEnum.Free.GetHashCode());
-
- return locations?
- .OrderBy(x => x.Layer)
- .ThenBy(x => x.Depth)
- .ThenBy(x => x.Column)
- .ThenBy(x => x.Row)
- .FirstOrDefault();
- }
-
- /// <summary>
- /// 鑾峰彇璐т綅淇℃伅(鏍规嵁宸烽亾鍜岃揣浣嶇紪鐮佹煡璇�)
- /// </summary>
- public async Task<Dt_LocationInfo?> GetLocationInfo(string roadwayNo, string locationCode)
- {
- return await BaseDal.QueryFirstAsync(x => x.RoadwayNo == roadwayNo && x.LocationCode == locationCode);
- }
-
- public async Task<Dt_LocationInfo> GetLocationInfoAsync( string locationCode)
- {
- return await BaseDal.QueryFirstAsync(x=>x.LocationCode == locationCode);
- }
-
- /// <summary>
- /// 鏇存柊璐т綅淇℃伅
- /// </summary>
- public async Task<bool> UpdateLocationInfoAsync(Dt_LocationInfo locationInfo)
- {
- return await BaseDal.UpdateDataAsync(locationInfo);
- }
-
-
- /// <summary>
- /// 妫�鏌ュ苟鐢熸垚绉诲簱浠诲姟鎴栬繑鍥炲嚭搴撲换鍔�
- /// </summary>
- /// <param name="locationID">浠诲姟鍙�</param>
- /// <returns>浠诲姟瀵硅薄</returns>
- public async Task<Dt_Task> TransferCheckAsync(int taskNum)
- {
- try
- {
- // 鏍规嵁浠诲姟鍙疯幏鍙栦换鍔�
- var outboundTask = await _taskRepository.QueryFirstAsync(x => x.TaskNum == taskNum);
- if (outboundTask == null)
- return null;
-
- var location = await BaseDal.QueryFirstAsync(x => x.LocationCode == outboundTask.SourceAddress);
-
- // 妫�鏌ユ槸鍚﹂渶瑕佽繘琛岀Щ搴�
- if (CheckForInternalTransfer(location))
- {
- // 璁$畻瀵瑰簲浣嶇疆鐨勭浉瀵瑰簱浣� 锛堝鏁拌鐨勪笅涓�琛屾垨鑰呭伓鏁拌鐨勪笂涓�琛岋級
- var newLocationID = GetRelativeLocationID(location);
-
- // 鑾峰彇鏂扮殑搴撲綅鐨勪换鍔�
- var internalTransferTask = await _taskRepository.QueryFirstAsync(x => x.SourceAddress == newLocationID && x.Roadway == outboundTask.Roadway);
-
- // 濡傛灉鏂扮殑搴撲綅娌℃湁鎵惧埌瀵瑰簲鐨勪换鍔�
- if (internalTransferTask == null)
- {
- return await HandleNoTaskAtLocation(outboundTask.SourceAddress, newLocationID, outboundTask);
- }
-
- // 鐩存帴杩斿洖涓�娣变綅鍑哄簱浠诲姟
- return internalTransferTask;
- }
-
- // 杩斿洖褰撳墠搴撲綅鐨勫嚭搴撲换鍔�
- return outboundTask;
- }
- catch (Exception)
- {
- return null;
- }
- }
-
-
- #region 绉诲簱鏂规硶
-
- /// <summary>
- /// 璁$畻鐩稿鐨勫簱浣岻D
- /// </summary>
- /// <param name="locationID">褰撳墠搴撲綅ID</param>
- /// <returns>鐩稿鐨勫簱浣岻D</returns>
- private string GetRelativeLocationID(Dt_LocationInfo locationInfo)
- {
- int line = locationInfo.Row;
-
- // 璁$畻鐩稿鐨勮揣浣嶈鍊硷紝濂囨暟琛岀殑涓嬩竴琛屾垨鑰呭伓鏁拌鐨勪笂涓�琛�
- int relativeLine = line % 2 == 1 ? line + 1 : line - 1;
-
- // 鏋勫缓鏂扮殑搴撲綅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);
-
- if (stockInfo == null)
- {
- // 濡傛灉娌℃湁搴撳瓨锛岀洿鎺ヨ繑鍥炲綋鍓嶅嚭搴撲换鍔�
- return outboundTask;
- }
- else
- {
- // 濡傛灉鏈夊簱瀛橈紝鐢熸垚绉诲簱浠诲姟
- var emptyLocation = await GetTransferLocationEmptyAsync(outboundTask.Roadway);
- var taskNo = await _taskRepository.GetTaskNo();
- Dt_Task newTransferTask = new Dt_Task()
- {
- CreateDate = DateTime.Now,
- Creater = App.User.UserName,
- CurrentAddress = originalLocationID,
- Grade = 99,
- NextAddress = emptyLocation.LocationCode,
- PalletCode = stockInfo.PalletCode,
- Remark = "绉诲簱",
- Roadway = stockInfo.LocationDetails.RoadwayNo,
- SourceAddress = originalLocationID,
- TaskNum = taskNo,
- TargetAddress = emptyLocation.LocationCode,
- TaskType = TaskTypeEnum.Relocation.GetHashCode(),
- };
-
- return await _taskRepository.Db.Insertable(newTransferTask).ExecuteReturnEntityAsync();
- }
- }
-
- /// <summary>
- /// 鏍规嵁璐т綅鏄惁闇�瑕佺Щ搴�
- /// </summary>
- /// <param name="locationID">璐т綅ID</param>
- /// <returns>鏄惁闇�瑕佺Щ搴�</returns>
- private bool CheckForInternalTransfer(Dt_LocationInfo location)
- {
- return location.Depth == 2 ? true : false;
- }
-
- /// <summary>
- /// 鏍规嵁宸烽亾鑾峰彇浜屾繁浣嶇殑绌哄簱浣�
- /// </summary>
- /// <param name="roadway">宸烽亾</param>
- /// <returns>璐т綅瀵硅薄</returns>
- private async Task<Dt_LocationInfo> GetTransferLocationEmptyAsync(string roadway)
- {
- return await BaseDal.QueryFirstAsync(x => x.Depth == 2 && x.LocationStatus == (LocationStatusEnum.Free.GetHashCode()) && 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 绉诲簱鏂规硶
-
+ #endregion 绉佹湁鏂规硶
}
-}
+}
\ No newline at end of file
--
Gitblit v1.9.3