Przeglądaj źródła

1、修复管理员没有菜单权限的拦截bug
2、当打开多个设备的页签时,保证每个页签收到的报文都要更新页面数据,即使不是当前激活页签

liweimin 1 tydzień temu
rodzic
commit
45aca2885a

+ 14 - 2
ruoyi-device/src/main/java/com/ruoyi/device/websocket/TsbWebSocketService.java

@@ -198,7 +198,9 @@ public class TsbWebSocketService
             return;
         }
         Set<String> userPermissions = getUserPermissionsFromSession(session);
-        if (StringUtils.isEmpty(userPermissions) || !userPermissions.contains(tsbWebSocketMessage.getCmdType()))
+        Long userId = getUserIdFromSession(session);
+        if (StringUtils.isEmpty(userPermissions) ||
+                (!userPermissions.contains(tsbWebSocketMessage.getCmdType()) && 1L != userId))
         {
             log.warn("WebSocket 用户权限不足, sessionId={}, cmdType={}", session.getId(), tsbWebSocketMessage.getCmdType());
             TsbWebSocketUsers.sendMessageToUserByText(session,
@@ -277,6 +279,15 @@ public class TsbWebSocketService
     }
 
     /**
+     * 从 Session 中安全获取 userId
+     */
+    public Long getUserIdFromSession(Session session)
+    {
+        Object userIdObj = session.getUserProperties().get(TsbWebSocketConstants.USER_ID_KEY);
+        return userIdObj instanceof Long ? (Long) userIdObj : null;
+    }
+
+    /**
      * 设备 MQTT 上行解析后推送到 Web端
      */
     public void pushPageSyncFromDevice(String pageKey, JSONObject bodyObj)
@@ -297,10 +308,11 @@ public class TsbWebSocketService
         Map<Long, Session> sessionUsers = TsbWebSocketUsers.getSessionUsers();
         if (!sessionUsers.containsKey(deviceSn))
         {
-            log.warn("web端用户未登录, deviceSn={}", deviceSn);
+            log.warn("web端用户未操作设备, deviceSn={}", deviceSn);
             return;
         }
         // 3、发送消息
+        log.info("MQTT上行同步消息, pageKey={}, deviceSn={}, bodyObj={}", pageKey, deviceSn, bodyObj);
         TsbWebSocketMessage msg = TsbWebSocketMessage.success(pageKey, "操作成功", bodyObj);
         TsbWebSocketUsers.sendMessageToUserByText(sessionUsers.get(deviceSn), msg);
     }

+ 1 - 1
ruoyi-ui/src/store/getters.js

@@ -29,7 +29,7 @@ const getters = {
     if (sn == null) {
       return 'home'
     }
-    return state.tsb.devicePanelMap[sn] || 'home'
+    return state.tsb.devicePanelMap[String(sn)] || 'home'
   }
 }
 export default getters

+ 11 - 4
ruoyi-ui/src/store/modules/tsb.js

