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 | 291 ++++++++++++++++++++++++++++++++++++---------------------
1 files changed, 184 insertions(+), 107 deletions(-)
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
index d4b8ccf..d7aee6d 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
@@ -26,6 +26,7 @@
private readonly IRepository<Dt_StockInfo> _stockInfoRepository;
private readonly IRecordService _recordService;
private readonly IRepository<Dt_Warehouse> _warehouseRepository;
+ private readonly IUnitOfWorkManage _unitOfWorkManage;
/// <summary>
/// 鏋勯�犲嚱鏁�
@@ -33,19 +34,22 @@
/// <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) : base(baseDal)
+ IRecordService recordService,
+ IUnitOfWorkManage unitOfWorkManage) : base(baseDal)
{
_taskRepository = taskRepository;
_stockInfoRepository = stockInfoRepository;
_mapper = mapper;
_recordService = recordService;
_warehouseRepository = warehouseRepository;
+ _unitOfWorkManage = unitOfWorkManage;
}
/// <summary>
@@ -133,22 +137,43 @@
/// <summary>
/// 鏍规嵁宸烽亾鑾峰彇绌洪棽璐т綅淇℃伅
+ /// 鎺掑簭绛栫暐锛氭繁搴︿紭鍏堬紙浜屾繁浣嶄紭鍏堬級锛屽叾娆℃寜灞傘�佸垪銆佽鍗囧簭
/// </summary>
/// <param name="roadwayNo">宸烽亾缂栧彿</param>
- /// <returns>绌洪棽璐т綅淇℃伅锛屽鏋滄湭鎵惧埌鍒欒繑鍥瀗ull</returns>
+ /// <returns>绌洪棽璐т綅淇℃伅锛屽鏋滅┖闂茶揣浣嶄笉瓒冲垯杩斿洖null</returns>
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());
+ // HC 宸烽亾浣跨敤 Capacity 绫诲瀷锛屽叾浠栧贩閬撲娇鐢� ShelfCapacity 绫诲瀷
+ var locationType = roadwayNo.Contains("HC")
+ ? (int)LocationTypeEnum.Capacity
+ : (int)LocationTypeEnum.ShelfCapacity;
- return locations?
- .OrderByDescending(x => x.Depth) // 1. 娣卞害浼樺厛锛堜粠澶у埌灏忥級
- .ThenBy(x => x.Layer) // 2. 灞傛暟
- .ThenBy(x => x.Column) // 3. 鍒�
- .ThenBy(x => x.Row) // 4. 琛�
- .FirstOrDefault();
+ 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>
@@ -170,6 +195,16 @@
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>
@@ -239,24 +274,29 @@
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 newLocationID = GetRelativeLocationID(location);
+ var relativeLocationCode = GetRelativeLocationID(location);
- // 鑾峰彇鏂扮殑搴撲綅鐨勪换鍔�
- var internalTransferTask = await _taskRepository.QueryFirstAsync(x => x.SourceAddress == newLocationID && x.Roadway == outboundTask.Roadway);
+ // 鏌ユ壘鐩稿搴撲綅涓婄殑娲昏穬浠诲姟锛堟帓闄ゅ凡瀹屾垚銆佸凡鍙栨秷銆佸紓甯哥瓑缁堟�侊級
+ var activeTask = await _taskRepository.QueryFirstAsync(x =>
+ x.SourceAddress == relativeLocationCode
+ && x.Roadway == outboundTask.Roadway
+ && (x.TaskStatus == TaskOutStatusEnum.OutNew.GetHashCode()));
- // 濡傛灉鏂扮殑搴撲綅娌℃湁鎵惧埌瀵瑰簲鐨勪换鍔�
- if (internalTransferTask == null)
+ // 濡傛灉鐩稿搴撲綅娌℃湁娲昏穬浠诲姟锛屽皾璇曞垱寤虹Щ搴撲换鍔�
+ if (activeTask == null)
{
- return content.OK("鑾峰彇鍒扮Щ搴撲换鍔�", await HandleNoTaskAtLocation(newLocationID, outboundTask));
+ return await HandleNoTaskAtLocation(relativeLocationCode, outboundTask);
}
// 鐩存帴杩斿洖涓�娣变綅鍑哄簱浠诲姟
- return content.OK("鑾峰彇鍒颁竴娣变綅鍑哄簱浠诲姟", internalTransferTask);
+ return content.OK("鑾峰彇鍒颁竴娣变綅鍑哄簱浠诲姟", activeTask);
}
// 杩斿洖褰撳墠搴撲綅鐨勫嚭搴撲换鍔�
@@ -288,113 +328,132 @@
}
/// <summary>
- /// 澶勭悊娌℃湁浠诲姟鐨勫簱浣嶆儏鍐�
+ /// 澶勭悊娌℃湁娲昏穬浠诲姟鐨勫簱浣嶆儏鍐�
+ /// 鍒ゆ柇鏄惁鏈夊簱瀛橈紝鏈夊垯鐢熸垚绉诲簱浠诲姟锛屾棤鍒欑洿鎺ヨ繑鍥炲嚭搴撲换鍔�
/// </summary>
- /// <param name="newLocationID">鏂扮殑搴撲綅ID</param>
- /// <param name="outboundTask">鍑哄簱浠诲姟</param>
- /// <returns>鐢熸垚鐨勭Щ搴撲换鍔℃垨鍘熷鍑哄簱浠诲姟</returns>
- private async Task<Dt_Task> HandleNoTaskAtLocation(string newLocationID, Dt_Task outboundTask)
+ /// <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.StockStatus == StockStatusEmun.绌烘墭鐩樺簱瀛�.GetHashCode()) &&
x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode());
if (stockInfo == null)
{
// 濡傛灉娌℃湁搴撳瓨锛岀洿鎺ヨ繑鍥炲綋鍓嶅嚭搴撲换鍔�
- return outboundTask;
+ return WebResponseContent.Instance.OK("褰撳墠鍑哄簱浠诲姟", outboundTask);
}
- // 濡傛灉鏈夊簱瀛橈紝鐢熸垚绉诲簱浠诲姟
- var emptyLocation = await GetTransferLocationEmptyAsync(outboundTask.Roadway);
- var taskNo = await _taskRepository.GetTaskNo();
-
- var newTransferTask = new Dt_Task
+ // 鏈夊簱瀛樻椂锛屽湪浜嬪姟涓垱寤虹Щ搴撲换鍔″苟閿佸畾鐩稿叧璧勬簮
+ return await _unitOfWorkManage.BeginTranAsync(async () =>
{
- 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 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 createdTask = await _taskRepository.Db.Insertable(newTransferTask).ExecuteReturnEntityAsync();
- var beforeStock = CloneStockSnapshot(stockInfo);
- var beforeSourceLocation = stockInfo.LocationDetails == null ? null : CloneLocationSnapshot(stockInfo.LocationDetails);
- var beforeTargetLocation = CloneLocationSnapshot(emptyLocation);
+ // 鑾峰彇鐩爣绌哄簱浣�
+ var emptyLocation = await GetTransferLocationEmptyAsync(outboundTask.Roadway);
+ if (emptyLocation == null)
+ {
+ return WebResponseContent.Instance.Error("鏈壘鍒板彲鐢ㄧ殑绌哄簱浣嶇敤浜庣Щ搴�");
+ }
- // 鍒涘缓绉诲簱浠诲姟鍚庯紝绔嬪嵆閿佸畾搴撳瓨鍜岀浉鍏宠揣浣嶏紝閬垮厤骞跺彂閲嶅鍒嗛厤
- stockInfo.StockStatus = StockStatusEmun.绉诲簱閿佸畾.GetHashCode();
- var updateStockResult = await _stockInfoRepository.UpdateDataAsync(stockInfo);
+ 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 locationsToUpdate = new List<Dt_LocationInfo>();
- if (stockInfo.LocationDetails != null)
- {
- stockInfo.LocationDetails.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
- locationsToUpdate.Add(stockInfo.LocationDetails);
- }
+ 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);
- emptyLocation.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
- locationsToUpdate.Add(emptyLocation);
+ // 鍒涘缓绉诲簱浠诲姟鍚庯紝绔嬪嵆閿佸畾搴撳瓨鍜岀浉鍏宠揣浣�
+ stockInfo.StockStatus = StockStatusEmun.绉诲簱閿佸畾.GetHashCode();
+ var updateStockResult = await _stockInfoRepository.UpdateDataAsync(stockInfo);
- var updateLocationResult = await BaseDal.UpdateDataAsync(locationsToUpdate);
- if (!updateStockResult || !updateLocationResult)
- {
- throw new Exception("鍒涘缓绉诲簱浠诲姟鍚庢洿鏂板簱瀛樼姸鎬佹垨璐т綅鐘舵�佸け璐�");
- }
+ var locationsToUpdate = new List<Dt_LocationInfo>();
+ if (stockInfo.LocationDetails != null)
+ {
+ stockInfo.LocationDetails.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
+ locationsToUpdate.Add(stockInfo.LocationDetails);
+ }
- var saveStockRecordResult = await _recordService.AddStockChangeRecordAsync(
- beforeStock,
- stockInfo,
- StockChangeTypeEnum.Relocation,
- createdTask.TaskNum,
- createdTask.OrderNo,
- "绉诲簱浠诲姟棰勫崰搴撳瓨");
- if (!saveStockRecordResult)
- {
- throw new Exception("鍒涘缓绉诲簱浠诲姟鍚庤褰曞簱瀛樺彉鏇村け璐�");
- }
+ emptyLocation.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
+ locationsToUpdate.Add(emptyLocation);
- if (beforeSourceLocation != null && stockInfo.LocationDetails != null)
- {
- var saveSourceLocationRecordResult = await _recordService.AddLocationChangeRecordAsync(
- beforeSourceLocation,
- stockInfo.LocationDetails,
+ 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 (!saveSourceLocationRecordResult)
+ "绉诲簱浠诲姟閿佸畾鐩爣璐т綅");
+ if (!saveTargetLocationRecordResult)
{
- throw new Exception("鍒涘缓绉诲簱浠诲姟鍚庤褰曟簮璐т綅鍙樻洿澶辫触");
+ return WebResponseContent.Instance.Error("鍒涘缓绉诲簱浠诲姟鍚庤褰曠洰鏍囪揣浣嶅彉鏇村け璐�");
}
- }
- var saveTargetLocationRecordResult = await _recordService.AddLocationChangeRecordAsync(
- beforeTargetLocation,
- emptyLocation,
- LocationChangeType.RelocationAssignLocation,
- createdTask.TaskNum,
- createdTask.OrderNo,
- null,
- "绉诲簱浠诲姟閿佸畾鐩爣璐т綅");
- if (!saveTargetLocationRecordResult)
- {
- throw new Exception("鍒涘缓绉诲簱浠诲姟鍚庤褰曠洰鏍囪揣浣嶅彉鏇村け璐�");
- }
-
- return createdTask;
+ return WebResponseContent.Instance.OK("鑾峰彇鍒扮Щ搴撲换鍔�", createdTask);
+ });
}
private static Dt_LocationInfo CloneLocationSnapshot(Dt_LocationInfo location)
@@ -462,13 +521,30 @@
}
/// <summary>
- /// 鏍规嵁宸烽亾鑾峰彇浜屾繁浣嶇殑绌哄簱浣�
+ /// 鏍规嵁宸烽亾鑾峰彇绌哄簱浣嶇敤浜庣Щ搴擄紙浼樺厛浜屾繁浣嶏紝鏃犲垯闄嶇骇鍙栦竴娣变綅锛�
/// </summary>
/// <param name="roadway">宸烽亾缂栧彿</param>
- /// <returns>璐т綅瀵硅薄</returns>
- private async Task<Dt_LocationInfo> GetTransferLocationEmptyAsync(string roadway)
+ /// <returns>璐т綅瀵硅薄锛屾湭鎵惧埌鍒欒繑鍥瀗ull</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);
+ 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>
@@ -517,11 +593,12 @@
EnableStatus = EnableStatusEnum.Normal.GetHashCode(),
LocationStatus = LocationStatusEnum.Free.GetHashCode(),
LocationType = LocationTypeEnum.Undefined.GetHashCode(),
- LocationCode = $"{roadwayNo}-{row:D3}-{col:D3}-{layer:D3}",
+ //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}娣�"
};
}
#endregion 绉佹湁鏂规硶
}
-}
+}
\ No newline at end of file
--
Gitblit v1.9.3