jifuyun-order-v1/chat-server/app/chat.js

164 lines
5.9 KiB
JavaScript

const WebSocket = require('ws')
const { v4: uuidv4 } = require('uuid')
class ChatServer {
constructor(server) {
this.wss = new WebSocket.Server({ server })
this.clients = new Map() // 存储所有连接的客户端
this.messageHistory = [] // 存储消息历史
this.setupWebSocket()
this.setupHeartbeat()
}
setupWebSocket() {
this.wss.on('connection', (ws, req) => {
const clientId = uuidv4()
const userInfo = { id: clientId, name: '未命名用户' }
// 存储客户端连接
this.clients.set(clientId, { ws, userInfo, lastPing: Date.now() })
// 广播新用户加入
this.broadcast({
type: 'system',
action: 'join',
user: userInfo,
users: Array.from(this.clients.values()).map(client => client.userInfo),
timestamp: Date.now()
})
// 发送在线用户列表
ws.send(JSON.stringify({
type: 'system',
action: 'userList',
users: Array.from(this.clients.values()).map(client => client.userInfo),
timestamp: Date.now()
}))
// 发送历史消息
ws.send(JSON.stringify({
type: 'system',
action: 'history',
messages: this.messageHistory.slice(-50), // 最近50条消息
timestamp: Date.now()
}))
// 处理消息
ws.on('message', (data) => {
try {
const message = JSON.parse(data)
const client = this.clients.get(clientId)
if (client) {
client.lastPing = Date.now() // 更新最后活动时间
}
switch (message.type) {
case 'chat':
// 处理聊天消息
const chatMessage = {
id: uuidv4(),
type: 'chat',
from: userInfo,
to: message.to, // null表示群发
content: message.content,
timestamp: Date.now()
}
this.messageHistory.push(chatMessage)
if (message.to) {
// 私聊消息
const targetClient = this.clients.get(message.to.id)
if (targetClient) {
targetClient.ws.send(JSON.stringify(chatMessage))
ws.send(JSON.stringify(chatMessage)) // 发送给自己
}
} else {
// 群发消息
this.broadcast(chatMessage)
}
break
case 'user':
// 更新用户信息
if (message.action === 'update') {
userInfo.name = message.name
this.broadcast({
type: 'system',
action: 'userUpdate',
user: userInfo,
users: Array.from(this.clients.values()).map(client => client.userInfo),
timestamp: Date.now()
})
}
break
case 'ping':
// 处理心跳消息
ws.send(JSON.stringify({
type: 'pong',
timestamp: Date.now()
}))
break
}
} catch (error) {
console.error('处理消息时出错:', error)
}
})
// 处理连接关闭
ws.on('close', () => {
this.clients.delete(clientId)
this.broadcast({
type: 'system',
action: 'leave',
user: userInfo,
users: Array.from(this.clients.values()).map(client => client.userInfo),
timestamp: Date.now()
})
})
// 处理错误
ws.on('error', (error) => {
console.error('WebSocket错误:', error)
this.clients.delete(clientId)
})
})
}
// 设置心跳检测
setupHeartbeat() {
const HEARTBEAT_INTERVAL = 30000 // 30秒检查一次
const CLIENT_TIMEOUT = 60000 // 60秒超时
setInterval(() => {
const now = Date.now()
this.clients.forEach((client, clientId) => {
if (now - client.lastPing > CLIENT_TIMEOUT) {
console.log(`客户端 ${clientId} 超时断开`)
client.ws.terminate()
this.clients.delete(clientId)
}
})
}, HEARTBEAT_INTERVAL)
}
// 广播消息给所有客户端
broadcast(message) {
const messageStr = JSON.stringify(message)
this.clients.forEach(client => {
if (client.ws.readyState === WebSocket.OPEN) {
client.ws.send(messageStr)
}
})
}
// 清理历史消息
cleanHistory() {
const oneDay = 24 * 60 * 60 * 1000 // 一天的毫秒数
const now = Date.now()
this.messageHistory = this.messageHistory.filter(msg => {
return (now - msg.timestamp) < oneDay
})
}
}
module.exports = ChatServer