优化目录结构
This commit is contained in:
157
client/public/docs/README.md
Normal file
157
client/public/docs/README.md
Normal file
@@ -0,0 +1,157 @@
|
||||
OneByOne 视频通话应用代码结构分析
|
||||
1. 项目概述
|
||||
OneByOne 是一个基于 WebRTC 和 WebSocket 的一对一视频通话应用,具有现代化的用户界面和丰富的功能。
|
||||
|
||||
2. 目录结构
|
||||
|
||||
plainText
|
||||
onebyone/
|
||||
├── index.html # 主HTML文件,包含页面结构
|
||||
├── main.js # 主入口文件,初始化应用
|
||||
├── renderer.js # UI渲染器,负责将状态映射到DOM
|
||||
├── store.js # 状态管理,使用Observable模式
|
||||
├── models.js # 数据模型定义
|
||||
├── websocket.js # WebSocket管理
|
||||
├── utils.js # 工具函数
|
||||
└── style.css # 样式文件
|
||||
3. 核心模块分析
|
||||
3.1 数据模型 (models.js)
|
||||
定义了应用的核心数据结构:
|
||||
|
||||
CallSession: 通话会话,包含通话ID、类型、状态、时长等信息
|
||||
LocalUser: 本地用户,包含用户ID、名称、头像、媒体状态等
|
||||
RemoteUser: 远端用户,包含用户ID、名称、头像、状态、网络质量等
|
||||
MediaState: 媒体状态,包含音频、视频、屏幕共享、录屏、说话状态等
|
||||
ChatMessage: 聊天消息,包含消息ID、发送者信息、内容、类型、时间戳等
|
||||
3.2 状态管理 (store.js)
|
||||
使用简单的 Observable 模式实现状态管理:
|
||||
|
||||
核心状态:通话会话、消息列表、侧边栏状态、未读消息数、媒体流等
|
||||
状态更新方法:
|
||||
updateLocalMedia(): 更新本地媒体状态
|
||||
updateRemoteMedia(): 更新远端媒体状态
|
||||
addMessage(): 添加消息
|
||||
toggleSidebar(): 切换侧边栏
|
||||
endCall(): 结束通话
|
||||
事件通知:通过 notify() 方法通知所有监听器状态变化
|
||||
3.3 UI渲染器 (renderer.js)
|
||||
负责将状态映射到DOM:
|
||||
|
||||
DOM元素缓存:缓存常用DOM元素,提高性能
|
||||
渲染方法:
|
||||
renderHeader(): 渲染头部
|
||||
renderCallDuration(): 渲染通话时长
|
||||
renderRemoteVideo(): 渲染远端视频
|
||||
renderLocalVideo(): 渲染本地视频
|
||||
renderControlButtons(): 渲染控制按钮
|
||||
renderChatMessages(): 渲染聊天消息
|
||||
renderLocalUserStatus(): 渲染本地用户状态
|
||||
消息渲染:支持文本消息和图片消息的渲染
|
||||
3.4 WebSocket管理 (websocket.js)
|
||||
管理WebSocket连接和事件:
|
||||
|
||||
连接管理:连接、断开、重连
|
||||
消息处理:处理服务器发送的消息
|
||||
事件订阅:支持事件订阅和触发
|
||||
心跳检测:支持ping/pong心跳机制
|
||||
3.5 主入口 (main.js)
|
||||
初始化应用,连接各个模块:
|
||||
|
||||
应用初始化:初始化渲染器、WebSocket连接、事件绑定
|
||||
WebSocket事件绑定:处理服务器事件
|
||||
DOM事件绑定:处理用户交互
|
||||
功能实现:
|
||||
麦克风、视频、录屏控制
|
||||
聊天消息发送
|
||||
图片上传和发送
|
||||
通话结束确认
|
||||
3.6 样式文件 (style.css)
|
||||
提供应用的样式:
|
||||
|
||||
基础样式:布局、颜色、字体等
|
||||
组件样式:玻璃效果、控制按钮、聊天消息等
|
||||
动画效果:消息滑入、音频波形、视频淡入等
|
||||
4. 核心功能
|
||||
4.1 视频通话
|
||||
支持一对一视频通话
|
||||
本地视频预览(画中画模式)
|
||||
远端视频显示
|
||||
视频开关控制
|
||||
4.2 音频控制
|
||||
麦克风开关控制
|
||||
说话状态检测
|
||||
音频波形动画
|
||||
4.3 聊天功能
|
||||
文本消息发送
|
||||
图片消息发送(支持文件选择和预览)
|
||||
消息时间戳
|
||||
消息类型区分(系统消息、自己的消息、对方的消息)
|
||||
4.4 录屏功能
|
||||
录屏开关控制
|
||||
录制状态通知
|
||||
4.5 网络状态
|
||||
网络质量检测和显示
|
||||
连接状态提示
|
||||
4.6 其他功能
|
||||
端到端加密提示
|
||||
通话时长显示
|
||||
键盘快捷键(空格键静音,Ctrl+V切换视频)
|
||||
通知系统
|
||||
5. 技术特点
|
||||
5.1 模块化设计
|
||||
代码按功能模块分离,职责清晰
|
||||
使用ES6模块系统,便于维护和扩展
|
||||
5.2 状态管理
|
||||
使用Observable模式实现简单的状态管理
|
||||
状态变化自动触发UI更新
|
||||
5.3 响应式设计
|
||||
使用Tailwind CSS实现响应式布局
|
||||
适配不同屏幕尺寸
|
||||
5.4 现代化UI
|
||||
玻璃拟态效果
|
||||
平滑动画
|
||||
清晰的视觉层次
|
||||
5.5 事件驱动
|
||||
使用事件机制处理用户交互和系统通知
|
||||
松耦合的组件通信
|
||||
6. API接口
|
||||
应用定义了以下API接口:
|
||||
|
||||
GET /api/call/:callId - 获取通话信息
|
||||
POST /api/call/:callId/join - 加入通话
|
||||
POST /api/call/:callId/leave - 离开通话
|
||||
POST /api/call/:callId/media - 更新媒体状态
|
||||
GET /api/call/:callId/messages - 获取历史消息
|
||||
POST /api/call/:callId/message - 发送消息
|
||||
7. WebSocket事件
|
||||
应用使用WebSocket处理实时通信,支持以下事件:
|
||||
|
||||
connect - 连接建立
|
||||
disconnect - 连接断开
|
||||
user-joined - 用户加入
|
||||
user-left - 用户离开
|
||||
media-state-changed - 媒体状态变化
|
||||
message-received - 收到消息
|
||||
network-quality - 网络质量变化
|
||||
call-ended - 通话结束
|
||||
8. 代码优化建议
|
||||
错误处理:增加更完善的错误处理机制,特别是WebSocket连接和API调用
|
||||
性能优化:
|
||||
减少DOM操作,使用虚拟DOM或批量更新
|
||||
优化图片加载和处理
|
||||
安全性:
|
||||
增加输入验证,防止XSS攻击
|
||||
实现真正的端到端加密
|
||||
可扩展性:
|
||||
考虑使用更成熟的状态管理库(如Redux)
|
||||
增加单元测试和集成测试
|
||||
用户体验:
|
||||
增加更多的动画和过渡效果
|
||||
优化移动端体验
|
||||
增加更多的用户反馈
|
||||
9. 总结
|
||||
OneByOne 是一个功能完整、界面现代化的一对一视频通话应用,采用了模块化设计和现代化的前端技术。它具有视频通话、音频控制、聊天功能、录屏功能等核心功能,并且支持实时通信和状态管理。
|
||||
|
||||
应用的代码结构清晰,职责分离明确,使用了Observable模式进行状态管理,WebSocket进行实时通信,Tailwind CSS进行样式设计。这些技术选择使得应用具有良好的可维护性和可扩展性。
|
||||
|
||||
通过进一步的优化和扩展,OneByOne可以成为一个功能更加强大、用户体验更加出色的视频通话应用。
|
||||
458
client/public/docs/code-structure.md
Normal file
458
client/public/docs/code-structure.md
Normal file
@@ -0,0 +1,458 @@
|
||||
# onebyone 模块代码调用结构图
|
||||
|
||||
> 本文档基于优化后的代码自动生成,反映当前实际架构。
|
||||
|
||||
---
|
||||
|
||||
## 3.1 文件依赖关系图
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
main[main.js] --> store[store.js]
|
||||
main --> renderer[renderer.js]
|
||||
main --> utils[utils.js]
|
||||
main --> chatmsg[chatmessage.js]
|
||||
|
||||
store --> models[models.js]
|
||||
store --> utils
|
||||
store --> chatmsg
|
||||
store --> signaling[../../module/signaling.js]
|
||||
store --> rs[../../module/renderstreaming.js]
|
||||
store --> config[../js/config.js]
|
||||
|
||||
renderer --> utils
|
||||
renderer --> models
|
||||
renderer --> chatmsg
|
||||
renderer -.-> store
|
||||
|
||||
chatmsg --> utils
|
||||
chatmsg --> store
|
||||
chatmsg --> models
|
||||
|
||||
connect[connect/connect.js] --> store
|
||||
connect --> utils
|
||||
|
||||
endcall[endcall/endcall.js] --> utils
|
||||
|
||||
index[index.html] --> main
|
||||
connectHtml[connect/connect.html] --> connect
|
||||
endcallHtml[endcall/endcall.html] --> endcall
|
||||
|
||||
style utils fill:#e1f5fe
|
||||
style models fill:#e1f5fe
|
||||
style signaling fill:#fff3e0
|
||||
style rs fill:#fff3e0
|
||||
style config fill:#fff3e0
|
||||
```
|
||||
|
||||
**图例说明**
|
||||
- 蓝色:内部工具/数据模块
|
||||
- 橙色:外部依赖模块(signaling.js、renderstreaming.js、config.js)
|
||||
|
||||
---
|
||||
|
||||
## 3.2 核心调用链
|
||||
|
||||
### 通话初始化流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant Browser
|
||||
participant main as main.js
|
||||
participant store as store.js
|
||||
participant render as renderer.js
|
||||
participant RS as RenderStreaming
|
||||
participant Sig as Signaling
|
||||
|
||||
Browser->>main: DOMContentLoaded
|
||||
main->>main: 检查 localStorage.connectionId
|
||||
alt 无连接ID
|
||||
main->>Browser: 跳转 connect/connect.html
|
||||
else 有连接ID
|
||||
main->>render: new UIRenderer(store)
|
||||
render->>store: subscribe(render)
|
||||
main->>store: joinCall(connectionId)
|
||||
store->>store: init()
|
||||
store->>store: setupConfig() / getServerConfig()
|
||||
store->>store: loadUserSettings()
|
||||
store->>store: getLocalStream()
|
||||
store->>Browser: getUserMedia(MEDIA_CONSTRAINTS)
|
||||
Browser-->>store: MediaStream
|
||||
store->>store: notify(LOCAL_STREAM_OBTAINED)
|
||||
store->>store: notify(LOCAL_MEDIA_CHANGE x2)
|
||||
store->>store: emitMediaStateChange()
|
||||
main->>store: setUp(connectionId)
|
||||
store->>store: _createSignalingAndRTC()
|
||||
store->>Sig: new WebSocketSignaling() / new Signaling()
|
||||
store->>RS: new RenderStreaming(signaling, config)
|
||||
store->>store: _registerCallbacks()
|
||||
store->>store: _startConnection()
|
||||
store->>RS: start()
|
||||
store->>RS: createConnection(connectionId)
|
||||
RS-->>store: onConnect(role, participantId)
|
||||
store->>store: notify(CALL_STATUS_CHANGE, ongoing)
|
||||
store->>store: sendMessage(user-info)
|
||||
store->>store: emitMediaStateChange()
|
||||
main->>main: bindDomEvents()
|
||||
end
|
||||
```
|
||||
|
||||
### 媒体控制流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant User
|
||||
participant main as main.js
|
||||
participant store as store.js
|
||||
participant render as renderer.js
|
||||
participant RS as RenderStreaming
|
||||
participant Remote as 远端
|
||||
|
||||
User->>main: 点击 toggleMute / toggleVideo
|
||||
main->>store: updateLocalMedia(type, value)
|
||||
|
||||
alt 开启视频
|
||||
store->>Browser: getUserMedia(VIDEO_ONLY_CONSTRAINT)
|
||||
Browser-->>store: newVideoTrack
|
||||
store->>RS: replaceTrack / addTransceiver
|
||||
store->>store: notify(LOCAL_STREAM_OBTAINED)
|
||||
store->>store: notify(LOCAL_MEDIA_CHANGE, video=true)
|
||||
store->>store: emitMediaStateChange()
|
||||
else 关闭视频
|
||||
store->>store: track.stop()
|
||||
store->>store: notify(LOCAL_MEDIA_CHANGE, video=false)
|
||||
store->>store: emitMediaStateChange()
|
||||
else 切换音频
|
||||
store->>store: track.enabled = value
|
||||
store->>store: notify(LOCAL_MEDIA_CHANGE, audio=value)
|
||||
store->>store: emitMediaStateChange()
|
||||
end
|
||||
|
||||
store->>render: notify(USER_LIST_UPDATE)
|
||||
render->>render: renderUserList()
|
||||
render->>render: renderControlButtons()
|
||||
render->>render: renderLocalVideo()
|
||||
|
||||
store->>RS: sendMessage(media-state-changed)
|
||||
RS->>Remote: WebSocket 信令
|
||||
Remote-->>store: onMessage(media-state-changed)
|
||||
store->>store: updateRemoteMedia()
|
||||
store->>render: notify(REMOTE_MEDIA_CHANGE)
|
||||
render->>render: renderRemoteVideo()
|
||||
render->>render: renderUserList()
|
||||
render->>render: renderParticipantVideoPlaceholder()
|
||||
```
|
||||
|
||||
### 消息发送流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant User
|
||||
participant chat as chatmessage.js
|
||||
participant store as store.js
|
||||
participant render as renderer.js
|
||||
participant RS as RenderStreaming
|
||||
participant Remote as 远端
|
||||
|
||||
User->>chat: 输入消息 / 回车
|
||||
chat->>chat: sendMessage()
|
||||
chat->>chat: addMessage() 本地
|
||||
chat->>chat: notify(NEW_MESSAGE)
|
||||
render->>render: renderMessageState(NEW_MESSAGE)
|
||||
render->>render: renderChatMessages()
|
||||
render->>render: renderUnreadCount()
|
||||
|
||||
chat->>store: getRenderStreaming()
|
||||
chat->>RS: sendMessage(chat-message)
|
||||
RS->>Remote: WebSocket 信令
|
||||
|
||||
Remote-->>chat: handleChatMessage(data)
|
||||
chat->>chat: addMessage() 远端
|
||||
chat->>chat: notify(NEW_MESSAGE)
|
||||
render->>render: renderMessageState(NEW_MESSAGE)
|
||||
render->>render: renderChatMessages()
|
||||
render->>render: renderUnreadCount()
|
||||
|
||||
User->>chat: 点击 toggleSidebar()
|
||||
chat->>chat: notify(SIDEBAR_TOGGLE)
|
||||
render->>render: renderMessageState(SIDEBAR_TOGGLE)
|
||||
render->>render: renderSidebar()
|
||||
```
|
||||
|
||||
### Participant 管理流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant store as store.js
|
||||
participant render as renderer.js
|
||||
participant RS as RenderStreaming
|
||||
participant Remote as 远端Participant
|
||||
|
||||
Note over RS,Remote: 新 Participant 加入
|
||||
RS-->>store: onParticipantJoined(participantId)
|
||||
store->>store: 初始化 participant 默认信息
|
||||
store->>store: notify(PARTICIPANTS_UPDATE)
|
||||
store->>RS: broadcastParticipantsList()
|
||||
render->>render: renderUserList()
|
||||
render->>render: syncParticipantTileNames()
|
||||
|
||||
Note over RS,Remote: Participant 离开
|
||||
RS-->>store: onParticipantLeft(participantId)
|
||||
store->>store: 清理 remoteStreams / participants
|
||||
store->>store: notify(PARTICIPANT_LEFT)
|
||||
store->>store: notify(PARTICIPANTS_UPDATE)
|
||||
store->>RS: broadcastParticipantsList()
|
||||
render->>render: renderParticipantLeft()
|
||||
render->>render: renderUserList()
|
||||
|
||||
Note over RS,Remote: 成员列表同步
|
||||
RS-->>store: onMessage(participants-sync)
|
||||
store->>store: 过滤自身条目 -> state.participants
|
||||
store->>store: notify(PARTICIPANTS_UPDATE)
|
||||
render->>render: renderUserList()
|
||||
render->>render: syncParticipantTileNames()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3.3 状态变化流转图
|
||||
|
||||
### Store -> Renderer 状态流转
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph StoreNotify[store.js notify 类型]
|
||||
A[INIT]
|
||||
B[LOCAL_STREAM_OBTAINED]
|
||||
C[LOCAL_MEDIA_CHANGE]
|
||||
D[REMOTE_STREAM_OBTAINED]
|
||||
E[REMOTE_MEDIA_CHANGE]
|
||||
F[USER_LIST_UPDATE]
|
||||
G[PARTICIPANTS_UPDATE]
|
||||
H[NETWORK_CHANGE]
|
||||
I[CALL_STATUS_CHANGE]
|
||||
J[CALL_ENDED]
|
||||
K[PARTICIPANT_LEFT]
|
||||
L[DURATION_UPDATE]
|
||||
end
|
||||
|
||||
subgraph RenderMethod[renderer.js 渲染方法]
|
||||
RM1[renderRemoteVideo]
|
||||
RM2[renderLocalVideo]
|
||||
RM3[renderLocalStream]
|
||||
RM4[renderRemoteStream]
|
||||
RM5[renderControlButtons]
|
||||
RM6[renderUserList]
|
||||
RM7[renderNetworkStatus]
|
||||
RM8[renderCallStatus]
|
||||
RM9[renderCallEnded]
|
||||
RM10[renderParticipantLeft]
|
||||
RM11[renderCallDuration]
|
||||
RM12[renderHeader]
|
||||
RM13[renderParticipantVideoPlaceholder]
|
||||
RM14[syncParticipantTileNames]
|
||||
end
|
||||
|
||||
A --> RM1 & RM2 & RM5 & RM6 & RM12
|
||||
B --> RM3 & RM2
|
||||
C --> RM5 & RM2 & RM6
|
||||
D --> RM4
|
||||
E --> RM1 & RM6 & RM13
|
||||
F --> RM6
|
||||
G --> RM6 & RM14
|
||||
H --> RM7
|
||||
I --> RM8
|
||||
J --> RM9
|
||||
K --> RM10
|
||||
L --> RM11
|
||||
```
|
||||
|
||||
### chatMessage -> Renderer 状态流转
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph ChatNotify[chatmessage.js notify 类型]
|
||||
N[NEW_MESSAGE]
|
||||
S[SIDEBAR_TOGGLE]
|
||||
end
|
||||
|
||||
subgraph MsgRender[renderer.js 消息渲染]
|
||||
MR1[renderChatMessages]
|
||||
MR2[renderUnreadCount]
|
||||
MR3[renderSidebar]
|
||||
end
|
||||
|
||||
N --> MR1 & MR2
|
||||
S --> MR3 & MR2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3.4 各模块导出函数清单
|
||||
|
||||
### main.js
|
||||
|
||||
| 导出 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| store | 变量 | 重新导出 store 单例,供外部调试使用 |
|
||||
|
||||
**内部全局函数(绑定到 window):**
|
||||
|
||||
| 函数 | 说明 |
|
||||
|------|------|
|
||||
| toggleSidebar | 切换侧边栏显示 |
|
||||
| toggleMute | 切换麦克风状态 |
|
||||
| toggleVideo | 切换摄像头状态 |
|
||||
| toggleLocalVideo | 本地视频悬停控制 |
|
||||
| toggleRecording | 切换录屏状态 |
|
||||
| endCall | 显示结束通话确认对话框 |
|
||||
| cancelEndCall | 取消结束通话 |
|
||||
| confirmEndCall | 确认结束通话,调用 store.endCall() |
|
||||
| showCallRequest | 显示通话请求弹窗 |
|
||||
| rejectCall | 拒绝通话请求 |
|
||||
| acceptCall | 接受通话请求,初始化通话 |
|
||||
|
||||
### store.js (CallStateManager 类 / store 单例)
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| subscribe(callback) | 订阅状态变化 |
|
||||
| notify(changes) | 通知所有监听器 |
|
||||
| init() | 初始化配置、用户设置、本地流 |
|
||||
| loadUserSettings() | 从 localStorage 加载用户设置 |
|
||||
| setupConfig() | 获取服务器配置(WebSocket 模式) |
|
||||
| getLocalStream() | 获取本地摄像头媒体流 |
|
||||
| updateLocalMedia(mediaType, value) | 更新本地媒体状态(音频/视频/录屏) |
|
||||
| _createSignalingAndRTC(connectionId) | 创建信令和 RTC 实例 |
|
||||
| setUp(connectionId) | 设置 WebRTC 连接入口 |
|
||||
| _registerCallbacks() | 注册所有 WebRTC 回调 |
|
||||
| _startConnection(connectionId) | 启动连接和检测 |
|
||||
| hangUp() | 挂断连接,清理资源 |
|
||||
| sendMessage(type, data) | 通过 RenderStreaming 发送消息 |
|
||||
| broadcastParticipantsList() | Host 端广播成员列表 |
|
||||
| setCodecPreferences(participantId) | 设置 H264 编解码器偏好 |
|
||||
| updateRemoteMedia(mediaState, participantId) | 更新远端媒体状态 |
|
||||
| updateRemoteUserStatus(status) | 更新远端用户在线状态 |
|
||||
| updateRemoteUserNetworkQuality(q) | 更新远端网络质量 |
|
||||
| endCall() | 用户主动结束通话入口 |
|
||||
| joinCall(connectionId) | 加入通话 |
|
||||
| createCall() | 创建通话 |
|
||||
| detectNetworkQuality() | 基于 WebRTC stats 检测网络质量 |
|
||||
| startActivityDetection(stream, opts) | 启动音频活动检测(VAD) |
|
||||
| startNetworkQualityDetection() | 启动定时网络质量检测 |
|
||||
| stopNetworkQualityDetection() | 停止网络质量检测 |
|
||||
| emitMediaStateChange() | 发送媒体状态变化信令 |
|
||||
| showStatsMessage() | 显示并定时输出 WebRTC 统计信息 |
|
||||
| clearStatsMessage() | 清除统计定时器 |
|
||||
| getState / getLocalUser / getRemoteUser / getConnectionId / getRenderStreaming | Getter 方法 |
|
||||
|
||||
### renderer.js (UIRenderer 类)
|
||||
|
||||
| 方法 | 说明 |
|
||||
|------|------|
|
||||
| constructor(stateManager) | 构造函数,订阅 store 和 chatMessage |
|
||||
| render(state, changes) | 核心渲染分发器,根据 changes.type 路由 |
|
||||
| renderMessageState(msgState, changes) | 消息状态渲染分发器 |
|
||||
| renderCallStatus(status) | 渲染通话状态(连接中覆盖层) |
|
||||
| renderHeader(session) | 渲染头部信息 |
|
||||
| renderHeaderTitle() | 渲染标题(含 connectionId) |
|
||||
| renderCallDuration(duration) | 渲染通话时长 |
|
||||
| renderRemoteVideo(remoteUser) | 渲染远端视频和占位符 |
|
||||
| renderHeaderNetworkStatus(q) | 渲染头部网络质量文本 |
|
||||
| renderLocalVideo(localUser, stream) | 渲染本地视频和占位符 |
|
||||
| renderLocalStream(stream) | 将本地流绑定到 video 元素 |
|
||||
| renderRemoteStream(stream, id, isHost) | 远端流渲染分发 |
|
||||
| renderParticipantStream(stream, id) | Host 端多 participant 视频网格渲染 |
|
||||
| renderParticipantVideoPlaceholder(id, show) | 精准更新 participant tile 占位背景 |
|
||||
| syncParticipantTileNames(participants) | 同步所有 tile 名称标签 |
|
||||
| updateParticipantTileName(id, name) | 更新指定 tile 名称 |
|
||||
| renderSingleRemoteStream(stream) | Participant 端单路远端视频渲染 |
|
||||
| renderLocalUserStatus(localUser) | 渲染本地用户媒体状态文本 |
|
||||
| renderUserList(local, remote, participants) | 渲染侧边栏用户列表(支持多 participant) |
|
||||
| createUserEntry(options) | 创建通用用户条目 DOM |
|
||||
| getVideoResolution(track) | 获取视频轨道分辨率 |
|
||||
| adjustVideoSize(video, resolution) | 调整视频元素尺寸 |
|
||||
| renderControlButtons(mediaState) | 渲染底部控制按钮状态 |
|
||||
| renderChatMessages(messages) | 渲染聊天消息列表 |
|
||||
| createMessageElement(message) | 创建单条消息 DOM |
|
||||
| renderUnreadCount(count) | 渲染未读消息角标 |
|
||||
| renderSidebar(isOpen) | 渲染侧边栏显隐 |
|
||||
| renderNetworkStatus(quality) | 渲染网络状态提示 |
|
||||
| updateHeaderNetworkIndicator(q) | 更新头部网络指示器颜色 |
|
||||
| renderCallEnded() | 渲染通话结束,跳转 endcall 页面 |
|
||||
| renderParticipantLeft(id) | 清理 participant tile |
|
||||
| getStatusText / getNetworkQualityText | 状态/质量文本映射 |
|
||||
| destroy() | 销毁,取消订阅 |
|
||||
|
||||
### chatmessage.js
|
||||
|
||||
| 导出函数 | 说明 |
|
||||
|----------|------|
|
||||
| subscribe(callback) | 订阅消息状态变化 |
|
||||
| addMessage(message) | 添加消息到列表 |
|
||||
| sendChatMessage(message) | 通过 RenderStreaming 发送聊天消息 |
|
||||
| handleChatMessage(data) | 处理接收到的聊天消息 |
|
||||
| toggleSidebar() | 切换侧边栏,重置未读数 |
|
||||
| getMessageState() | 获取当前消息状态 |
|
||||
| sendMessage() | 用户发送消息入口(文本) |
|
||||
| handleChatSubmit(event) | 回车发送处理 |
|
||||
| openImagePicker() | 打开图片选择器 |
|
||||
| handleImageUpload(event) | 处理图片上传(限制 5MB) |
|
||||
| sendImageMessage(url, fileName) | 发送图片消息 |
|
||||
| bindMessageEvents() | 绑定消息相关 DOM 事件到 window |
|
||||
|
||||
### utils.js
|
||||
|
||||
| 导出函数 | 说明 |
|
||||
|----------|------|
|
||||
| formatTime(seconds) | 格式化为 MM:SS |
|
||||
| formatTimestamp(timestamp) | 格式化为 HH:MM |
|
||||
| generateId() | 生成随机唯一 ID |
|
||||
| showNotification(message, duration) | 显示顶部通知 |
|
||||
| toggleElement(element, show) | 切换元素显隐(hidden 类) |
|
||||
| toggleButtonState(button, active) | 切换按钮图标状态 |
|
||||
|
||||
### models.js
|
||||
|
||||
| 导出 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| mockCallSession | 常量对象 | 默认通话会话数据结构(含 localUser / remoteUser) |
|
||||
| mockMessages | 常量数组 | 默认聊天消息数组(含系统消息) |
|
||||
|
||||
### connect/connect.js
|
||||
|
||||
| 全局函数 | 说明 |
|
||||
|----------|------|
|
||||
| joinCall() | 读取输入框 connectionId,保存并跳转 |
|
||||
| createCall() | 生成随机 connectionId,保存并跳转 |
|
||||
| getAllConnectionIds() | 从服务器获取所有连接 ID 列表 |
|
||||
| displayConnectionIds(ids) | 渲染连接 ID 列表到 DOM |
|
||||
| selectConnectionId(id) | 选择指定 ID 填充输入框 |
|
||||
| saveSettings() | 保存用户设置到 localStorage |
|
||||
| handleAvatarUpload(event) | 上传头像(限制 2MB) |
|
||||
| copyUserId() | 复制用户 ID 到剪贴板 |
|
||||
| toggleSettingsMenu() | 切换设置菜单显隐 |
|
||||
|
||||
### endcall/endcall.js
|
||||
|
||||
| 全局函数 | 说明 |
|
||||
|----------|------|
|
||||
| reconnectCall() | 重新连接,跳转 index.html |
|
||||
| leaveCall() | 清除 connectionId,跳转 connect.html |
|
||||
|
||||
---
|
||||
|
||||
## 附录:页面跳转关系
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
connect[connect/connect.html] -->|joinCall / createCall| index[index.html]
|
||||
index -->|endCall| endcall[endcall/endcall.html]
|
||||
endcall -->|reconnectCall| index
|
||||
endcall -->|leaveCall| connect
|
||||
index -->|无 connectionId| connect
|
||||
```
|
||||
365
client/public/docs/knowledge-graph.md
Normal file
365
client/public/docs/knowledge-graph.md
Normal file
@@ -0,0 +1,365 @@
|
||||
# OneByOne 知识图谱
|
||||
|
||||
## 1. 模块依赖关系图
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Page["📄 页面层"]
|
||||
INDEX[index.html<br/>主通话页面]
|
||||
CONN[connect/connect.html<br/>连接页面]
|
||||
END[endcall/endcall.html<br/>结束页面]
|
||||
end
|
||||
|
||||
subgraph Core["⚙️ 核心模块"]
|
||||
MAIN[main.js<br/>应用入口]
|
||||
STORE[store.js<br/>状态管理]
|
||||
RENDER[renderer.js<br/>UI渲染器]
|
||||
CHAT[chatmessage.js<br/>消息模块]
|
||||
end
|
||||
|
||||
subgraph Base["📦 数据与工具"]
|
||||
MODELS[models.js<br/>数据模型]
|
||||
UTILS[utils.js<br/>工具函数]
|
||||
end
|
||||
|
||||
subgraph External["🔗 外部依赖"]
|
||||
SIGNALING[signaling.js<br/>信令管理]
|
||||
RENDERSTR[renderstreaming.js<br/>WebRTC管理]
|
||||
CONFIG[config.js<br/>配置管理]
|
||||
end
|
||||
|
||||
INDEX --> MAIN
|
||||
CONN --> CONN_JS[connect.js]
|
||||
END --> END_JS[endcall.js]
|
||||
|
||||
MAIN --> STORE
|
||||
MAIN --> RENDER
|
||||
MAIN --> UTILS
|
||||
MAIN --> CHAT
|
||||
|
||||
STORE --> MODELS
|
||||
STORE --> SIGNALING
|
||||
STORE --> RENDERSTR
|
||||
STORE --> CONFIG
|
||||
STORE --> UTILS
|
||||
STORE --> CHAT
|
||||
|
||||
RENDER --> UTILS
|
||||
RENDER --> MODELS
|
||||
RENDER --> CHAT
|
||||
|
||||
CHAT --> UTILS
|
||||
CHAT --> STORE
|
||||
CHAT --> MODELS
|
||||
|
||||
CONN_JS --> STORE
|
||||
|
||||
style STORE fill:#f9f,stroke:#333,stroke-width:2px
|
||||
style RENDER fill:#bbf,stroke:#333,stroke-width:2px
|
||||
style CHAT fill:#bfb,stroke:#333,stroke-width:2px
|
||||
```
|
||||
|
||||
## 2. 数据模型关系图
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph Models["数据模型结构"]
|
||||
SESSION[CallSession<br/>通话会话]
|
||||
LOCAL[LocalUser<br/>本地用户]
|
||||
REMOTE[RemoteUser<br/>远端用户]
|
||||
MEDIA[MediaState<br/>媒体状态]
|
||||
MSG[ChatMessage<br/>聊天消息]
|
||||
end
|
||||
|
||||
SESSION --> LOCAL
|
||||
SESSION --> REMOTE
|
||||
LOCAL --> MEDIA
|
||||
REMOTE --> MEDIA
|
||||
|
||||
style SESSION fill:#f96,stroke:#333,stroke-width:2px
|
||||
style MEDIA fill:#9f6,stroke:#333,stroke-width:2px
|
||||
```
|
||||
|
||||
### 模型属性说明
|
||||
|
||||
| 模型 | 属性 |
|
||||
|-----|------|
|
||||
| **CallSession** | id, type, status, startTime, duration, isEncrypted |
|
||||
| **LocalUser** | id, name, avatar, isHost, mediaState |
|
||||
| **RemoteUser** | id, name, avatar, status, networkQuality, mediaState |
|
||||
| **MediaState** | audio, video, screenShare, recording, isSpeaking |
|
||||
| **ChatMessage** | id, senderId, senderName, content, type, timestamp, isSelf |
|
||||
|
||||
## 3. 状态更新时序图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant USER as 👤 用户
|
||||
participant DOM as 🖱️ DOM事件
|
||||
participant MAIN as ⚙️ main.js
|
||||
participant STORE as 📦 store.js
|
||||
participant RENDER as 🎨 renderer.js
|
||||
participant UI as 🖥️ UI界面
|
||||
|
||||
USER->>DOM: 点击麦克风按钮
|
||||
DOM->>MAIN: toggleMute()
|
||||
MAIN->>STORE: updateLocalMedia('audio', false)
|
||||
STORE->>STORE: 更新 mediaState.audio
|
||||
STORE->>STORE: notify({type: 'LOCAL_MEDIA_CHANGE'})
|
||||
STORE->>RENDER: 回调 render(state, changes)
|
||||
RENDER->>RENDER: renderControlButtons(mediaState)
|
||||
RENDER->>RENDER: renderLocalUserStatus(localUser)
|
||||
RENDER->>UI: 更新DOM
|
||||
UI->>USER: 显示更新后的界面
|
||||
```
|
||||
|
||||
## 4. WebRTC 连接建立时序图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant USER as 👤 用户
|
||||
participant MAIN as ⚙️ main.js
|
||||
participant STORE as 📦 store.js
|
||||
participant SIGNAL as 📡 signaling.js
|
||||
participant RTC as 🔗 renderstreaming.js
|
||||
participant PEER as 👤 远端对等端
|
||||
|
||||
USER->>MAIN: 访问页面
|
||||
MAIN->>STORE: joinCall(connectionId)
|
||||
STORE->>STORE: init()
|
||||
STORE->>STORE: getLocalStream()<br/>获取本地摄像头
|
||||
MAIN->>STORE: setUp(connectionId)
|
||||
STORE->>SIGNAL: 创建信令实例
|
||||
STORE->>RTC: new RenderStreaming<br/>(signaling, config)
|
||||
RTC->>SIGNAL: start()
|
||||
RTC->>SIGNAL: createConnection(connectionId)
|
||||
|
||||
loop WebSocket信令交换
|
||||
PEER->>SIGNAL: 交换SDP/ICE
|
||||
SIGNAL->>RTC: 信令数据
|
||||
end
|
||||
|
||||
RTC->>STORE: onConnect 回调
|
||||
STORE->>STORE: 状态更新为 ongoing
|
||||
PEER->>RTC: 接收媒体流
|
||||
RTC->>STORE: onTrackEvent 回调
|
||||
STORE->>STORE: notify(REMOTE_STREAM_OBTAINED)
|
||||
STORE->>RENDER: 触发渲染更新
|
||||
RENDER->>UI: 显示远端视频
|
||||
```
|
||||
|
||||
## 5. 消息发送流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
autonumber
|
||||
participant USER as 👤 用户
|
||||
participant DOM as 🖱️ DOM
|
||||
participant CHAT as 💬 chatmessage.js
|
||||
participant STORE as 📦 store.js
|
||||
participant RTC as 🔗 renderstreaming.js
|
||||
participant PEER as 👤 远端用户
|
||||
|
||||
USER->>DOM: 输入消息并回车
|
||||
DOM->>CHAT: handleChatSubmit(event)
|
||||
CHAT->>CHAT: sendMessage()
|
||||
CHAT->>CHAT: addMessage(message)<br/>添加到本地列表
|
||||
CHAT->>STORE: getRenderStreaming()
|
||||
STORE-->>CHAT: 返回实例
|
||||
CHAT->>RTC: sendMessage()<br/>发送chat-message
|
||||
RTC->>PEER: 通过WebRTC数据通道发送
|
||||
|
||||
PEER->>RTC: 收到消息
|
||||
RTC->>STORE: onMessage 回调
|
||||
STORE->>CHAT: handleChatMessage(data)
|
||||
CHAT->>CHAT: addMessage()<br/>添加到消息列表
|
||||
CHAT->>RENDER: 更新聊天UI
|
||||
```
|
||||
|
||||
## 6. 页面流转状态图
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> 连接页面: 首次访问/无connectionId
|
||||
|
||||
连接页面: connect.html
|
||||
连接页面: - 输入连接ID
|
||||
连接页面: - 创建新通话
|
||||
连接页面: - 用户设置
|
||||
|
||||
通话页面: index.html
|
||||
通话页面: - 视频通话
|
||||
通话页面: - 聊天功能
|
||||
通话页面: - 媒体控制
|
||||
|
||||
结束页面: endcall.html
|
||||
结束页面: - 显示断开原因
|
||||
结束页面: - 重新连接选项
|
||||
|
||||
连接页面 --> 通话页面: 加入/创建通话
|
||||
通话页面 --> 结束页面: 通话结束/断开
|
||||
通话页面 --> 连接页面: 无有效connectionId
|
||||
结束页面 --> 通话页面: 重新连接
|
||||
结束页面 --> 连接页面: 返回
|
||||
```
|
||||
|
||||
## 7. 通话状态机
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
[*] --> idle: 初始化
|
||||
|
||||
idle: 空闲状态
|
||||
idle --> connecting: joinCall()<br/>加入通话
|
||||
idle --> connecting: createCall()<br/>创建通话
|
||||
|
||||
connecting: 连接中
|
||||
connecting --> ongoing: onConnect<br/>连接成功
|
||||
connecting --> failed: 连接失败
|
||||
|
||||
ongoing: 通话中
|
||||
ongoing --> ended: hangUp()<br/>挂断
|
||||
ongoing --> ended: endCall()<br/>结束通话
|
||||
|
||||
failed: 连接失败
|
||||
failed --> connecting: 重试连接
|
||||
failed --> ended: 放弃连接
|
||||
|
||||
ended: 已结束
|
||||
ended --> [*]: 清理资源
|
||||
```
|
||||
|
||||
## 8. 组件层次结构
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph index["index.html 组件结构"]
|
||||
HEADER["🧭 Header 顶部栏"]
|
||||
MAIN["📺 Main 主内容区"]
|
||||
FOOTER["🎛️ Footer 底部控制栏"]
|
||||
end
|
||||
|
||||
subgraph header["Header 内容"]
|
||||
TITLE["标题: 与xxx的通话"]
|
||||
NETWORK["网络质量指示"]
|
||||
DURATION["通话时长"]
|
||||
ENCRYPT["端到端加密标识"]
|
||||
end
|
||||
|
||||
subgraph main["Main 内容"]
|
||||
VIDEO_AREA["VideoArea 视频区"]
|
||||
SIDEBAR["Sidebar 侧边栏"]
|
||||
end
|
||||
|
||||
subgraph video["VideoArea"]
|
||||
REMOTE["RemoteVideo 远端视频"]
|
||||
LOCAL["LocalVideo 本地视频画中画"]
|
||||
end
|
||||
|
||||
subgraph sidebar["Sidebar"]
|
||||
USER_LIST["UserList 用户列表"]
|
||||
CHAT_MSG["ChatMessages 聊天消息"]
|
||||
CHAT_INPUT["ChatInput 消息输入"]
|
||||
end
|
||||
|
||||
subgraph footer["Footer 控制栏"]
|
||||
CTRL_MIC["🎤 麦克风"]
|
||||
CTRL_VIDEO["📹 视频"]
|
||||
CTRL_RECORD["🔴 录制"]
|
||||
CTRL_END["📞 结束通话"]
|
||||
CTRL_CHAT["💬 聊天"]
|
||||
end
|
||||
|
||||
HEADER --> TITLE
|
||||
HEADER --> NETWORK
|
||||
HEADER --> DURATION
|
||||
HEADER --> ENCRYPT
|
||||
|
||||
MAIN --> VIDEO_AREA
|
||||
MAIN --> SIDEBAR
|
||||
|
||||
VIDEO_AREA --> REMOTE
|
||||
VIDEO_AREA --> LOCAL
|
||||
|
||||
SIDEBAR --> USER_LIST
|
||||
SIDEBAR --> CHAT_MSG
|
||||
SIDEBAR --> CHAT_INPUT
|
||||
|
||||
FOOTER --> CTRL_MIC
|
||||
FOOTER --> CTRL_VIDEO
|
||||
FOOTER --> CTRL_RECORD
|
||||
FOOTER --> CTRL_END
|
||||
FOOTER --> CTRL_CHAT
|
||||
|
||||
style CTRL_END fill:#f66,stroke:#333,stroke-width:2px
|
||||
style ENCRYPT fill:#9f6,stroke:#333,stroke-width:2px
|
||||
```
|
||||
|
||||
## 9. 文件引用汇总
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph Import["导入关系"]
|
||||
direction TB
|
||||
M[main.js]
|
||||
S[store.js]
|
||||
R[renderer.js]
|
||||
C[chatmessage.js]
|
||||
CONN[connect.js]
|
||||
|
||||
M --> S
|
||||
M --> R
|
||||
M --> C
|
||||
S --> C
|
||||
R --> C
|
||||
CONN --> S
|
||||
end
|
||||
|
||||
subgraph Export["导出内容"]
|
||||
direction TB
|
||||
ST[store.js<br/>export default store]
|
||||
RE[renderer.js<br/>export default UIRenderer]
|
||||
UT[utils.js<br/>export { formatTime, showNotification, ... }]
|
||||
MO[models.js<br/>export { mockCallSession, mockMessages }]
|
||||
CH[chatmessage.js<br/>export { sendMessage, toggleSidebar, ... }]
|
||||
end
|
||||
```
|
||||
|
||||
## 10. API 接口调用图
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
subgraph Client["客户端"]
|
||||
CONN_JS[connect.js]
|
||||
end
|
||||
|
||||
subgraph Server["服务器"]
|
||||
API_CALL[/api/call/:callId]
|
||||
API_JOIN[/api/call/:callId/join]
|
||||
API_LEAVE[/api/call/:callId/leave]
|
||||
API_MEDIA[/api/call/:callId/media]
|
||||
API_MSG[/api/call/:callId/messages]
|
||||
API_SEND[/api/call/:callId/message]
|
||||
API_CONN[/signaling/connection-ids]
|
||||
API_UPLOAD[/api/upload/avatar]
|
||||
end
|
||||
|
||||
CONN_JS -.-> API_CONN
|
||||
CONN_JS -.-> API_UPLOAD
|
||||
|
||||
style API_CALL fill:#bbf,stroke:#333
|
||||
style API_JOIN fill:#bbf,stroke:#333
|
||||
style API_LEAVE fill:#bbf,stroke:#333
|
||||
style API_MEDIA fill:#bbf,stroke:#333
|
||||
style API_MSG fill:#bbf,stroke:#333
|
||||
style API_SEND fill:#bbf,stroke:#333
|
||||
style API_CONN fill:#fbf,stroke:#333
|
||||
style API_UPLOAD fill:#fbf,stroke:#333
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*文档生成时间: 2026-04-11*
|
||||
*适用于 OneByOne 视频通话应用*
|
||||
Reference in New Issue
Block a user