优化目录结构

This commit is contained in:
2026-05-25 20:37:36 +08:00
parent bbe7e71274
commit 40fd7f7e08
101 changed files with 108 additions and 110 deletions

View 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可以成为一个功能更加强大、用户体验更加出色的视频通话应用。

View 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
```

View 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 视频通话应用*