# Jitsi 会议独立页面实现方案 ## 问题背景 1. **Web端问题**:ChatRoom 弹窗关闭按钮不应该触发 `endMeeting`,只有在 Jitsi iframe 中点击"结束会议"才应该结束会议 2. **主持人问题**:主持人应该是数据库中的会议创建人,而不是第一个进入会议的人 3. **小程序问题**:无法捕捉结束会议事件 ## 解决方案 ### 1. 后端修改(已完成) #### 1.1 新增路由配置 (`initDataPermission.sql`) ```sql -- Jitsi视频会议独立页面(支持URL参数token认证,用于小程序和外部链接访问) ('VIEW-W003', 'view_workcase_jitsi_meeting', 'Jitsi视频会议', NULL, '/meeting/:meetingId', 'public/Meeting/JitsiMeetingView.vue', 'Video', 1, 'route', NULL, 'workcase', 'BlankLayout', 25, 'Jitsi视频会议独立页面,支持token参数认证', 'system', now(), false); ``` #### 1.2 新增 API 接口 | 接口 | 方法 | 说明 | |------|------|------| | `/meeting/{meetingId}/entry?token=xxx` | GET | 会议入口(支持URL参数token认证) | | `/meeting/{meetingId}/share-url` | GET | 生成会议分享URL | #### 1.3 Gateway 白名单 ```yaml whitelist: - /urban-lifeline/workcase/meeting/*/entry ``` #### 1.4 主持人逻辑 主持人判断逻辑已正确实现:`userId.equals(meeting.getCreator())` ### 2. 前端实现(需要在前端仓库实现) #### 2.1 创建 `JitsiMeetingView.vue` 路径:`src/views/public/Meeting/JitsiMeetingView.vue` ```vue ``` #### 2.2 API 接口定义 ```typescript // src/api/meeting.ts import request from '@/utils/request' // 获取会议入口信息(支持token参数) export function getMeetingEntry(meetingId: string, token?: string) { return request({ url: `/workcase/meeting/${meetingId}/entry`, method: 'get', params: { token }, // 不使用默认的Authorization header headers: token ? { 'Authorization': `Bearer ${token}` } : {} }) } // 结束会议 export function endMeeting(meetingId: string) { return request({ url: `/workcase/meeting/${meetingId}/end`, method: 'post' }) } // 生成会议分享URL export function generateMeetingShareUrl(meetingId: string, baseUrl?: string) { return request({ url: `/workcase/meeting/${meetingId}/share-url`, method: 'get', params: { baseUrl } }) } ``` #### 2.3 路由配置 ```typescript // src/router/workcase.ts { path: '/meeting/:meetingId', name: 'JitsiMeeting', component: () => import('@/views/public/Meeting/JitsiMeetingView.vue'), meta: { layout: 'BlankLayout', requiresAuth: false, // 允许通过URL token认证 title: '视频会议' } } ``` #### 2.4 修改 ChatRoom 组件 在 ChatRoom 组件中,点击"进入会议"时: ```typescript // 进入会议 const joinMeeting = async (meetingId: string) => { // 生成会议入口URL const response = await generateMeetingShareUrl(meetingId) if (response.success) { // 在新窗口打开会议页面 window.open(response.data, '_blank', 'width=1200,height=800') } } ``` #### 2.5 Web 初始化逻辑修改 在 workcase 前端的初始化逻辑中,添加从 URL token 恢复登录状态的功能: ```typescript // src/utils/auth.ts export async function initAuth() { // 1. 检查 localStorage 中是否有登录信息 const storedToken = localStorage.getItem('token') if (storedToken) { // 验证token是否有效 const isValid = await validateToken(storedToken) if (isValid) { return true } } // 2. 检查 URL 参数中是否有 token const urlParams = new URLSearchParams(window.location.search) const urlToken = urlParams.get('token') if (urlToken) { try { // 调用 refresh 接口验证 token 并获取用户信息 const response = await refreshToken(urlToken) if (response.success) { // 保存登录信息到 localStorage localStorage.setItem('token', response.data.token) localStorage.setItem('loginDomain', JSON.stringify(response.data)) return true } } catch (e) { console.error('URL token 验证失败:', e) } } return false } ``` ### 3. 小程序端实现 小程序端直接给出会议链接,用户在手机浏览器中打开: ```javascript // 小程序中获取会议链接 const getMeetingUrl = async (meetingId) => { const token = wx.getStorageSync('token') const baseUrl = 'https://your-domain.com/workcase' // 构建会议入口URL const meetingUrl = `${baseUrl}/meeting/${meetingId}?token=${token}` // 复制到剪贴板或显示给用户 wx.setClipboardData({ data: meetingUrl, success: () => { wx.showToast({ title: '会议链接已复制,请在浏览器中打开', icon: 'none' }) } }) } ``` ### 4. 关键流程 #### 4.1 Web 端进入会议流程 ``` 用户点击"进入会议" ↓ 调用 generateMeetingShareUrl 获取带token的URL ↓ window.open 打开 JitsiMeetingView.vue ↓ JitsiMeetingView 从URL获取token ↓ 调用 /meeting/{id}/entry?token=xxx 获取会议信息 ↓ 初始化 Jitsi External API ↓ 监听 readyToClose/hangup 事件 ↓ 主持人结束会议时调用 endMeeting API ``` #### 4.2 小程序端进入会议流程 ``` 用户点击"进入会议" ↓ 获取当前用户token ↓ 构建会议URL: /meeting/{id}?token=xxx ↓ 复制URL到剪贴板 ↓ 用户在手机浏览器打开URL ↓ JitsiMeetingView 从URL获取token ↓ 调用 /meeting/{id}/entry?token=xxx 获取会议信息 ↓ 初始化 Jitsi External API ``` ### 5. 注意事项 1. **主持人权限**:只有会议创建人(`meeting.creator`)才是主持人,JWT token 中的 `moderator` 字段会正确设置 2. **结束会议**:只有主持人点击"结束会议"或挂断时才会调用 `endMeeting` API 3. **Token 安全**:URL 中的 token 应该有时效性,建议使用短期 token 或一次性 token 4. **跨域问题**:确保 Jitsi 服务器配置了正确的 CORS 策略 5. **HTTPS**:生产环境必须使用 HTTPS,否则浏览器可能阻止摄像头/麦克风访问