rxjs-封装WebsocketService

500 阅读2分钟

api文档:cn.rx.js.org/

封装一个类,新建js文件

import { EMPTY, merge, Observable, Subject, ReplaySubject, timer, throwError, of } from 'rxjs'
import {
  catchError,
  delayWhen,
  filter,
  retryWhen,
  switchAll,
  tap,
  mergeMap,
  finalize,
} from 'rxjs/operators'
import { webSocket, WebSocketSubject } from 'rxjs/webSocket'
let index = 0

const RECONNECT_INTERVAL = 3000
const RECONNECT_ATTEMPTS = 3

class WebsocketChannel {
  constructor() {
    this.channelCollection = {}
  }

  addChannel(key, channel) {
    this.channelCollection[key] = channel
  }

  deleteChannel(key) {
    delete this.channelCollection[key]
  }

  clearChannel() {
    this.channelCollection = {}
  }

  hasChannel(key) {
    return this.channelCollection.hasOwnProperty(key)
  }

  forEach(fn) {
    for (let key in this.channelCollection) {
      fn(this.channelCollection[key])
    }
  }
}

export class WebsocketService {
  socket$
  reconnectCount = 0

  constructor(url, stopConnect) {
    this.url = url
    if (!this.stopConnect && stopConnect) {
      this.stopConnect = stopConnect
    } else {
      this.stopConnect = this.defaultStopConnect
    }
    this.channels = new WebsocketChannel()
    this.connect()
    this.subject = new Subject()
  }

  defaultStopConnect() {}

  connect() {
    let _this = this
    this.socket$ = webSocket({
      url: this.url,
      openObserver: {
        next: () => {
          _this.reconnectCount = 0
          _this.socket$.subscribe(message => {
            _this.subject.next(message)
          })
          //重连订阅消息
          _this.channels.forEach(item => {
            _this.next(item.subMsg())
          })
        },
      },
    })
    this.socket$.subscribe({
      next: () => {},
      error: () => {
        this.reconnect()
      },
      compelte: () => {
        console.log('compelte')
      },
    })
  }

  // 断线重连
  reconnect() {
    timer(RECONNECT_INTERVAL).subscribe(() => {
      this.reconnectCount++
      if (this.reconnectCount > RECONNECT_ATTEMPTS) {
        console.error('重连结束!!')
        this.stopConnect()
      } else {
        console.info(`第${this.reconnectCount}次websocket重连`)
        this.socket$ = undefined
        this.connect()
      }
    })
  }

  multiplex(subMsg, unsubMsg, messageFilter) {
    let id = ++index
    if (!this.channels.hasChannel(id)) {
      this.channels.addChannel(id, {
        subMsg,
        unsubMsg,
        messageFilter,
      })
    }
    const self = this
    return new Observable(observer => {
      try {
        self.next(subMsg())
      } catch (err) {
        observer.error(err)
      }

      const subscription = self.subject.subscribe(
        x => {
          try {
            if (messageFilter(x)) {
              observer.next(x)
            }
          } catch (err) {
            observer.error(err)
          }
        },
        err => observer.error(err),
        () => observer.complete()
      )

      return () => {
        try {
          self.next(unsubMsg())
          self.channels.deleteChannel(id)
        } catch (err) {
          observer.error(err)
        }
        subscription.unsubscribe()
      }
    })
  }

  //发送消息
  next(msg) {
    this.socket$.next(msg)
  }
}

挂载websocket在vue上

<template>
  <div style="display: none" class="notice-log"></div>
</template>
<script>

import { WebsocketService } from '@/api/WebsocketService.js'
import { getBaseUrl } from '@/utils/api'

