以下是我今天想要解决的问题
- 如何实现让聊天信息永久保存
- 展示聊过天的用户列表
- 区分当前聊天用户
如何实现让聊天信息永久保存
这里我使用了Mysql数据库,首先设计数据表
CREATE TABLE messages (
id INT AUTO_INCREMENT PRIMARY KEY,
sender_id INT NOT NULL,//发送者id
receiver_id INT NOT NULL,//接收者id
content TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
在node中的ws,当用户发送消息的时候必定会触发这个方法,所以当触发这个方法的时候,直接把信息添加进去即可,这里我额外写了一个功能就是未读与已读的消息,我觉得这个功能在这里写非常合适,因为ws的这一方法在发送的时候可以辨别当前要发送的用户是否在线,如果用户在线,则默认标记已读,未在线则标记未读。
wss.on('connection', (ws, req) => {
const userId = req.url.substring(1); // 假设用户ID通过URL传递,如 ws://example.com/userId
users[userId] = ws;
ws.on('message', (message) => {
console.log(`Received message from ${message}`);
console.log(JSON.parse(message).text);
// 添加信息接口
// 这里可以根据消息格式进行解析,找到接收方的ID
// 假设消息是一个JSON字符串,包含发送者和接收者ID以及实际消息内容
const msg = JSON.parse(message);
const recipientId = msg.to;
if (users[recipientId]) {
if (Buffer.isBuffer(message)) {
// 处理二进制数据
const text = message.toString('utf8');
console.log('Received binary data:', text);
users[recipientId].send(text);
} else {
console.log('Received message:', message);
// 处理文本消息
users[recipientId].send(message);
}
// 如果接收者在线,则直接发送消息
console.log('接收者在线');
const { messageId, to } = msg;
const sql = 'INSERT INTO user_message_status (message_id, user_id, read_at,is_read) VALUES (?, ?,NOW(),1)';
db.query(sql, [messageId+'',to+''], (err, results) => {
if (err) {
console.log(err.message);
}
})
} else {
// 如果接收者不在线,可以在这里处理离线消息逻辑
// ...
console.log('接收者不在线');
// 设置未读
const { messageId, to } = msg;
console.log(messageId,to);
const sql = 'INSERT INTO user_message_status (message_id, user_id, read_at) VALUES (?, ?,NOW())';
db.query(sql, [messageId+'',to+''], (err, results) => {
if (err) {
console.log(err.message);
}
})
}
});
那么接下来我们就需要在相应的聊天界面获取当前用户与聊天用户的信息了(注:加粗部分)。 以下是一部分用户与用户之前聊天的信息
- 数量量庞大,需要使用分页
- 根据时间来排序信息
- 根据发送者和接收者来获取数据(通过sql语句的or)
// 获取所有消息消息API
app.get('/get-messages/:user_id/:merchant_id', (req, res) => {
const user_id = req.params.user_id;
const merchant_id = req.params.merchant_id;
console.log(user_id,merchant_id);
// 获取查询参数
const page = parseInt(req.query.page) || 1; // 页码,默认为1
const limit = parseInt(req.query.limit) || 10; // 每页显示的消息数量,默认为10
// 计算跳过的记录数
const offset = (page - 1) * limit;
// 构建SQL查询语句
const sql = `
SELECT m.* FROM messages m
WHERE (m.sender_id = ? AND m.receiver_id = ?)
OR (m.sender_id = ? AND m.receiver_id = ?)
ORDER BY m.created_at DESC
LIMIT ? OFFSET ?
`;
// 执行查询
db.query(sql, [user_id, merchant_id, merchant_id, user_id, limit, offset], (err, results) => {
if (err) {
return res.status(500).json({ error: err.message });
}
res.status(200).json(results);
});
});
展示聊过天的用户列表
- 因为我们的需要是,获取发送过的,或者聊过天的用户,所以我们不会默认一上来就展示未聊过天的用户信息列表的。
- 所以我们只需要根据发送者id
- 这里会获取到数据,这里的数据只是聊天的数据,需要对这些数据进行去重,排序,获取当前信息的用户详细信息(头像,用户名等)。
app.get('/get-user-list/:sender_id',(req,res)=>{
const sender_id = req.params.sender_id;
console.log(sender_id);
// let sql = `SELECT * FROM messages where receiver_id = ?;`
let sql = 'SELECT * FROM messages WHERE sender_id = ? OR receiver_id = ?;';
db.query(sql,[sender_id,sender_id],async(err,result)=>{
// 使用 Map 对象去重,并保留 id 最大的记录
const uniqueData = new Map(
result.map(item => {
// 创建一个唯一的键,将 sender_id 和 receiver_id 组合起来
const key = `${item.sender_id}_${item.receiver_id}`;
return [key, item];
})
);
// 将 Map 的值转换为数组,并按 id 降序排序
const sortedUniqueData = Array.from(uniqueData.values())
.sort((a, b) => b.id - a.id);
// 创建一个 Map 来存储每个 sender_id 和 receiver_id 组合的最新消息
const latestMessages = new Map();
sortedUniqueData.forEach(item => {
// 为了去重,我们创建一个组合键,它将 sender_id 和 receiver_id 组合在一起
// 同时考虑相反的顺序,以便将它们视为相同的对话
const key = Math.min(item.sender_id, item.receiver_id) + '_' + Math.max(item.sender_id, item.receiver_id);
// 如果 Map 中没有这个键,或者当前项的 id 比 Map 中存储的 id 大,则更新 Map
if (!latestMessages.has(key) || (latestMessages.get(key) && item.id > latestMessages.get(key).id)) {
latestMessages.set(key, item);
}
});
// 将 Map 的值转换为数组
const newData = Array.from(latestMessages.values());
newData.forEach(async (item)=>{
let sql = `select * from user where phone = ?`
if(item.receiver_id.length===11){
await db.query(sql,item.receiver_id,(err,result)=>{
item.userMessage=result[0]
})
}else{
await db.query(sql,item.sender_id,(err,result)=>{
item.userMessage=result[0]
})
}
})
setTimeout(() => {
console.log(newData);
res.send({message:newData})
}, 1000);
})
})
区分当前聊天用户
在onmessage方法中,我们会获取到发送者的信息,我们根据发送者的id和我们当前聊天用户的id来判断,是否是我们当前聊天的用户。
webSocketTask.value.onMessage((message) => {
console.log(message);
let data = JSON.parse(message.data)
// 判断当前聊天的商家是否一致
if(data.sendId==merChantId._value){
messages.push({isMine:false,content:data.text})
}
// const textMessage = JSON.parse(message.data);
// messages.value.push({ id: messages.value.length, text: textMessage.text });
});