/**
* @file stores/modules/groupStore.js
* @description Pinia Store,用于管理用户所属的群组列表、活跃群组状态、群组邀请以及群组成员操作。
* @module GroupStore
*/
import { defineStore } from 'pinia'
import { ref } from 'vue'
import {
getUserGroups,
getGroupInvitations as apiGetGroupInvitations,
groupInvitationResponse,
kickGroupMember,
getGroupMembers,
disbandGroup as apiDisbandGroup
} from '@/api/group'
import { ElMessage } from 'element-plus'
import emitter from '../../services/eventBus' // 导入全局事件总线
/**
* @function useGroupStore
* @description Pinia Store,用于管理用户在应用程序中的群组相关状态和操作。
* 包含群组列表、活跃群组、群组邀请和成员管理等功能。
* @returns {{
* groups: Ref<Array<object>>,
* activeGroup: Ref<object|null>,
* activeGroupId: Ref<string|null>,
* isGroupActive: Ref<boolean>,
* groupInvitations: Ref<Array<object>>,
* unreadGroupInvitation: Ref<number|null>,
* groupMembers: Ref<Array<object>>,
* setGroups: Function,
* setActiveGroup: Function,
* getGroupInvitations: Function,
* handleRequestAction: Function,
* handleNewRequest: Function,
* handleKickGroupMember: Function,
* fetchGroupMembers: Function,
* clearActiveGroup: Function,
* addNewGroup: Function,
* updateGroup: Function,
* disbandGroup: Function
* }}
* @property {Ref<Array<object>>} groups - 用户所属的群组列表。
* @property {Ref<object|null>} activeGroup - 当前活跃的群组对象。
* @property {Ref<string|null>} activeGroupId - 当前活跃群组的 ID。
* @property {Ref<boolean>} isGroupActive - 当前是否活跃在群组(而非私聊)会话中。
* @property {Ref<Array<object>>} groupInvitations - 用户收到的群组邀请列表。
* @property {Ref<number|null>} unreadGroupInvitation - 未读群组邀请的数量。
* @property {Ref<Array<object>>} groupMembers - 当前活跃群组的成员列表。
* @property {Function} setGroups - 从后端获取并设置用户群组列表。
* @property {Function} setActiveGroup - 设置当前活跃的群组。
* @property {Function} getGroupInvitations - 从后端获取并更新群组邀请列表。
* @property {Function} handleRequestAction - 处理群组邀请(接受或拒绝)。
* @property {Function} handleNewRequest - 处理新的实时群组邀请,并发送通知。
* @property {Function} handleKickGroupMember - 将指定成员从当前活跃群组中踢出。
* @property {Function} fetchGroupMembers - 获取当前活跃群组的成员列表。
* @property {Function} clearActiveGroup - 清除当前活跃群组的状态。
* @property {Function} addNewGroup - 向群组列表中添加一个新创建的群组。
* @property {Function} updateGroup - 更新本地活跃群组的名称和描述。
* @property {Function} disbandGroup - 解散当前活跃的群组。
*/
export const useGroupStore = defineStore(
'group',
() => {
// --- 状态 (State) ---
/** @type {Ref<Array<object>>} */
const groups = ref([]) // 用户所属的群组列表
/** @type {Ref<object|null>} */
const activeGroup = ref(null) // 当前活跃的群组对象
/** @type {Ref<string|null>} */
const activeGroupId = ref(null) // 当前活跃群组的 ID
/** @type {Ref<boolean>} */
const isGroupActive = ref(false) // 标识当前是否活跃在群组会话中 (private: false, group: true)
/** @type {Ref<Array<object>>} */
const groupInvitations = ref([]) // 用户收到的群组邀请列表
/** @type {Ref<number|null>} */
const unreadGroupInvitation = ref(null) // 未读群组邀请的数量
/** @type {Ref<Array<object>>} */
const groupMembers = ref([]) // 当前活跃群组的成员列表
// Actions
/**
* @function setGroups
* @description 从后端 API 获取当前用户的所有群组列表,并更新 `groups` 状态。
* @returns {Promise<void>}
* @throws {Error} 如果获取群组列表失败,会打印错误信息。
*/
const setGroups = async () => {
try {
const response = await getUserGroups() // 调用 API 获取用户群组
groups.value = response.data // 更新群组列表
} catch (error) {
console.error(error)
}
}
/**
* @function setActiveGroup
* @description 设置当前活跃的群组,并更新相关状态。
* @param {object} group - 要设置为活跃状态的群组对象。
* @returns {void}
*/
const setActiveGroup = (group) => {
activeGroup.value = group
activeGroupId.value = activeGroup.value._id // 更新活跃群组 ID
isGroupActive.value = true // 标记为群组活跃
}
/**
* @function getGroupInvitations
* @description 从后端 API 获取用户收到的所有群组邀请,并更新 `groupInvitations` 状态。
* 同时更新 `unreadGroupInvitation` 的数量。
* @returns {Promise<void>}
* @throws {Error} 如果获取群组邀请失败,会打印错误信息。
*/
const getGroupInvitations = async () => {
try {
const response = await apiGetGroupInvitations() // 调用 API 获取群组邀请
groupInvitations.value = response.data // 更新群组邀请列表
unreadGroupInvitation.value = groupInvitations.value.length // 更新未读邀请数量
} catch (error) {
console.error('获取群组邀请失败:', error)
}
}
/**
* @function handleRequestAction
* @description 处理群组邀请的接受或拒绝操作。
* 成功后从邀请列表中移除该邀请,更新未读计数。如果接受,则添加到群组列表。
* @param {string} groupId - 邀请所属的群组 ID。
* @param {'accept' | 'decline'} action - 对邀请执行的操作('accept' 或 'decline')。
* @param {string} invitationId - 邀请的 ID。
* @returns {Promise<void>}
* @throws {Error} 如果 API 调用失败,会打印错误信息并显示操作失败的提示。
*/
const handleRequestAction = async (groupId, action, invitationId) => {
if (groupId) {
try {
const response = await groupInvitationResponse({
// 调用 API 响应群组邀请
groupId,
action
})
ElMessage.success(response.message)
// 从本地邀请列表中移除已处理的邀请
groupInvitations.value = groupInvitations.value.filter(
(invitation) => invitation._id !== invitationId
)
unreadGroupInvitation.value-- // 未读数减一
// 如果接受了邀请,将新加入的群组添加到用户群组列表中
if (action === 'accept') {
groups.value.unshift(response.data)
}
} catch (error) {
console.error('处理群组邀请失败:', error)
ElMessage.success('操作失败') // 显示操作失败提示
}
}
}
/**
* @function handleNewRequest
* @description 处理新的实时群组邀请。
* 将新邀请添加到 `groupInvitations` 列表的开头,增加未读计数,并发送桌面通知。
* @param {object} newRequestData - 新的群组邀请数据。
* @param {object} newRequestData.inviter - 邀请者用户信息(包含 username)。
* @param {object} newRequestData.group - 被邀请加入的群组信息(包含 name)。
* @returns {void}
*/
const handleNewRequest = (newRequestData) => {
groupInvitations.value.unshift(newRequestData) // 添加到列表开头
unreadGroupInvitation.value++ // 增加未读计数
// 发送全局通知事件
emitter.emit('show-notification', {
title: '新的群组邀请',
message: `用户 ${newRequestData.inviter.username} 邀请你加入群组 ${newRequestData.group.name}`
})
}
/**
* @function handleKickGroupMember
* @description 将指定成员从当前活跃群组中踢出。
* @param {string} memberId - 要踢出的成员的用户 ID。
* @returns {Promise<void>}
* @throws {Error} 如果踢出成员失败,会打印错误信息。
*/
const handleKickGroupMember = async (memberId) => {
try {
await kickGroupMember({
// 调用 API 踢出成员
groupId: activeGroupId.value,
memberId
})
ElMessage.success('已将该用户移出群组') // 显示成功提示
// 从本地群组成员列表中移除被踢出的成员
groupMembers.value = groupMembers.value.filter(
(item) => item._id !== memberId
)
} catch (error) {
console.error('踢出群成员失败:', error)
}
}
/**
* @function fetchGroupMembers
* @description 获取当前活跃群组的成员列表。
* @returns {Promise<void>}
* @throws {Error} 如果获取成员列表失败,会打印错误信息。
*/
const fetchGroupMembers = async () => {
try {
const response = await getGroupMembers(activeGroupId.value) // 调用 API 获取群组成员
groupMembers.value = response.data.members // 更新群组成员列表
} catch (error) {
console.error('获取群组成员失败:', error)
}
}
/**
* @function clearActiveGroup
* @description 清除当前活跃群组的状态,通常在切换到私聊会话时调用。
* @returns {void}
*/
const clearActiveGroup = () => {
activeGroup.value = null
isGroupActive.value = false
}
/**
* @function addNewGroup
* @description 向群组列表中添加一个新创建的群组。
* @param {object} newGroup - 新创建的群组对象。
* @returns {void}
*/
const addNewGroup = (newGroup) => {
if (newGroup) {
groups.value.push(newGroup) // 添加新群组到列表
}
}
/**
* @function updateGroup
* @description 更新本地活跃群组的名称和描述。
* @param {string} groupName - 群组的新名称。
* @param {string} groupDescription - 群组的新描述。
* @returns {void}
*/
const updateGroup = (groupName, groupDescription) => {
const group = groups.value.find((g) => g._id === activeGroup.value._id)
if (group) {
group.name = groupName
group.description = groupDescription
}
// 更新活跃群组的显示名称和描述,确保立即反映在 UI 上
activeGroup.value.name = groupName
activeGroup.value.description = groupDescription
}
/**
* @function disbandGroup
* @description 解散当前活跃的群组。
* 成功后从群组列表中移除该群组,并尝试激活列表中的下一个群组。
* @returns {Promise<void>}
* @throws {Error} 如果解散群组失败,会打印错误信息并显示提示。
*/
const disbandGroup = async () => {
try {
await apiDisbandGroup(activeGroup.value._id) // 调用 API 解散群组
ElMessage.success('群组已成功解散!') // 显示成功提示
// 从本地群组列表中移除已解散的群组
groups.value = groups.value.filter(
(group) => group._id !== activeGroup.value._id
)
// 替换当前激活群组
if (groups) {
setActiveGroup(groups.value[0])
}
} catch (error) {
console.error('解散群组失败:', error)
ElMessage.closeAll()
ElMessage.error('解散群组失败')
}
}
// --- 返回 Store 的状态和操作 ---
return {
groups,
isGroupActive,
activeGroup,
activeGroupId,
groupInvitations,
unreadGroupInvitation,
groupMembers,
setGroups,
setActiveGroup,
getGroupInvitations,
handleRequestAction,
handleNewRequest,
handleKickGroupMember,
fetchGroupMembers,
clearActiveGroup,
addNewGroup,
updateGroup,
disbandGroup
}
},
{
// Pinia 持久化配置
persist: {
paths: ['activeGroup']
}
}
)