Files
EchoChat/frontend/src/api/meeting.js
bujinyuan f97fec24a8 feat(phase2e-2): 前端 mediasoup-client 集成 + Pinia meeting Store(Task 9)
- frontend/src/api/meeting.js:12 个 REST 接口封装,统一 unwrap envelope.data
- frontend/src/services/websocket.js:新增 sendWithAck(Promise 化 + 超时 + 序列号)
- frontend/src/utils/mediasoup-client.js:MediaEngine 包装 Device/Transport/Producer/Consumer
- frontend/src/store/meeting.js:Pinia 会议状态机,桥接 14 个 WS 事件 + cleanupStaleMeetings
- frontend/src/constants/meeting.js:状态枚举 + 事件名集中管理
- frontend/src/pages/meeting/debug.vue:临时调试页(H5 原生 video/audio DOM 绕过 uni 组件限制)
- backend:meeting.consume.resume WS 事件 + create/join 响应透传 router_id + rtp_capabilities
- 文档:frontend/meeting.md、websocket.md、CURRENT_STATUS、plan 全部同步 Task 9 落地

Made-with: Cursor
2026-04-22 11:22:49 +08:00

161 lines
4.5 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.

/**
* 会议模块 REST API
*
* 对应后端路由:/api/v1/meeting/*(见 backend/go-service/app/meeting/router.go
*
* 设计原则:
* - 本文件仅做 REST 封装业务层store/meeting.js统一调本文件方法
* - WebRTC 媒体层信令通过 WSservices/websocket.js + sendWithAck发送不在此处
* - 响应中的 DTO 字段命名与后端 DTO 完全一致(下划线),前端按需再转换
*/
import { get, post } from '@/utils/request'
/**
* 统一拆包utils/request.js 返回完整 envelope { code, message, data, trace_id, time }
* 业务层只关心 data 负载;这里一次性拆包,避免每个 store/页面都写 .data.xxx
* 对无 body 的响应(如 leave/end返回 null 而非 undefined方便调用方判空
*/
const unwrap = (promise) => promise.then((resp) => (resp && 'data' in resp ? (resp.data ?? null) : (resp ?? null)))
// ==================== 会议房间6 接口) ====================
/**
* 创建即时会议
* @param {Object} payload - { title, password?, max_members? }
* @returns {Promise<{ room: MeetingRoomDTO }>}
*/
const createRoom = (payload) => {
return unwrap(post('/api/v1/meeting/rooms', payload))
}
/**
* 查询会议详情(含参与者列表)
* @param {string} roomCode - 会议号XXX-XXX-XXX 或 9 位纯数字)
* @returns {Promise<{ room, participants, online_count }>}
*/
const getRoom = (roomCode) => {
return unwrap(get(`/api/v1/meeting/rooms/${roomCode}`))
}
/**
* 加入会议
* @param {string} roomCode - 会议号
* @param {Object} [payload] - { password? }
* @returns {Promise<{ room, participant, router_id }>}
*/
const joinRoom = (roomCode, payload = {}) => {
return unwrap(post(`/api/v1/meeting/rooms/${roomCode}/join`, payload))
}
/**
* 离开会议
* @param {string} roomCode
* @returns {Promise<{ duration }>}
*/
const leaveRoom = (roomCode) => {
return unwrap(post(`/api/v1/meeting/rooms/${roomCode}/leave`))
}
/**
* 结束会议(仅主持人可调)
* @param {string} roomCode
* @returns {Promise<void>}
*/
const endRoom = (roomCode) => {
return unwrap(post(`/api/v1/meeting/rooms/${roomCode}/end`))
}
/**
* 我的会议列表(历史 + 进行中)
* @param {Object} [params] - { status?, before_id?, limit? }
* @returns {Promise<{ list, has_more }>}
*/
const listMyMeetings = (params = {}) => {
return unwrap(get('/api/v1/meeting/rooms/mine', params))
}
// ==================== 会议成员管理3 接口) ====================
/**
* 转让主持人
* @param {string} roomCode
* @param {number} targetUserID
* @returns {Promise<void>}
*/
const transferHost = (roomCode, targetUserID) => {
return unwrap(post(`/api/v1/meeting/rooms/${roomCode}/transfer-host`, {
target_user_id: targetUserID
}))
}
/**
* 移除成员(踢人,仅主持人)
* @param {string} roomCode
* @param {number} userID
* @returns {Promise<void>}
*/
const kickMember = (roomCode, userID) => {
return unwrap(post(`/api/v1/meeting/rooms/${roomCode}/kick`, { user_id: userID }))
}
/**
* 邀请用户(批量)
* @param {string} roomCode
* @param {number[]} inviteeIDs - 被邀请用户 ID 数组1~50
* @returns {Promise<{ pushed, skipped }>}
*/
const inviteUsers = (roomCode, inviteeIDs) => {
return unwrap(post(`/api/v1/meeting/rooms/${roomCode}/invite`, {
invitee_ids: inviteeIDs
}))
}
// ==================== 邀请兑换1 接口) ====================
/**
* 兑换邀请 Token从通知点进链接时使用
* @param {string} token - 邀请链接中的 token 字符串
* @returns {Promise<{ room_code, inviter_id, has_password }>}
*/
const redeemInvite = (token) => {
return unwrap(post(`/api/v1/meeting/invite-tokens/${token}/redeem`))
}
// ==================== 会议内聊天2 接口) ====================
/**
* 发送会议内聊天消息
* @param {string} roomCode
* @param {string} content - 1~500 字
* @returns {Promise<{ message }>}
*/
const sendChat = (roomCode, content) => {
return unwrap(post(`/api/v1/meeting/rooms/${roomCode}/chats`, { content }))
}
/**
* 查询会议聊天历史(游标分页)
* @param {string} roomCode
* @param {Object} [params] - { before_id?, limit? }
* @returns {Promise<{ list, has_more }>}
*/
const listChats = (roomCode, params = {}) => {
return unwrap(get(`/api/v1/meeting/rooms/${roomCode}/chats`, params))
}
export default {
createRoom,
getRoom,
joinRoom,
leaveRoom,
endRoom,
listMyMeetings,
transferHost,
kickMember,
inviteUsers,
redeemInvite,
sendChat,
listChats
}