export default {
  name: 'noticeLog',
  data() {
    return {
      noticeId: '',
      notification: null
    }
  },
  created() {
    //刷新创建websocket
    let vm = this
    if (vm.$store.state.account.token != '') {
      vm.mountWebsockt().then(() => {
        if (vm.$root.$wsmaintenance) {
          //全局通知消息
          const observable = this.$root.$wsmaintenance.multiplex(
            () => ({ op : "snapshot"}),
            () => ({ op : "update", update: { id : null}}),
            data => true
          )
    
          const subscription = observable.subscribe(data => {
            const res = data.data
            if (data.code === 0 && res.title) {
              if (res.id && res.startTime) {
                if (this.notificatio) this.notificatio.close()
                const h = this.$createElement
                this.notificatio = this.$notify.info({
                  dangerouslyUseHTMLString: true,
                  message: h('strong', null, [
                    h('div', { class: 'noticelog-bottom'}, `标题:${res.title}`),
                    h('div', { class: 'noticelog-bottom'}, `开始时间:${res.startTime}`),
                    h('div', { class: 'noticelog-bottom'}, `结束时间:${res.endTime}`),
                    h('div', { class: 'noticelog-bottom'}, `内容:${res.content}`),
                    h('a', {
                      on:{
                        click:()=>{
                          this.closeTips(res.id)
                        }
                      },
                      class: 'noticelog-btn',
                      href: 'javascript:;'
                    }, "不再提示")
                ]),
                  position: 'bottom-right',
                  duration: 0
                })
                this.noticeId = res.id
              } else {
                this.$notify.info({
                  dangerouslyUseHTMLString: true,
                  message: `<strong><div class="noticelog-bottom">标题:${res.title}</div><div>内容:${res.content}</div></strong>`,
                  position: 'bottom-right',
                  duration: 0
                })
              }
            } 
          })
          vm.$once('hook:beforeDestroy', () => {
            if (this.notificatio) this.notificatio.close()
            // subscription.unsubscribe()
          })
        }
      })
    } 
  },
  mounted() {
    //监听鼠标点击事件
    // document.addEventListener('mouseup', (e) => {
    //   let _track = document.getElementById('notips');
    //     if (_track) {
    //       if (_track.contains(e.target)) {
    //       this.closeTips();//事件
    //     }
    //   }
    // })
  },
  methods: {
    //挂载websocket在vue上
    async mountWebsockt() {
      let vm = this
      try {
        const token = 'Bearer ' + vm.$store.state.account.token
        const apiUrl = getBaseUrl().replace(/^https?\:\/\//i, "")
        const wsHeader = getBaseUrl().slice(0, 5) === 'https' ? 'wss' : 'ws'
        vm.$root.$wsmaintenance = new WebsocketService(
          `${wsHeader}://${apiUrl}/oauth/websocket/maintenance?token=${token}`
        )
      } catch (error) {
        console.error('websocket创建失败')
      }
    },
    closeTips(id) {
      this.$root.$wsmaintenance.next({ op : "update", update: { id : id}})
      this.notificatio.close()
    }
  },
}
</script>

<style lang='scss'>
  .noticelog-bottom {
    margin-bottom: 5px;
    word-break: break-all;
  }
  .noticelog-btn {
    color: #409eff;
    float: right;
  }
</style>

组件中调用

<template>
  <div style="display: none"></div>
</template>
<script>
import { WebsocketService } from '@/api/WebsocketService.js'
import realTimeMonitorApi from '@/api/gasLeakMonitor/realTimeMonitor.js'
import { Notification } from 'element-ui'
import { map } from 'rxjs'
import routerMixin from '@/mixins/router.js'
import messageManageApi from '@/api/customerServiceManage/messageManage'
import { notifications } from '@/views/gmis/customerServiceManage/messageManage/constant.js'
export default {
  name: 'NotifictionMessage',
  mixins: [routerMixin],
  data() {
    return {
      notifications: [],
      currentSystemTypeId: null,
    }
  },
  created() {
    //刷新创建websocket
    let vm = this
    if (vm.$store.state.account.token != '') {
      vm.mountWebsockt().then(() => {
        if (vm.$root.$websocket) {
          //全局通知消息
          const observable = this.$root.$websocket.multiplex(
            () => ({ topic: 'PUSH_ALARM_SYSTEM_PUSH_POP_UP_WINDOW', operation: 'open' }),
            () => ({ topic: 'PUSH_ALARM_SYSTEM_PUSH_POP_UP_WINDOW', operation: 'close' }),
            data => data.module == 'PUSH_ALARM_SYSTEM' && data.type == 'PUSH_POP_UP_WINDOW'
          )
          const subscription = observable.pipe(map(item => item.data)).subscribe(data => {
            this.$store.commit(
              'account/setMessageCount',
              this.$store.state.account.messageCount + 1
            )
            vm.notifications.push(data)
            if (vm.notifications.length > 6) {
              Notification.closeAll()
              vm.$notify({
                message: `您收到了${vm.notifications.length}条新消息`,
                position: 'bottom-right',
                onClick() {
                  vm.currentSystemTypeId = '4'
                  vm.getRouter(vm.currentSystemTypeId, {
                    path: '/customerServiceManage/messageManage/Index',
                    params: JSON.parse(data.jsonParam),
                  })
                },
              })
            } else {
              const notification = vm.$notify({
                message: data.content,
                position: 'bottom-right',
                onClick() {
                  const redirectUrl = notifications[data.type].value[data.module].redirectUrl
                  if (redirectUrl) {
                    vm.currentSystemTypeId = redirectUrl['systemTypeId']
                    if (data.module == 'NOTIFY_ALARM_SYSTEM') {
                      vm.markReadedStatus(data)
                      let jsonParam = JSON.parse(data.jsonParam)
                      if (jsonParam.customerTypeName == 'RESIDENT') {
                        vm.getRouter('21', {
                          path: '/householdLeakMonitor/inhabitantGasLeakEvent',
                          query: { eventId: JSON.parse(data.jsonParam).eventId },
                        })
                      } else {
                        vm.getRouter('21', {
                          path: '/householdLeakMonitor/businessGasLeakEvent',
                          query: { eventId: JSON.parse(data.jsonParam).eventId },
                        })
                      }
                    } else {
                      vm.getRouter(vm.currentSystemTypeId, {
                        path: redirectUrl['path'],
                        query: JSON.parse(data.jsonParam),
                      })
                    }
                    vm.$store.commit(
                      'account/setMessageCount',
                      vm.$store.state.account.messageCount - 1
                    )
                  }
                  notification.close()
                },
              })
            }
          })
          vm.$once('hook:beforeDestroy', () => {
            subscription.unsubscribe()
          })
        }
      })
    } else {
      this.$emit('ws-ready')
    }
  },
  methods: {
    //挂载websocket在vue上
    async mountWebsockt() {
      let vm = this
      try {
        const {
          data: { isSuccess, data },
        } = await realTimeMonitorApi.getWebsocketUrl()
        if (isSuccess) {
          const user = vm.$store.state.account.user
          const token = 'Bearer ' + vm.$store.state.account.token
          vm.$root.$websocket = new WebsocketService(
            `ws://${data.ip + ':' + data.port + data.path}?token=${token}&tenant=${
              user.tenantId
            }&account=${user.account}`
          )
        }
      } catch (error) {
        console.error('websocket创建失败')
      }
      this.$emit('ws-ready')
    },
    //标记已读
    async markReadedStatus(data) {
      const {
        data: { isSuccess },
      } = await messageManageApi.updateReadStatus({
        id: data.receiveId,
        isRead: true,
      })
    },
  },
}
</script>
    updateFeaturebyWebsocket() {
      let vm = this
      const alarmObservable = vm.$root.$websocket.multiplex(
        () => ({ topic: 'PUSH_ALARM_SYSTEM_PUSH_WARN', operation: 'open' }),
        () => ({ topic: 'PUSH_ALARM_SYSTEM_PUSH_WARN', operation: 'close' }),
        data => data.module == 'PUSH_ALARM_SYSTEM' && data.type == 'PUSH_WARN'
      )
      const subscription = alarmObservable.pipe(map(item => item.data)).subscribe(data => {
        if (vm.vectorSource) {
          const feature = vm.vectorSource.getFeatureById(data.pointId)
          if (feature) {
            vm.vectorSource.removeFeature(feature)
            const newFeature = new Feature({
              geometry: new Point([parseFloat(data.longitude), parseFloat(data.latitude)]),
              properties: {
                ...data,
              },
            })
            newFeature.setId(data.pointId)
            vm.vectorSource.addFeature(newFeature)
          }
        }
      })
      vm.$once('hook:beforeDestroy', () => {
        subscription.unsubscribe()
      })
    },

第二次链接

<template>
  <div style="display: none"></div>
</template>
<script>
import { WebsocketService } from '@/api/WebsocketService.js'
import realTimeMonitorApi from '@/api/gasLeakMonitor/realTimeMonitor.js'
import { Notification } from 'element-ui'
import { map } from 'rxjs'
import routerMixin from '@/mixins/router.js'
import messageManageApi from '@/api/customerServiceManage/messageManage'
import { notifications } from '@/views/gmis/customerServiceManage/messageManage/constant.js'
export default {
  name: 'NotifictionMessage',
  mixins: [routerMixin],
  data() {
    return {
      notifications: [],
      currentSystemTypeId: null,
    }
  },
  created() {
    //刷新创建websocket
    let vm = this
    if (vm.$store.state.account.token != '') {
      vm.mountWebsockt().then(() => {
        if (vm.$root.$websocket) {
          //全局通知消息
          const observable = this.$root.$websocket.multiplex(
            () => ({ topic: 'PUSH_ALARM_SYSTEM_PUSH_POP_UP_WINDOW', operation: 'open' }),
            () => ({ topic: 'PUSH_ALARM_SYSTEM_PUSH_POP_UP_WINDOW', operation: 'close' }),
            data => data.module == 'PUSH_ALARM_SYSTEM' && data.type == 'PUSH_POP_UP_WINDOW'
          )
          const subscription = observable.pipe(map(item => item.data)).subscribe(data => {
            this.$store.commit(
              'account/setMessageCount',
              this.$store.state.account.messageCount + 1
            )
            vm.notifications.push(data)
            if (vm.notifications.length > 6) {
              Notification.closeAll()
              vm.$notify({
                message: `您收到了${vm.notifications.length}条新消息`,
                position: 'bottom-right',
                onClick() {
                  vm.currentSystemTypeId = '4'
                  vm.getRouter(vm.currentSystemTypeId, {
                    path: '/customerServiceManage/messageManage/Index',
                    params: JSON.parse(data.jsonParam),
                  })
                },
              })
            } else {
              const notification = vm.$notify({
                message: data.content,
                position: 'bottom-right',
                onClick() {
                  const redirectUrl = notifications[data.type].value[data.module].redirectUrl
                  if (redirectUrl) {
                    vm.currentSystemTypeId = redirectUrl['systemTypeId']
                    if (data.module == 'NOTIFY_ALARM_SYSTEM') {
                      vm.markReadedStatus(data)
                      let jsonParam = JSON.parse(data.jsonParam)
                      if (jsonParam.customerTypeName == 'RESIDENT') {
                        vm.getRouter('21', {
                          path: '/householdLeakMonitor/inhabitantGasLeakEvent',
                          query: { eventId: JSON.parse(data.jsonParam).eventId },
                        })
                      } else {
                        vm.getRouter('21', {
                          path: '/householdLeakMonitor/businessGasLeakEvent',
                          query: { eventId: JSON.parse(data.jsonParam).eventId },
                        })
                      }
                    } else {
                      vm.getRouter(vm.currentSystemTypeId, {
                        path: redirectUrl['path'],
                        query: JSON.parse(data.jsonParam),
                      })
                    }
                    vm.$store.commit(
                      'account/setMessageCount',
                      vm.$store.state.account.messageCount - 1
                    )
                  }
                  notification.close()
                },
              })
            }
          })
          vm.$once('hook:beforeDestroy', () => {
            subscription.unsubscribe()
          })
        }
      })
    } else {
      this.$emit('ws-ready')
    }
  },
  methods: {
    //挂载websocket在vue上
    async mountWebsockt() {
      let vm = this
      try {
        const {
          data: { isSuccess, data },
        } = await realTimeMonitorApi.getWebsocketUrl()
        if (isSuccess) {
          const user = vm.$store.state.account.user
          const token = 'Bearer ' + vm.$store.state.account.token
          vm.$root.$websocket = new WebsocketService(
            `ws://${data.ip + ':' + data.port + data.path}?token=${token}&tenant=${
              user.tenantId
            }&account=${user.account}`
          )
        }
      } catch (error) {
        console.error('websocket创建失败')
      }
      this.$emit('ws-ready')
    },
    //标记已读
    async markReadedStatus(data) {
      const {
        data: { isSuccess },
      } = await messageManageApi.updateReadStatus({
        id: data.receiveId,
        isRead: true,
      })
    },
  },
}
</script>

R4TE%[Z1$~A7_@H}]ZY(YFB.png