From 9b77acb859f0866f3a854d2a2842072b2fe9cca8 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期二, 31 三月 2026 16:43:27 +0800
Subject: [PATCH] feat(wms): 完善库存三维看板与库存/货位变更追踪

---
 Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs |  147 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 145 insertions(+), 2 deletions(-)

diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
index af5cc9a..aff926c 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
@@ -11,6 +11,7 @@
 using WIDESEA_DTO.Basic;
 using WIDESEA_DTO.Task;
 using WIDESEA_IBasicService;
+using WIDESEA_IRecordService;
 using WIDESEA_Model.Models;
 
 namespace WIDESEA_BasicService
@@ -23,6 +24,7 @@
         private readonly IMapper _mapper;
         private readonly IRepository<Dt_Task> _taskRepository;
         private readonly IRepository<Dt_StockInfo> _stockInfoRepository;
+        private readonly IRecordService _recordService;
 
         /// <summary>
         /// 鏋勯�犲嚱鏁�
@@ -34,11 +36,13 @@
             IRepository<Dt_LocationInfo> baseDal,
             IRepository<Dt_Task> taskRepository,
             IRepository<Dt_StockInfo> stockInfoRepository,
-            IMapper mapper) : base(baseDal)
+            IMapper mapper,
+            IRecordService recordService) : base(baseDal)
         {
             _taskRepository = taskRepository;
             _stockInfoRepository = stockInfoRepository;
             _mapper = mapper;
+            _recordService = recordService;
         }
 
         /// <summary>
@@ -172,7 +176,48 @@
         /// <returns>鏇存柊鏄惁鎴愬姛</returns>
         public async Task<bool> UpdateLocationInfoAsync(Dt_LocationInfo locationInfo)
         {
-            return await BaseDal.UpdateDataAsync(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>
@@ -281,6 +326,9 @@
             };
 
             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();
@@ -302,9 +350,104 @@
                 throw new Exception("鍒涘缓绉诲簱浠诲姟鍚庢洿鏂板簱瀛樼姸鎬佹垨璐т綅鐘舵�佸け璐�");
             }
 
+            var saveStockRecordResult = await _recordService.AddStockChangeRecordAsync(
+                beforeStock,
+                stockInfo,
+                StockChangeTypeEnum.Relocation,
+                createdTask.TaskNum,
+                createdTask.OrderNo,
+                "绉诲簱浠诲姟棰勫崰搴撳瓨");
+            if (!saveStockRecordResult)
+            {
+                throw new Exception("鍒涘缓绉诲簱浠诲姟鍚庤褰曞簱瀛樺彉鏇村け璐�");
+            }
+
+            if (beforeSourceLocation != null && stockInfo.LocationDetails != null)
+            {
+                var saveSourceLocationRecordResult = await _recordService.AddLocationChangeRecordAsync(
+                    beforeSourceLocation,
+                    stockInfo.LocationDetails,
+                    LocationChangeType.RelocationAssignLocation,
+                    createdTask.TaskNum,
+                    createdTask.OrderNo,
+                    null,
+                    "绉诲簱浠诲姟閿佸畾婧愯揣浣�");
+                if (!saveSourceLocationRecordResult)
+                {
+                    throw new Exception("鍒涘缓绉诲簱浠诲姟鍚庤褰曟簮璐т綅鍙樻洿澶辫触");
+                }
+            }
+
+            var saveTargetLocationRecordResult = await _recordService.AddLocationChangeRecordAsync(
+                beforeTargetLocation,
+                emptyLocation,
+                LocationChangeType.RelocationAssignLocation,
+                createdTask.TaskNum,
+                createdTask.OrderNo,
+                null,
+                "绉诲簱浠诲姟閿佸畾鐩爣璐т綅");
+            if (!saveTargetLocationRecordResult)
+            {
+                throw new Exception("鍒涘缓绉诲簱浠诲姟鍚庤褰曠洰鏍囪揣浣嶅彉鏇村け璐�");
+            }
+
             return 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>

--
Gitblit v1.9.3