@@ -18,7 +18,9 @@ const tsb = {
     /** 各设备页面表单缓存:key = deviceSn::panel */
     devicePageFormCache: {},
     /** 数据版本号,供页面 watch 实时更新 */
-    pageDataVersion: 0
+    pageDataVersion: 0,
+    /** 设备页签切换版本号,供子页面在切回时刷新 WS 数据 */
+    deviceSwitchVersion: 0
   },
   mutations: {
     SET_CURRENT_DEVICE(state, device) {
@@ -47,7 +49,7 @@ const tsb = {
       if (deviceSn == null || !panel) {
         return
       }
-      state.devicePanelMap = { ...state.devicePanelMap, [deviceSn]: panel }
+      state.devicePanelMap = { ...state.devicePanelMap, [String(deviceSn)]: panel }
     },
     SET_DEVICE_PANEL_MAP(state, map) {
       state.devicePanelMap = map || {}
@@ -86,16 +88,20 @@ const tsb = {
       state.devicePageFormCache = next
     },
     SET_WS_PAGE_DATA(state, { cmdType, data, navigate, deviceSn }) {
-      const key = deviceSn != null ? `${deviceSn}::${cmdType}` : cmdType
+      const sn = deviceSn != null ? String(deviceSn) : null
+      const key = sn != null ? `${sn}::${cmdType}` : cmdType
       state.pageData = { ...state.pageData, [key]: data }
       state.pageDataVersion += 1
       if (navigate) {
         state.initFromWs = { ...state.initFromWs, [key]: true }
       }
     },
+    NOTIFY_DEVICE_SWITCHED(state) {
+      state.deviceSwitchVersion += 1
+    },
     CLEAR_WS_INIT(state, payload) {
       const cmdType = payload && payload.cmdType ? payload.cmdType : payload
-      const deviceSn = payload && payload.deviceSn != null ? payload.deviceSn : null
+      const deviceSn = payload && payload.deviceSn != null ? String(payload.deviceSn) : null
       const key = deviceSn != null ? `${deviceSn}::${cmdType}` : cmdType
       const initFromWs = { ...state.initFromWs }
       delete initFromWs[key]
@@ -111,6 +117,7 @@ const tsb = {
       state.devicePanelMap = {}
       state.devicePageFormCache = {}
       state.pageDataVersion = 0
+      state.deviceSwitchVersion = 0
     }
   }
 }

+ 5 - 12
ruoyi-ui/src/utils/tsbWebSocket.js

@@ -161,14 +161,7 @@ function dispatchBroadcastMessage(message) {
   if (!messageHandler || !message) {
     return
   }
-  const msgSn = message._tsbDeviceSn
-  const tabSn = store.getters.tsbCurrentDeviceSn || currentDeviceSn
-  if (msgSn != null && tabSn != null && poolKey(msgSn) !== poolKey(tabSn)) {
-    return
-  }
-  const payload = { ...message }
-  delete payload._tsbDeviceSn
-  messageHandler(payload)
+  messageHandler(message)
 }
 
 function persistDeviceSession(device) {
@@ -221,10 +214,9 @@ function bindSocketEvents(ws, entry, deviceSn) {
     }
     try {
       const message = JSON.parse(evt.data)
-      broadcastWsMessage({ ...message, _tsbDeviceSn: deviceSn })
-      if (poolKey(deviceSn) === poolKey(currentDeviceSn)) {
-        messageHandler(message)
-      }
+      const payload = { ...message, _tsbDeviceSn: deviceSn }
+      broadcastWsMessage(payload)
+      messageHandler(payload)
     } catch (e) {
       console.warn('TSB WebSocket 消息解析失败', e)
     }
@@ -465,6 +457,7 @@ function switchToDevice(device) {
   currentDeviceSn = device.deviceSn
   userInitiatedConnect = true
   store.commit('tsb/SET_CURRENT_DEVICE', device)
+  store.commit('tsb/NOTIFY_DEVICE_SWITCHED')
   const panelMap = store.state.tsb.devicePanelMap || {}
   saveTsbDeviceSession(buildDeviceSession(device, opened, panelMap))
   broadcastWsStatusForCurrent()

+ 8 - 5
ruoyi-ui/src/utils/tsbWsRouter.js

@@ -25,7 +25,10 @@ export function handleTsbWsMessage(msg) {
     return
   }
 
-  const deviceSn = store.getters.tsbCurrentDeviceSn || tsbWebSocket.getCurrentDeviceSn()
+  const currentSn = store.getters.tsbCurrentDeviceSn || tsbWebSocket.getCurrentDeviceSn()
+  const deviceSn = msg._tsbDeviceSn != null ? String(msg._tsbDeviceSn)
+    : (currentSn != null ? String(currentSn) : null)
+  const isActiveDevice = deviceSn == null || deviceSn === String(currentSn)
   const routePath = getRouteByCmdType(msg.cmdType, deviceSn)
   if (!routePath) {
     return
@@ -57,16 +60,16 @@ export function handleTsbWsMessage(msg) {
     store.commit('tsb/SET_DEVICE_PANEL', { deviceSn, panel: 'tax' })
   }
 
-  const currentPanel = store.getters.tsbCurrentPanel
-  const onTargetPage = isOnCmdRoute(router.currentRoute.path, msg.cmdType, deviceSn, currentPanel)
+  const devicePanel = (store.state.tsb.devicePanelMap || {})[String(deviceSn)] || 'home'
+  const onTargetPage = isOnCmdRoute(router.currentRoute.path, msg.cmdType, deviceSn, devicePanel)
   store.commit('tsb/SET_WS_PAGE_DATA', {
     cmdType: msg.cmdType,
     data,
-    navigate: !onTargetPage,
+    navigate: isActiveDevice && !onTargetPage,
     deviceSn
   })
 
-  if (!onTargetPage) {
+  if (isActiveDevice && !onTargetPage) {
     if (routePath === TSB_WORKSPACE_ROUTE) {
       navigateToWorkspace().catch(() => {})
     } else {

+ 23 - 31
ruoyi-ui/src/views/app-common/tax/index.vue

@@ -114,8 +114,6 @@
 <script>
 import { getTsbWsBind } from '@/api/tsb/ws'
 import tsbWebSocket from '@/utils/tsbWebSocket'
-import { isOnCmdRoute } from '@/utils/tsbCmdRoute'
-
 const PAGE_CMD = 'common:tax'
 
 /** 按钮操作类型 */
@@ -226,8 +224,11 @@ export default {
   },
   watch: {
     '$store.state.tsb.pageDataVersion'() {
-      if (this.canOperateDevice) {
-        this.tryApplyWsPageData(false)
+      this.tryApplyWsPageData(false)
+    },
+    '$store.state.tsb.deviceSwitchVersion'() {
+      if (String(this.$store.getters.tsbCurrentDeviceSn) === String(this.ownerDeviceSn)) {
+        this.refreshPageFromStore()
       }
     }
   },
@@ -235,18 +236,10 @@ export default {
     this.wsTimer = setInterval(() => {
       this.wsConnected = tsbWebSocket.isConnected()
     }, 1000)
-    if (this.restorePageCache()) {
-      return
-    }
-    this.initPageData()
+    this.refreshPageFromStore()
   },
   activated() {
-    if (this.restorePageCache()) {
-      return
-    }
-    if (this.tryApplyWsPageData(true)) {
-      return
-    }
+    this.refreshPageFromStore()
   },
   deactivated() {
     this.savePageCache()
@@ -258,11 +251,20 @@ export default {
   methods: {
     pageCacheKey(deviceSn) {
       const sn = deviceSn != null ? deviceSn : this.ownerDeviceSn
-      return sn != null ? `${sn}::tax` : null
+      return sn != null ? `${String(sn)}::tax` : null
     },
     pageDataKey(deviceSn) {
       const sn = deviceSn != null ? deviceSn : this.ownerDeviceSn
-      return sn != null ? `${sn}::${PAGE_CMD}` : PAGE_CMD
+      return sn != null ? `${String(sn)}::${PAGE_CMD}` : PAGE_CMD
+    },
+    refreshPageFromStore() {
+      if (this.tryApplyWsPageData(true)) {
+        return
+      }
+      if (this.restorePageCache()) {
+        return
+      }
+      this.initPageData()
     },
     savePageCache(deviceSn) {
       const key = this.pageCacheKey(deviceSn)
@@ -342,28 +344,18 @@ export default {
         this.syncDefaultParamsToDevice()
       }
     },
-    /** 从 store 应用 WebSocket 推送数据 */
+    /** 从 store 应用 WebSocket 推送数据(按设备 SN 隔离,与当前激活页签无关) */
     tryApplyWsPageData(clearInitFlag) {
       const dataKey = this.pageDataKey()
       const data = this.$store.state.tsb.pageData[dataKey]
-      const initFromWs = this.$store.state.tsb.initFromWs[dataKey]
       if (!data) {
         return false
       }
-      if (initFromWs) {
-        this.applyTaxData(data)
-        if (clearInitFlag) {
-          this.$store.commit('tsb/CLEAR_WS_INIT', { cmdType: PAGE_CMD, deviceSn: this.ownerDeviceSn })
-        }
-        this.savePageCache()
-        return true
-      }
-      if (isOnCmdRoute(this.$route.path, PAGE_CMD, this.ownerDeviceSn, this.$store.getters.tsbCurrentPanel)) {
-        this.applyTaxData(data)
-        this.savePageCache()
-        return true
+      this.applyTaxData(data)
+      if (clearInitFlag && this.$store.state.tsb.initFromWs[dataKey]) {
+        this.$store.commit('tsb/CLEAR_WS_INIT', { cmdType: PAGE_CMD, deviceSn: this.ownerDeviceSn })
       }
-      return false
+      return true
     },
     resetFormDefaults() {
       const dateParts = createDefaultDateParts()

+ 5 - 1
ruoyi-ui/src/views/tsb/device-shell/index.vue

@@ -115,7 +115,11 @@ export default {
       if (this.isActiveTab(device)) {
         return
       }
-      tsbWebSocket.switchToDevice(device).catch(() => {})
+      tsbWebSocket.switchToDevice(device).then(() => {
+        this.$nextTick(() => {
+          this.$store.commit('tsb/NOTIFY_DEVICE_SWITCHED')
+        })
+      }).catch(() => {})
     },
     closeDevice(device) {
       const closingCurrent = this.isActiveTab(device)