Files
cashier_app/store/chat.js

289 lines
8.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineStore } from "pinia";
// #ifdef H5
const socketUrl = "http://192.168.1.42:2348";
// #endif
// #ifndef H5
const socketUrl = "ws://192.168.1.42:2348";
// #endif
// 聊天
export const useChatStore = defineStore("chat", {
state: () => {
return {
socketUrl,
isConnect: false,
socketTask: null,
onReceiveMsg: () => {},
chatList: [],
// ========== 新增Socket 保活与重连状态 ==========
isManualClose: false, // 是否主动关闭true不重连false自动重连
heartbeatTimer: null, // 心跳定时器
reconnectTimer: null, // 重连定时器
reconnectCount: 0, // 当前重连次数
maxReconnectCount: 5, // 最大重连次数(避免无限重连)
reconnectDelay: 1000, // 初始重连延迟ms
maxReconnectDelay: 8000, // 最大重连延迟ms
heartbeatInterval: 30000, // 心跳间隔30s根据服务器调整
};
},
actions: {
init() {
if (!this.isConnect) {
return uni.showToast({
title: "请先连接socket",
icon: "none",
});
}
this.sendMessage(
{
type: "OnbocChat",
operate_type: "init",
shop_id: uni.getStorageSync("shopId"),
token: uni.getStorageSync("iToken").tokenValue || "",
},
false
);
},
sendMessage(msg, isAutoAppend = true) {
// 1. 主动关闭状态:提示用户
if (this.isManualClose) {
return uni.showToast({
title: "Socket已主动关闭请重新连接",
icon: "none",
});
}
// 2. 连接未建立:尝试重连后发送
if (!this.isConnect) {
this.reconnect();
// 延迟发送,确保重连成功
setTimeout(() => {
this.sendMessage(msg, isAutoAppend);
}, this.reconnectDelay);
return;
}
// 3. 正常发送消息
console.log('发送的消息', msg);
const message = isAutoAppend
? {
type: "OnbocChat",
operate_type: "sendMsg",
shop_id: uni.getStorageSync("shopId"),
token: uni.getStorageSync("iToken").tokenValue || "",
...msg,
}
: msg;
this.socketTask.send({
data: JSON.stringify(message),
success: (res) => {
console.log("发送成功", res);
},
fail: (error) => {
console.log("发送失败", error);
// 发送失败可能是连接断开,触发重连
if (!this.isManualClose) {
this.reconnect();
}
},
});
},
// ========== 新增:发送心跳包 ==========
sendHeartbeat() {
if (!this.isConnect || this.isManualClose) return;
this.socketTask.send({
data: JSON.stringify({
type: "OnbocChat",
operate_type: "heartbeat", // 心跳类型(需与服务器约定)
shop_id: uni.getStorageSync("shopId"),
token: uni.getStorageSync("iToken").tokenValue || "",
timestamp: Date.now(), // 时间戳(可选,用于服务器校验)
}),
fail: (error) => {
console.log("心跳发送失败,触发重连", error);
this.reconnect();
},
});
},
// ========== 新增:启动心跳定时器 ==========
startHeartbeat() {
// 清除旧定时器,避免重复
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
}
// 每30s发送一次心跳
this.heartbeatTimer = setInterval(() => {
this.sendHeartbeat();
}, this.heartbeatInterval);
},
// ========== 新增:停止心跳定时器 ==========
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
this.heartbeatTimer = null;
}
},
// ========== 新增:智能重连逻辑 ==========
reconnect() {
// 1. 主动关闭或已连接:不重连
if (this.isManualClose || this.isConnect) return;
// 2. 超过最大重连次数:停止重连
if (this.reconnectCount >= this.maxReconnectCount) {
console.log("已达最大重连次数,停止重连");
return;
}
// 3. 清除旧重连定时器
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
}
// 4. 计算重连延迟退避算法1s → 2s → 4s → 8s → 8s...
const delay = Math.min(
this.reconnectDelay * Math.pow(2, this.reconnectCount),
this.maxReconnectDelay
);
console.log(`${this.reconnectCount + 1}次重连,延迟${delay}ms`);
// 5. 延迟执行重连
this.reconnectTimer = setTimeout(() => {
this.connectSocket();
this.reconnectCount++;
}, delay);
},
// ========== 优化连接Socket ==========
connectSocket() {
// 1. 避免重复连接
if (this.isConnect || this.socketTask) {
return;
}
// 2. 重置主动关闭标记
this.isManualClose = false;
// 3. 创建Socket连接
this.socketTask = uni.connectSocket({
url: this.socketUrl,
success: (res) => {
console.log("Socket连接请求发送成功");
},
fail: (res) => {
console.log("Socket连接请求失败", res);
// 请求失败,触发重连
this.reconnect();
},
});
// 4. 连接成功回调
this.socketTask.onOpen((res) => {
console.log("Socket连接成功");
this.isConnect = true;
this.reconnectCount = 0; // 重置重连次数
this.init(); // 初始化聊天
this.startHeartbeat(); // 启动心跳
});
// 5. 接收消息回调(添加容错处理)
this.socketTask.onMessage((res) => {
try {
const data = JSON.parse(res.data);
console.log("收到服务器消息", data);
if (data.msg) {
uni.showToast({
title: data.msg,
icon: "none",
});
}
// 处理发送消息
if (data?.operate_type === "sendMsg") {
this.chatList.unshift(data.data);
this.onReceiveMsg(data.data);
}
// 处理接收消息
if (data?.operate_type === "receive_msg") {
const msg = {
...data.data,
operate_type: "receive_msg",
};
this.chatList.unshift(msg);
this.onReceiveMsg(msg);
}
// 处理心跳响应(如果服务器返回)
if (data?.operate_type === "heartbeat_ack") {
console.log("心跳响应正常");
}
} catch (error) {
console.error("消息解析失败", error);
}
});
// 6. 连接错误回调
this.socketTask.onError((res) => {
console.log("Socket连接错误", res);
this.isConnect = false;
// 错误触发重连
this.reconnect();
});
// 7. 连接关闭回调(区分主动/意外关闭)
this.socketTask.onClose(() => {
console.log("Socket连接已关闭");
this.isConnect = false;
this.stopHeartbeat(); // 停止心跳
// 只有非主动关闭时才重连
if (!this.isManualClose) {
console.log("意外断开,触发重连");
this.reconnect();
} else {
console.log("主动关闭,不重连");
}
});
},
// ========== 优化主动关闭Socket ==========
closeSocket() {
// 1. 设置主动关闭标记(关键:避免自动重连)
this.isManualClose = true;
// 2. 停止所有定时器,清理资源
this.stopHeartbeat();
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
this.reconnectTimer = null;
}
// 3. 关闭Socket连接
if (this.socketTask) {
this.socketTask.close();
this.socketTask = null; // 释放引用
}
// 4. 重置连接状态
this.isConnect = false;
this.reconnectCount = 0; // 重置重连次数
console.log("Socket已主动关闭");
},
},
unistorage: false, // 开启后对 state 的数据读写都将持久化
});