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