import { defineStore } from "pinia" import { ref, computed } from "vue" import { chatDB } from "@/utils/db" import { useWebRTC } from "@/composables/useWebRTC" import { useUserStore } from "@/stores/user" import { sendMessage } from "@/utils/request.js" export const useChatStore = defineStore("chat", () => { const friends = ref([]) const currentFriend = ref(null) const messages = ref([]) const connectionStatus = ref("disconnected") const socket = ref(null) // 通话相关状态 const currentCall = ref(null) const incomingCall = ref(null) const callStatus = ref("idle") // idle, calling, ringing, ongoing, ended const callError = ref(null) // 错误状态:'rejected', 'no-answer', 'busy' const callConnectionStatus = ref("") // 连接状态:等待接听、正在连接等 const webrtc = useWebRTC() const userStore = useUserStore() // 计算属性 const isConnected = computed(() => connectionStatus.value === "connected") const isInCall = computed(() => callStatus.value !== "idle") const isCalling = computed(() => callStatus.value === "calling") const isRinging = computed(() => callStatus.value === "ringing") const isOngoingCall = computed(() => callStatus.value === "ongoing") // 初始化数据库 const initDB = async () => { try { await chatDB.init() console.log("数据库初始化成功") } catch (error) { console.error("数据库初始化失败:", error) } } // 加载好友列表 const loadFriends = async (presetUsers, currentUserId) => { try { // 从数据库获取好友列表 let friendList = await chatDB.getFriends(currentUserId) // 如果数据库中没有好友,使用预设用户初始化 if (friendList.length === 0) { friendList = presetUsers.filter((user) => user.id !== currentUserId) // 保存到数据库 for (const friend of friendList) { await chatDB.saveFriend( { ...friend, lastMessage: "", unreadCount: 0, }, currentUserId ) } } friends.value = friendList } catch (error) { console.error("加载好友列表失败:", error) // 降级到预设用户 const friendList = presetUsers.filter((user) => user.id !== currentUserId) friendList.forEach((friend) => { friend.lastMessage = "" friend.unreadCount = 0 }) friends.value = friendList } } // 设置当前好友 const setCurrentFriend = (friend) => { currentFriend.value = friend // 清除未读消息计数 if (friend) { friend.unreadCount = 0 } } // 切换当前聊天好友 const switchFriend = async (friend, currentUserId) => { currentFriend.value = friend // 清空未读消息计数 friend.unreadCount = 0 await chatDB.clearUnreadCount(friend.id) // 加载聊天记录 try { const chatHistory = await chatDB.getChatHistory(currentUserId, friend.id) messages.value = chatHistory.sort((a, b) => a.timestamp - b.timestamp) } catch (error) { console.error("加载聊天记录失败:", error) messages.value = [] } } // 添加消息 const addMessage = async (message, currentUserId) => { messages.value.push(message) if (currentFriend.value) { try { // 保存到数据库 await chatDB.saveMessage(message, currentUserId, currentFriend.value.id) // 更新好友列表中的最后消息 const friend = friends.value.find((f) => f.id === currentFriend.value.id) if (friend) { let lastMessage = message.content if (message.type === "image") lastMessage = "[图片]" if (message.type === "video") lastMessage = "[视频]" if (message.type === "audio") lastMessage = "[语音]" friend.lastMessage = lastMessage.length > 20 ? lastMessage.substring(0, 20) + "..." : lastMessage // 更新数据库中的好友信息 await chatDB.updateFriendLastMessage(friend.id, currentUserId, friend.lastMessage, 0) } } catch (error) { console.error("保存消息失败:", error) } } } // 处理接收到的消息 const handleIncomingMessage = async (messageData, currentUserId) => { // 添加来源检查 - 防止循环 if (messageData.isFromHttp) { console.log("忽略来自HTTP的消息,避免循环") return } const senderId = messageData.sender_user_id const receiverId = messageData.receiver_user_id if (receiverId !== currentUserId) return // 如果是通话信令,直接处理 if (messageData.call_id || messageData.call_status) { handleCallSignal(messageData) return } const newMessage = { type: getMessageType(messageData.message_type), content: messageData.content, time: new Date().toLocaleTimeString().slice(0, 5), senderId: senderId, read: false, duration: messageData.duration || 0, timestamp: Date.now(), } try { // 保存消息到数据库 await chatDB.saveMessage(newMessage, currentUserId, senderId) // 如果消息来自当前聊天用户,直接显示 if (currentFriend.value && senderId == currentFriend.value.id) { newMessage.read = true messages.value.push(newMessage) } else { // 更新未读消息计数 const friend = friends.value.find((f) => f.id == senderId) if (friend) { friend.unreadCount = (friend.unreadCount || 0) + 1 // 更新最后消息 let lastMessage = newMessage.content if (newMessage.type === "image") lastMessage = "[图片]" if (newMessage.type === "video") lastMessage = "[视频]" if (newMessage.type === "audio") lastMessage = "[语音]" friend.lastMessage = lastMessage // 更新数据库 await chatDB.updateFriendLastMessage(friend.id, currentUserId, lastMessage, friend.unreadCount) } } } catch (error) { console.error("处理接收消息失败:", error) } } // 根据消息类型编号获取类型字符串 const getMessageType = (type) => { const types = [ "text", "image", "audio", "video", "prescription", "medical-record", "video-call", "audio-call", "file" ] return types[type] || "text" } // 处理通话信令 const handleCallSignal = (data) => { console.log("处理通话信令:", data) // 根据通话状态处理 switch (data.call_status) { case "invite": handleIncomingCall(data) break case "accepted": handleCallAccepted(data) break case "rejected": handleCallRejected(data) break case "ended": handleCallEnded(data) break case "candidate": handleIceCandidate(data) break case "no-answer": // 添加无人接听状态 handleCallNoAnswer(data) break case "connecting": // 新增连接状态 callConnectionStatus.value = "正在连接..." break case "busy": // 对方忙线中 handleCallBusy(data) break } } // 处理来电 const handleIncomingCall = (data) => { // 如果自己正在通话中,自动拒绝并发送忙线状态 if (isInCall.value) { sendMessage({ senderId: userStore.currentUser?.id, receiverId: data.sender_user_id, type: data.message_type === 6 ? "video-call" : "audio-call", callId: data.call_id, callStatus: "busy" }) // 添加未接来电消息 const friend = friends.value.find(f => f.id === data.sender_user_id) if (friend) { friend.lastMessage = "[未接来电]" chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, "[未接来电]", friend.unreadCount) } return } incomingCall.value = { callId: data.call_id, callerId: data.sender_user_id, callType: data.message_type === 6 ? "video" : "audio", content: data.content } callStatus.value = "ringing" callError.value = null callConnectionStatus.value = "收到来电" console.log("收到来电:", incomingCall.value) } // 处理通话接受 const handleCallAccepted = (data) => { if (currentCall.value && currentCall.value.callId === data.call_id) { callStatus.value = "ongoing" callError.value = null callConnectionStatus.value = "通话中" console.log("通话已被接受") } } // 处理通话拒绝 const handleCallRejected = (data) => { if (currentCall.value && currentCall.value.callId === data.call_id) { callStatus.value = "ended" callError.value = "rejected" callConnectionStatus.value = "对方已拒绝" currentCall.value = null console.log("通话已被拒绝") // 5秒后清除状态 setTimeout(() => { callStatus.value = "idle" callError.value = null callConnectionStatus.value = "" }, 5000) } } // 处理通话结束 const handleCallEnded = (data) => { if ((currentCall.value && currentCall.value.callId === data.call_id) || (incomingCall.value && incomingCall.value.callId === data.call_id)) { callStatus.value = "ended" callError.value = null callConnectionStatus.value = "通话已结束" currentCall.value = null incomingCall.value = null console.log("通话已结束") // 5秒后清除状态 setTimeout(() => { callStatus.value = "idle" callConnectionStatus.value = "" }, 5000) } } // 处理ICE候选 const handleIceCandidate = (data) => { if (currentCall.value && currentCall.value.callId === data.call_id) { console.log("收到ICE候选:", data.content) // 这里可以添加WebRTC处理逻辑 } } // 处理无人接听 const handleCallNoAnswer = (data) => { if (currentCall.value && currentCall.value.callId === data.call_id) { callStatus.value = "ended" callError.value = "no-answer" callConnectionStatus.value = "对方无人接听" currentCall.value = null console.log("对方无人接听") // 5秒后清除状态 setTimeout(() => { callStatus.value = "idle" callError.value = null callConnectionStatus.value = "" }, 5000) } } // 处理对方忙线 const handleCallBusy = (data) => { if (currentCall.value && currentCall.value.callId === data.call_id) { callStatus.value = "ended" callError.value = "busy" callConnectionStatus.value = "对方忙线中" currentCall.value = null console.log("对方忙线中") // 5秒后清除状态 setTimeout(() => { callStatus.value = "idle" callError.value = null callConnectionStatus.value = "" }, 5000) } } // 发起通话 const startCall = (peerId, type) => { if (isInCall.value) { alert('您正在通话中,请先结束当前通话。') return } const friend = friends.value.find(f => f.id === peerId) if (!friend) { alert('好友信息无效') return } const callId = Date.now().toString() + Math.random().toString(36).substr(2, 9) currentCall.value = { callId: callId, peerId: peerId, callType: type, startTime: Date.now(), status: 'calling' } callStatus.value = "calling" callError.value = null callConnectionStatus.value = "等待对方接听..." // 设置30秒超时(无人接听) const timeoutId = setTimeout(() => { if (callStatus.value === "calling") { callStatus.value = "ended" callError.value = "no-answer" callConnectionStatus.value = "对方无人接听" console.log("呼叫超时,对方无人接听") // 发送无人接听信号 sendMessage({ senderId: userStore.currentUser?.id, receiverId: peerId, type: type === "video" ? "video-call" : "audio-call", callId: callId, callStatus: "no-answer" }) // 更新好友最后消息为未接来电 friend.lastMessage = "[未接来电]" chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, "[未接来电]", friend.unreadCount) // 5秒后清除状态 setTimeout(() => { callStatus.value = "idle" currentCall.value = null callConnectionStatus.value = "" }, 5000) } }, 30000) currentCall.value.timeoutId = timeoutId // 发送通话请求 sendMessage({ senderId: userStore.currentUser?.id, receiverId: peerId, type: type === "video" ? "video-call" : "audio-call", callId: callId, callStatus: "invite" }) console.log("发起通话:", currentCall.value) } // 接听来电 const acceptCall = () => { if (!incomingCall.value) return // 清除可能存在的超时计时器 if (currentCall.value?.timeoutId) { clearTimeout(currentCall.value.timeoutId) } // 发送接受信号 sendMessage({ senderId: userStore.currentUser?.id, receiverId: incomingCall.value.callerId, type: incomingCall.value.callType === "video" ? "video-call" : "audio-call", callId: incomingCall.value.callId, callStatus: "accepted" }) currentCall.value = { callId: incomingCall.value.callId, peerId: incomingCall.value.callerId, callType: incomingCall.value.callType, startTime: Date.now(), status: 'ongoing' } callStatus.value = "ongoing" callConnectionStatus.value = "通话中" incomingCall.value = null callError.value = null console.log("接听通话") } // 拒绝来电 const rejectCall = () => { if (!incomingCall.value) return // 发送拒绝信号 sendMessage({ senderId: userStore.currentUser?.id, receiverId: incomingCall.value.callerId, type: incomingCall.value.callType === "video" ? "video-call" : "audio-call", callId: incomingCall.value.callId, callStatus: "rejected" }) callStatus.value = "ended" callConnectionStatus.value = "已拒绝" callError.value = "rejected" // 更新好友最后消息为已拒绝 const friend = friends.value.find(f => f.id === incomingCall.value.callerId) if (friend) { friend.lastMessage = "[已拒绝]" chatDB.updateFriendLastMessage(friend.id, userStore.currentUser.id, "[已拒绝]", friend.unreadCount) } incomingCall.value = null // 5秒后清除状态 setTimeout(() => { callStatus.value = "idle" callError.value = null callConnectionStatus.value = "" }, 5000) console.log("拒绝通话") } // 结束通话 const endCall = () => { const callId = currentCall.value?.callId || incomingCall.value?.callId const peerId = currentCall.value?.peerId || incomingCall.value?.callerId const callTypeVal = currentCall.value?.callType || incomingCall.value?.callType if (!callId || !peerId) return // 清除超时计时器 if (currentCall.value?.timeoutId) { clearTimeout(currentCall.value.timeoutId) } // 发送结束信号 sendMessage({ senderId: userStore.currentUser?.id, receiverId: peerId, type: callTypeVal === "video" ? "video-call" : "audio-call", callId: callId, callStatus: "ended" }) callStatus.value = "ended" callConnectionStatus.value = "通话结束" // 5秒后清除状态 setTimeout(() => { callStatus.value = "idle" currentCall.value = null incomingCall.value = null callError.value = null callConnectionStatus.value = "" }, 5000) console.log("结束通话") } return { friends, currentFriend, messages, connectionStatus, socket, currentCall, incomingCall, callStatus, callError, callConnectionStatus, isConnected, isInCall, isCalling, isRinging, isOngoingCall, initDB, loadFriends, setCurrentFriend, switchFriend, addMessage, handleIncomingMessage, handleCallSignal, startCall, acceptCall, rejectCall, endCall } })