实现IM系统与PC端一致的WebSocket连接,优化聊天功能,支持多端消息同步,更新联系人和会话列表接口,增强用户体验。

This commit is contained in:
geht
2026-07-21 15:53:06 +08:00
parent f24a21a41c
commit 7a9c19e4f3
171 changed files with 661 additions and 747 deletions

View File

@@ -1,5 +1,5 @@
// @ts-nocheck
import { randomString } from './uitls'
import md5 from 'md5'
import { useUserStore } from '@/store/user'
const baseUrl = import.meta.env.VITE_SERVER_BASEURL
@@ -8,66 +8,81 @@ class socket {
constructor() {
this.socketUrl = baseUrl
this.socketStart = false
this.socketType = ''
this.socketType = 'websocket'
this._heartbeatTimer = null
this.monitorSocketError()
this.monitorSocketClose()
this.socketReceive()
}
//update-begin---author:xsl ---date:2026-07-20 for【APP对接真实IM】系统 WebSocket 地址与 PC 一致-----------
/** 构建系统 WebSocket 地址:/websocket/{userId}_{md5(token)} */
buildSystemWebSocketUrl() {
const userStore = useUserStore()
const userId = userStore.userInfo.userid
const token = userStore.userInfo.token || ''
if (!userId || !this.socketUrl) {
return ''
}
const wsClientId = md5(token)
const base = this.socketUrl.replace('https://', 'wss://').replace('http://', 'ws://').replace(/\/$/, '')
return `${base}/websocket/${userId}_${wsClientId}`
}
//update-end---author:xsl ---date:2026-07-20 for【APP对接真实IM】系统 WebSocket 地址与 PC 一致-----------
init(socket_type, callback?) {
const userStore = useUserStore()
if (baseUrl) {
if (this.socketStart) {
console.log('webSocket已经启动了')
} else {
this.socketType = socket_type
let url =
this.socketUrl.replace('https://', 'wss://').replace('http://', 'ws://') +
'/' +
socket_type +
'/' +
userStore.userInfo.userid +
'_app'
if (socket_type == 'eoaNewChatSocket') {
let randomMessageId = randomString(6)
url =
this.socketUrl.replace('https://', 'wss://').replace('http://', 'ws://') +
'/eoaNewChatSocket/' +
userStore.userInfo.userid +
'/' +
randomMessageId
}
console.log('启动this.socketUrl连接地址', url)
// update-begin-author:taoyan date:20220422 for:v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
let token = userStore.userInfo.token
uni.connectSocket({
url: url,
method: 'GET',
protocols: [token],
})
// update-end-author:taoyan date:20220422 for: v2.4.6 的 websocket 服务端,存在性能和安全问题。 #3278
uni.onSocketOpen((res) => {
this.socketStart = true
callback && callback()
console.log('WebSocket连接已打开')
})
/* setTimeout(() => {
this.getHeartbeat();
}, 5000); */
}
} else {
if (!baseUrl) {
console.log('config/baseUrl socketUrl为空')
return
}
if (this.socketStart) {
console.log('webSocket已经启动了')
callback && callback()
return
}
this.socketType = socket_type || 'websocket'
//update-begin---author:xsl ---date:2026-07-20 for【APP对接真实IM】连接原生 /websocket-----------
let url = this.buildSystemWebSocketUrl()
if (this.socketType !== 'websocket') {
// 兼容旧路径(如历史业务仍传入自定义 type
url =
this.socketUrl.replace('https://', 'wss://').replace('http://', 'ws://') +
'/' +
this.socketType +
'/' +
userStore.userInfo.userid +
'_app'
}
//update-end---author:xsl ---date:2026-07-20 for【APP对接真实IM】连接原生 /websocket-----------
if (!url) {
console.log('WebSocket url 为空,跳过连接')
return
}
console.log('启动this.socketUrl连接地址', url)
const token = userStore.userInfo.token
uni.connectSocket({
url: url,
method: 'GET',
protocols: [token],
})
uni.onSocketOpen((res) => {
this.socketStart = true
callback && callback()
console.log('WebSocket连接已打开')
this.startHeartbeat()
})
}
// Socket给服务器发送消息
send(data, callback) {
const userStore = useUserStore()
if (userStore.userInfo.userid) {
if (userStore.userInfo.userid && typeof data === 'object' && data !== null) {
data.userUid = userStore.userInfo.userid
}
console.log(data)
const payload = typeof data === 'string' ? data : JSON.stringify(data)
uni.sendSocketMessage({
data: JSON.stringify(data),
data: payload,
success: () => {
callback && callback(true)
},
@@ -76,61 +91,76 @@ class socket {
},
})
}
acceptMessage(msg) {
console.log(msg)
}
// Socket接收服务器发送过来的消息
socketReceive() {
uni.onSocketMessage((res) => {
console.log('APP:--》收到服务器内容:')
let data = JSON.parse(res.data)
// console.log('收到服务器内容:', data);
//update-begin---author:xsl ---date:2026-07-20 for【APP对接真实IM】忽略心跳并解析 JSON-----------
if (res.data === 'ping' || res.data === 'pong') {
return
}
let data
try {
data = typeof res.data === 'string' ? JSON.parse(res.data) : res.data
} catch (e) {
console.log('WebSocket 非 JSON 消息:', res.data)
return
}
//update-end---author:xsl ---date:2026-07-20 for【APP对接真实IM】忽略心跳并解析 JSON-----------
this.acceptMessage && this.acceptMessage(data)
})
}
// 关闭Socket
closeSocket() {
this.stopHeartbeat()
uni.closeSocket()
this.socketStart = false
}
// 监听Socket关闭
monitorSocketClose() {
uni.onSocketClose((res) => {
console.log('WebSocket 已关闭!')
this.socketStart = false
this.stopHeartbeat()
setTimeout(() => {
this.init(this.socketType)
}, 3000)
})
}
// 监听Socket错误
monitorSocketError() {
uni.onSocketError(function (res) {
uni.onSocketError((res) => {
this.socketStart = false
console.log('WebSocket连接打开失败请检查')
})
}
// 心跳
getHeartbeat() {
const userStore = useUserStore()
this.send(
{
type: '心跳',
userUid: userStore.userInfo.userid,
},
(val) => {
setTimeout(() => {
if (val) {
// this.getHeartbeat();
} else {
if (!this.socketStart) {
// this.init();
}
}
}, 10000)
},
)
//update-begin---author:xsl ---date:2026-07-20 for【APP对接真实IM】心跳 ping-----------
startHeartbeat() {
this.stopHeartbeat()
this._heartbeatTimer = setInterval(() => {
if (!this.socketStart) {
return
}
this.send('ping')
}, 55000)
}
stopHeartbeat() {
if (this._heartbeatTimer) {
clearInterval(this._heartbeatTimer)
this._heartbeatTimer = null
}
}
//update-end---author:xsl ---date:2026-07-20 for【APP对接真实IM】心跳 ping-----------
}
const mySocket = new socket()
export default mySocket