From 715cf4c8b9e097aae6c4bcaf00bdd67a763529c4 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期一, 30 三月 2026 13:58:09 +0800
Subject: [PATCH] feat(stockChat): 集成 SignalR 实现实时库存更新

---
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/Task_HtyService.cs   |    5 ++
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs       |   12 +++---
 Code/WMS/WIDESEA_WMSClient/src/views/stock/stockChat.vue                |   41 ++++++++++++++++++++
 Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITask_HtyService.cs |    5 ++
 4 files changed, 57 insertions(+), 6 deletions(-)

diff --git a/Code/WMS/WIDESEA_WMSClient/src/views/stock/stockChat.vue b/Code/WMS/WIDESEA_WMSClient/src/views/stock/stockChat.vue
index 0f7402e..9cc6436 100644
--- a/Code/WMS/WIDESEA_WMSClient/src/views/stock/stockChat.vue
+++ b/Code/WMS/WIDESEA_WMSClient/src/views/stock/stockChat.vue
@@ -59,8 +59,12 @@
 import { ref, reactive, onMounted, onUnmounted, watch, getCurrentInstance } from 'vue'
 import * as THREE from 'three'
 import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
+import * as signalR from '@microsoft/signalr'
 
 const { proxy } = getCurrentInstance()
+
+// SignalR 杩炴帴
+let connection = null
 
 // 棰滆壊甯搁噺
 const COLOR_MAP = {
@@ -101,6 +105,39 @@
 let locationMesh = null
 let locationData = []
 let animationId = null
+
+// SignalR 鍒濆鍖�
+function initSignalR() {
+  proxy.http.post('api/User/GetCurrentUserInfo').then((result) => {
+    connection = new signalR.HubConnectionBuilder()
+      .withAutomaticReconnect()
+      .withUrl(`${proxy.http.ipAddress}stockHub?userName=${result.data.userName}`)
+      .build();
+
+    connection.start().catch((err) => console.log('SignalR杩炴帴澶辫触:', err));
+
+    connection.on('StockUpdated', (update) => {
+      // 鏇存柊瀵瑰簲璐т綅鐨勬暟鎹�
+      const idx = locationData.findIndex(x => x.locationId === update.locationId);
+      if (idx !== -1) {
+        locationData[idx].stockQuantity = update.stockQuantity;
+        locationData[idx].stockStatus = update.stockStatus;
+        // 閲嶆柊娓叉煋鍗曚釜璐т綅棰滆壊
+        updateInstanceColor(idx, update.stockStatus);
+      }
+    });
+  });
+}
+
+// 鏇存柊鍗曚釜璐т綅棰滆壊
+function updateInstanceColor(instanceId, stockStatus) {
+  if (!locationMesh) return;
+  const loc = locationData[instanceId];
+  if (!loc) return;
+  const color = getLocationColor(loc);
+  locationMesh.setColorAt(instanceId, new THREE.Color(color));
+  locationMesh.instanceColor.needsUpdate = true;
+}
 
 // 鑾峰彇璐т綅棰滆壊
 function getLocationColor(location) {
@@ -386,6 +423,7 @@
 onMounted(() => {
   initThreeJS()
   loadWarehouseList()
+  initSignalR()
   window.addEventListener('resize', onWindowResize)
 })
 
@@ -401,6 +439,9 @@
   if (renderer) {
     renderer.dispose()
   }
+  if (connection) {
+    connection.stop()
+  }
 })
 </script>
 
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITask_HtyService.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITask_HtyService.cs
index 8bd7243..53ced20 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITask_HtyService.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITask_HtyService.cs
@@ -6,6 +6,7 @@
 using System.Text;
 using System.Threading.Tasks;
 using WIDESEA_Core;
+using WIDESEA_Core.BaseRepository;
 using WIDESEA_Core.BaseServices;
 using WIDESEA_Core.Enums;
 using WIDESEA_DTO.Stock;
@@ -15,4 +16,8 @@
 
 public interface ITask_HtyService : IService<Dt_Task_Hty>
 {
+    /// <summary>
+    /// 鑾峰彇浠诲姟鍘嗗彶浠撳偍鎺ュ彛
+    /// </summary>
+    IRepository<Dt_Task_Hty> Repository { get; }
 }
\ No newline at end of file
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
index aaf105e..ebf7fb8 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -254,7 +254,7 @@
                     {
                         return content.Error($"浠诲姟瀹屾垚澶辫触锛歁ES杩涚珯澶辫触: {inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "鏈煡閿欒"}");
                     }
-                    return await CompleteTaskAsync(task, "Inbound");
+                    return await CompleteTaskAsync(task);
                 });
             }
             catch (Exception ex)
@@ -308,7 +308,7 @@
                         return content.Error($"浠诲姟瀹屾垚澶辫触锛歁ES鍑虹珯澶辫触: {outboundResult?.Data?.Msg ?? outboundResult?.ErrorMessage ?? "鏈煡閿欒"}");
                     }
 
-                    return await CompleteTaskAsync(task, "Outbound");
+                    return await CompleteTaskAsync(task);
                 });
             }
             catch (Exception ex)
@@ -354,7 +354,7 @@
                     if (!updateSourceResult || !updateTargetResult || !updateStockResult)
                         return WebResponseContent.Instance.Error("绉诲簱浠诲姟瀹屾垚澶辫触");
 
-                    return await CompleteTaskAsync(task, "Relocation");
+                    return await CompleteTaskAsync(task);
                 });
             }
             catch (Exception ex)
@@ -565,7 +565,7 @@
         /// <summary>
         /// 瀹屾垚浠诲姟鍚庣粺涓�澶勭悊锛堝垹闄や换鍔℃暟鎹級
         /// </summary>
-        private async Task<WebResponseContent> CompleteTaskAsync(Dt_Task task, string operateType)
+        private async Task<WebResponseContent> CompleteTaskAsync(Dt_Task task, string operateType = "")
         {
             var deleteTaskResult = await BaseDal.DeleteDataAsync(task);
             if (!deleteTaskResult) return WebResponseContent.Instance.Error("浠诲姟瀹屾垚澶辫触");
@@ -573,8 +573,8 @@
             var historyTask = _mapper.Map<Dt_Task_Hty>(task);
             historyTask.InsertTime = DateTime.Now;
             historyTask.OperateType = operateType;
-            var saveResult = _task_HtyService.AddData(historyTask);
-            if (!saveResult.Status) return WebResponseContent.Instance.Error("浠诲姟鍘嗗彶淇濆瓨澶辫触");
+            var saveResult = await _task_HtyService.Repository.AddDataAsync(historyTask) > 0;
+            if (!saveResult) return WebResponseContent.Instance.Error("浠诲姟鍘嗗彶淇濆瓨澶辫触");
 
             return WebResponseContent.Instance.OK("浠诲姟瀹屾垚");
         }
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/Task_HtyService.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/Task_HtyService.cs
index 9731102..9bff8b4 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/Task_HtyService.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/Task_HtyService.cs
@@ -11,6 +11,11 @@
     public class Task_HtyService : ServiceBase<Dt_Task_Hty, IRepository<Dt_Task_Hty>>, ITask_HtyService
     {
         /// <summary>
+        /// 鑾峰彇浠诲姟鍘嗗彶浠撳偍鎺ュ彛
+        /// </summary>
+        public IRepository<Dt_Task_Hty> Repository => BaseDal;
+
+        /// <summary>
         /// 鏋勯�犲嚱鏁�
         /// </summary>
         /// <param name="baseDal">鍩虹鏁版嵁璁块棶瀵硅薄</param>

--
Gitblit v1.9.3