问题修复

This commit is contained in:
2025-12-05 11:10:50 +08:00
parent 885ef57c93
commit a4473cf0e5
3 changed files with 307 additions and 85 deletions

View File

@@ -174,7 +174,7 @@
import {
onReady,
onReachBottom,
onLoad,
onLoad,onShow,
onPageScroll,
} from "@dcloudio/uni-app";
import {
@@ -243,7 +243,6 @@ chatStore.onReceiveMsg = (msg) => {
scrollView.intoView = "msg-0";
});
};
chatStore.connectSocket();
const msg = ref("");
const shopInfo = uni.getStorageSync("shopInfo");
@@ -477,6 +476,18 @@ onLoad((opt) => {
// #endif
});
onMounted(() => {
chatStore.connectSocket();
// 确保状态监听已初始化
if (!chatStore._listenersInitialized) {
chatStore.initStateListeners();
chatStore._listenersInitialized = true;
}
});
onShow(() => {
});
function toMore() {
go.to("PAGES_CHAT_GROUP_INFO", {
group_id: groupInfo.value.id,

View File

@@ -56,24 +56,36 @@
<script setup>
import * as chatApi from "@/http/php/chat";
import { ref, reactive,inject } from "vue";
import { ref, reactive, inject, onMounted,onUnmounted } from "vue";
import { onShow } from "@dcloudio/uni-app";
import { useChatStore } from "@/store/chat";
const chatStore = useChatStore();
onMounted(() => {
chatStore.connectSocket();
// 确保状态监听已初始化
if (!chatStore._listenersInitialized) {
chatStore.initStateListeners();
chatStore._listenersInitialized = true;
}
});
const startReciveMsg=ref(true)
onUnmounted(() => {
startReciveMsg.value=false
})
chatStore.onReceiveMsg = (msg) => {
console.log("onReceiveMsg", msg);
if(!startReciveMsg.value){
return
}
if (msg.operate_type == "receive_msg" && msg.chat_type == 2) {
const index = allList.value.findIndex((v) => v.group_id == msg.group_id);
if (index != -1) {
allList.value[index].unread_count += 1;
allList.value[index].msg = returnMsg(msg);
allList.value[index].send_time = msg.send_time;
}
const index1 = list.value.findIndex((v) => v.group_id == msg.group_id);
if (index1 != -1) {
@@ -83,7 +95,6 @@ chatStore.onReceiveMsg = (msg) => {
}
}
};
function returnMsg(msg) {
if (msg.msg_type == 1) {
return msg.nick + "" + msg.content;
@@ -213,6 +224,7 @@ function toDetail(item) {
const messageUnreadCount = ref(0);
onShow(() => {
startReciveMsg.value=true
getList();
// 获取未读消息总数
chatApi.messageUnreadCount({}).then((res) => {

View File

@@ -17,7 +17,6 @@ export const useChatStore = defineStore("chat", {
onReceiveMsg: () => {},
chatList: [],
// ========== 新增Socket 保活与重连状态 ==========
isManualClose: false, // 是否主动关闭true不重连false自动重连
heartbeatTimer: null, // 心跳定时器
reconnectTimer: null, // 重连定时器
@@ -26,10 +25,98 @@ export const useChatStore = defineStore("chat", {
reconnectDelay: 1000, // 初始重连延迟ms
maxReconnectDelay: 8000, // 最大重连延迟ms
heartbeatInterval: 30000, // 心跳间隔30s根据服务器调整
isAppActive: true, // 应用是否在前台
lastHeartbeatTime: 0, // 上次心跳时间戳
// ========== 新增:修复未定义的状态 ==========
_listenersInitialized: false, // 状态监听器是否已初始化
// ========== 新增:连接状态管理 ==========
isConnecting: false, // 是否正在建立连接防止重复调用connectSocket
// ========== 新增:消息去重机制 ==========
recentMessages: new Map(), // 最近处理的消息映射表key=消息特征哈希value=处理时间戳
deduplicationWindow: 5000, // 去重时间窗口5秒内相同消息只处理一次
maxRecentMessages: 100, // 最近消息缓存最大数量
};
},
actions: {
// ========== 初始化状态监听 ==========
initStateListeners() {
// 避免重复初始化
if (this._listenersInitialized) return;
console.log("初始化状态监听器");
// 监听应用前后台切换
uni.onAppShow(() => {
console.log("应用切前台检查Socket连接");
this.isAppActive = true;
// 应用切前台,强制重连(无论当前状态如何)
this.forceReconnect();
});
uni.onAppHide(() => {
console.log("应用切后台");
this.isAppActive = false;
// 后台可选择暂停心跳(根据实际需求调整)
// this.stopHeartbeat();
});
// 监听网络状态变化
uni.onNetworkStatusChange((res) => {
console.log("网络状态变化", res);
if (res.isConnected) {
// 网络恢复,自动重连
console.log("网络恢复,触发重连");
this.forceReconnect();
}
});
this._listenersInitialized = true;
},
// ========== 强制重连方法 ==========
forceReconnect() {
// 主动关闭状态下不重连
if (this.isManualClose) return;
console.log("执行强制重连");
// 1. 清理现有连接资源
this.cleanupSocket();
// 2. 重置重连状态
this.reconnectCount = 0;
this.stopHeartbeat();
// 3. 立即重新连接
setTimeout(() => {
this.connectSocket();
}, 100);
},
// ========== 新增清理Socket资源 ==========
cleanupSocket() {
if (this.socketTask) {
try {
this.socketTask.close({
code: 1000, // 正常关闭
reason: 'force reconnect'
});
} catch (error) {
console.error("关闭Socket失败", error);
} finally {
// 强制重置状态
this.socketTask = null;
this.isConnect = false;
this.isConnecting = false;
}
}
},
init() {
if (!this.isConnect) {
return uni.showToast({
@@ -68,7 +155,7 @@ export const useChatStore = defineStore("chat", {
}
// 3. 正常发送消息
console.log('发送的消息', msg);
console.log("发送的消息", msg);
const message = isAutoAppend
? {
type: "OnbocChat",
@@ -94,17 +181,18 @@ export const useChatStore = defineStore("chat", {
});
},
// ========== 新增:发送心跳包 ==========
// ========== 发送心跳包 ==========
sendHeartbeat() {
// 应用在后台时可调整心跳策略(如降低频率)
if (!this.isConnect || this.isManualClose) return;
const now = Date.now();
// 记录心跳时间
this.lastHeartbeatTime = now;
this.socketTask.send({
data: JSON.stringify({
type: "OnbocChat",
operate_type: "heartbeat", // 心跳类型(需与服务器约定)
shop_id: uni.getStorageSync("shopId"),
token: uni.getStorageSync("iToken").tokenValue || "",
timestamp: Date.now(), // 时间戳(可选,用于服务器校验)
type: "ping_interval",
}),
fail: (error) => {
console.log("心跳发送失败,触发重连", error);
@@ -113,20 +201,23 @@ export const useChatStore = defineStore("chat", {
});
},
// ========== 新增:启动心跳定时器 ==========
// ========== 启动心跳定时器 ==========
startHeartbeat() {
// 清除旧定时器,避免重复
// 清除旧定时器
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
}
// 每30s发送一次心跳
// 立即发送一次心跳,确保连接活跃
this.sendHeartbeat();
// 启动心跳定时器间隔15s
this.heartbeatTimer = setInterval(() => {
this.sendHeartbeat();
}, this.heartbeatInterval);
},
// ========== 新增:停止心跳定时器 ==========
// ========== 停止心跳定时器 ==========
stopHeartbeat() {
if (this.heartbeatTimer) {
clearInterval(this.heartbeatTimer);
@@ -134,23 +225,29 @@ export const useChatStore = defineStore("chat", {
}
},
// ========== 新增:智能重连逻辑 ==========
// ========== 智能重连逻辑 ==========
reconnect() {
// 1. 主动关闭或已连接:不重连
if (this.isManualClose || this.isConnect) return;
// 2. 超过最大重连次数:停止重连
// 2. 正在连接中:不重连
if (this.isConnecting) {
console.log("正在建立连接中,跳过重连");
return;
}
// 3. 超过最大重连次数:停止重连
if (this.reconnectCount >= this.maxReconnectCount) {
console.log("已达最大重连次数,停止重连");
return;
}
// 3. 清除旧重连定时器
// 4. 清除旧重连定时器
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer);
}
// 4. 计算重连延迟退避算法1s → 2s → 4s → 8s → 8s...
// 5. 计算重连延迟退避算法1s → 2s → 4s → 8s → 8s...
const delay = Math.min(
this.reconnectDelay * Math.pow(2, this.reconnectCount),
this.maxReconnectDelay
@@ -158,7 +255,7 @@ export const useChatStore = defineStore("chat", {
console.log(`${this.reconnectCount + 1}次重连,延迟${delay}ms`);
// 5. 延迟执行重连
// 6. 延迟执行重连
this.reconnectTimer = setTimeout(() => {
this.connectSocket();
this.reconnectCount++;
@@ -167,15 +264,25 @@ export const useChatStore = defineStore("chat", {
// ========== 优化连接Socket ==========
connectSocket() {
// 1. 避免重复连接
if (this.isConnect || this.socketTask) {
// 1. 严格检查:避免重复连接
if (this.isConnect || this.isConnecting || this.isManualClose) {
console.warn("跳过连接:当前状态不允许建立新连接", {
isConnect: this.isConnect,
isConnecting: this.isConnecting,
isManualClose: this.isManualClose
});
return;
}
// 2. 重置主动关闭标记
// 2. 设置连接中状态
this.isConnecting = true;
// 3. 重置主动关闭标记
this.isManualClose = false;
// 3. 创建Socket连接
console.log("开始创建新的Socket连接");
// 4. 创建Socket连接
this.socketTask = uni.connectSocket({
url: this.socketUrl,
success: (res) => {
@@ -183,37 +290,45 @@ export const useChatStore = defineStore("chat", {
},
fail: (res) => {
console.log("Socket连接请求失败", res);
// 请求失败,触发重连
// 请求失败,重置状态
this.isConnecting = false;
// 触发重连
this.reconnect();
},
});
// 4. 连接成功回调
// 5. 连接成功回调
this.socketTask.onOpen((res) => {
console.log("Socket连接成功");
// 重置连接状态
this.isConnect = true;
this.reconnectCount = 0; // 重置重连次数
this.init(); // 初始化聊天
this.startHeartbeat(); // 启动心跳
this.isConnecting = false;
this.reconnectCount = 0;
this.init();
// 缩短心跳间隔到15s更频繁的心跳维持连接活跃
this.heartbeatInterval = 15000;
this.startHeartbeat();
// 初始化状态监听(仅在首次连接时执行)
if (!this._listenersInitialized) {
this.initStateListeners();
this._listenersInitialized = true;
}
});
// 5. 接收消息回调(添加容错处理)
// 6. 接收消息回调(添加容错处理和消息去重
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);
this.processMessage({
...data.data,
direction: 'send' // 添加消息方向标识
});
}
// 处理接收消息
@@ -221,9 +336,9 @@ export const useChatStore = defineStore("chat", {
const msg = {
...data.data,
operate_type: "receive_msg",
direction: 'receive' // 添加消息方向标识
};
this.chatList.unshift(msg);
this.onReceiveMsg(msg);
this.processMessage(msg);
}
// 处理心跳响应(如果服务器返回)
@@ -235,18 +350,22 @@ export const useChatStore = defineStore("chat", {
}
});
// 6. 连接错误回调
// 7. 连接错误回调
this.socketTask.onError((res) => {
console.log("Socket连接错误", res);
// 重置连接状态
this.isConnect = false;
this.isConnecting = false;
// 错误触发重连
this.reconnect();
});
// 7. 连接关闭回调(区分主动/意外关闭)
// 8. 连接关闭回调(区分主动/意外关闭)
this.socketTask.onClose(() => {
console.log("Socket连接已关闭");
// 重置连接状态
this.isConnect = false;
this.isConnecting = false;
this.stopHeartbeat(); // 停止心跳
// 只有非主动关闭时才重连
@@ -259,6 +378,89 @@ export const useChatStore = defineStore("chat", {
});
},
// ========== 新增:消息处理与去重 ==========
processMessage(msg) {
// 1. 生成消息特征哈希
const msgHash = this.generateMessageHash(msg);
const now = Date.now();
// 2. 清理过期消息
this.cleanupExpiredMessages(now);
// 3. 去重逻辑:只对接收的消息应用严格去重
if (msg.direction === 'receive') {
// 接收消息5秒内相同特征的消息只处理一次
const lastProcessTime = this.recentMessages.get(msgHash);
if (lastProcessTime && (now - lastProcessTime) < this.deduplicationWindow) {
console.log("重复接收消息,已忽略(特征哈希:", msgHash, "");
return; // 重复消息,直接返回
}
// 记录接收消息的处理时间
this.recentMessages.set(msgHash, now);
} else {
// 发送消息:直接通过,不做严格去重
console.log("发送消息,直接处理(特征哈希:", msgHash, "");
}
// 4. 限制最近消息数量,避免内存泄漏
if (this.recentMessages.size > this.maxRecentMessages) {
// 移除最早的消息
const firstKey = this.recentMessages.keys().next().value;
this.recentMessages.delete(firstKey);
}
// 5. 正常处理消息
this.chatList.unshift(msg);
this.onReceiveMsg(msg);
},
// ========== 新增:生成消息特征哈希 ==========
generateMessageHash(msg) {
// 基于消息的核心字段生成哈希,确保相同内容的消息哈希一致
const {
content, // 消息内容
msg_type, // 消息类型
timestamp, // 时间戳
operate_type, // 操作类型
from_user_id, // 发送者ID如果有
to_user_id, // 接收者ID如果有
image_url, // 图片URL如果有
direction // 消息方向
} = msg;
// 组合核心字段,生成唯一字符串
// 发送消息添加随机数,确保每次发送的消息哈希唯一
const msgFeatures = JSON.stringify({
content: content || '',
msg_type: msg_type || '',
operate_type: operate_type || '',
from_user_id: from_user_id || '',
to_user_id: to_user_id || '',
image_url: image_url || '',
direction: direction || '',
// 发送消息添加随机数,确保每次发送的消息哈希唯一
random: direction === 'send' ? Math.random().toString(36).substr(2, 9) : ''
});
// 使用简单的哈希算法生成32位哈希值
let hash = 0;
for (let i = 0; i < msgFeatures.length; i++) {
const char = msgFeatures.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // 转换为32位整数
}
return Math.abs(hash).toString(16); // 转换为16进制字符串
},
// ========== 新增:清理过期消息 ==========
cleanupExpiredMessages(currentTime) {
for (const [hash, time] of this.recentMessages.entries()) {
if (currentTime - time > this.deduplicationWindow) {
this.recentMessages.delete(hash);
}
}
},
// ========== 优化主动关闭Socket ==========
closeSocket() {
// 1. 设置主动关闭标记(关键:避免自动重连)
@@ -271,11 +473,8 @@ export const useChatStore = defineStore("chat", {
this.reconnectTimer = null;
}
// 3. 关闭Socket连接
if (this.socketTask) {
this.socketTask.close();
this.socketTask = null; // 释放引用
}
// 3. 清理Socket资源
this.cleanupSocket();
// 4. 重置连接状态
this.isConnect = false;