# 機能設計書 25-アクティブユーザー数取得

## 概要

本ドキュメントは、Etherpadにおけるアクティブユーザー数取得機能の設計を定義するものである。

### 本機能の処理概要

この機能は、指定されたパッドを現在編集中（接続中）のユーザー数を取得する機能である。Socket.IOを通じてパッドに接続しているクライアント数をリアルタイムでカウントして返却する。

**業務上の目的・背景**：リアルタイムコラボレーションにおいて、現在何人のユーザーがパッドを編集しているかを把握することは重要である。パッド編集画面では接続ユーザー数を表示し、管理画面ではパッドの活動状況を示す指標として使用される。また、サーバー負荷の把握やキャパシティプランニングにも活用できる。

**機能の利用シーン**：
- パッド編集画面での接続ユーザー数表示
- パッド管理画面でのアクティブユーザー数表示
- サーバー監視ダッシュボードでの同時編集者数確認
- 負荷分散やスケーリング判断の材料
- 会議やワークショップでの参加者数確認

**主要な処理内容**：
1. リクエストパラメータのバリデーション（padID）
2. Socket.IOのルーム（パッドID）に接続しているソケット数をカウント
3. ユーザー数をJSON形式で返却

**関連システム・外部連携**：
- Socket.IOサーバーのルーム情報を参照
- REST API（`GET /api/2/pads/usersCount`）として外部システムから呼び出し可能

**権限による制御**：API認証（APIキーまたはOAuth2トークン）が必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | パッド編集画面 | API連携 | 接続ユーザー数の表示 |
| 10 | パッド管理画面 | 主機能 | パッド一覧にアクティブユーザー数を表示 |

## 機能種別

データ取得（リアルタイム情報）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| padID | string | Yes | パッドの識別子 | 文字列型であること |

### 入力データソース

- HTTPリクエストのクエリパラメータ（GETリクエスト）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| code | integer | レスポンスコード（0: 成功） |
| message | string | レスポンスメッセージ（"ok"） |
| data.padUsersCount | integer | パッドに接続中のユーザー数 |

### 出力先

- HTTPレスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. APIリクエストの受信
   └─ GET /api/2/pads/usersCount?padID=xxx

2. パラメータの取得
   └─ padIDを取得

3. ルームソケット数の取得
   └─ _getRoomSockets(padID).lengthでソケット数を取得

4. レスポンスの返却
   └─ {padUsersCount: number}形式でJSON返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[リクエスト受信]
    B --> C[padID取得]
    C --> D[_getRoomSockets実行]
    D --> E[ソケット数をカウント]
    E --> F[ユーザー数を返却]
    F --> G[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-25-01 | リアルタイム計測 | 結果はAPI呼び出し時点の接続数 | 常に適用 |
| BR-25-02 | ゼロユーザー | 誰も接続していない場合は0を返却 | 常に適用 |
| BR-25-03 | パッド存在非依存 | パッドが存在しなくても0を返却（エラーにならない） | 常に適用 |

### 計算ロジック

`padUsersCount`関数は、`_getRoomSockets(padID).length`を直接返却する。`_getRoomSockets`はSocket.IOのルーム（パッドID）に接続しているソケットの配列を返す。

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| なし | - | - | データベースアクセスなし（メモリ上のSocket.IO情報を参照） |

### テーブル別操作詳細

データベース操作は発生しない。Socket.IOサーバーのインメモリ情報を直接参照する。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 本機能は通常エラーを発生させない | - |

### リトライ仕様

特になし

## トランザクション仕様

データベースアクセスがないためトランザクション管理は不要。

## パフォーマンス要件

- レスポンス時間: 通常10ms以内
- Socket.IOのインメモリ情報を直接参照するため非常に高速

## セキュリティ考慮事項

- API認証（APIキーまたはOAuth2トークン）が必須
- ユーザー数のみを返却し、個人を特定する情報は含まない

## 備考

- パッドが存在しない場合でも0が返却される（パッド存在チェックは行わない）
- リアルタイム性が重要な場合はSocket.IO経由での通知を推奨

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | sessioninfos構造（94-95行目） |

**読解のコツ**: `sessioninfos`はSocket.IDをキーとしたセッション情報のマップ。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | RestAPI.ts | `src/node/handler/RestAPI.ts` | `/pads/usersCount`エンドポイント定義（752-766行目）|
| 2-2 | APIHandler.ts | `src/node/handler/APIHandler.ts` | padUsersCount関数のパラメータ定義（61行目）|

**主要処理フロー**:
1. **752-766行目** (RestAPI.ts): エンドポイント定義とレスポンス形式
2. **61行目** (APIHandler.ts): `padUsersCount: ['padID']`

#### Step 3: API関数の実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | API.ts | `src/node/db/API.ts` | padUsersCount関数のエクスポート（66行目）|

**主要処理フロー**:
- **66行目**: `exports.padUsersCount = padMessageHandler.padUsersCount;`（委譲）

#### Step 4: 実装詳細を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | padUsersCount関数（1327-1329行目）|
| 4-2 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | _getRoomSockets関数（1310-1322行目）|

**主要処理フロー**:
- **1327-1329行目**: `_getRoomSockets(padID).length`を返却
- **1310-1322行目**: Socket.IOルームからソケット配列を取得

### プログラム呼び出し階層図

```
RestAPI.expressCreateServer (RestAPI.ts)
    │
    ├─ Express Router: GET /api/2/pads/usersCount
    │      │
    │      └─ APIHandler.handle (APIHandler.ts)
    │             │
    │             └─ API.padUsersCount (API.ts:66)
    │                    │
    │                    └─ padMessageHandler.padUsersCount (PadMessageHandler.ts:1327-1329)
    │                           │
    │                           └─ _getRoomSockets (PadMessageHandler.ts:1310-1322)
    │                                  │
    │                                  └─ socketio.sockets.adapter.rooms.get(padID)
```

### データフロー図

```
[入力]                    [処理]                         [出力]

padID ─────────────────> _getRoomSockets ────────────> Socket Array
                              │
                              v
                    socketio.adapter.rooms.get(padID)
                              │
                              v
                    Array.from(room).map → socket配列
                              │
                              v
                    ───────────────────────────────> {padUsersCount: length}
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| API.ts | `src/node/db/API.ts` | ソース | padUsersCount関数のエクスポート |
| APIHandler.ts | `src/node/handler/APIHandler.ts` | ソース | APIバージョン管理とパラメータ定義 |
| RestAPI.ts | `src/node/handler/RestAPI.ts` | ソース | RESTエンドポイント定義 |
| PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | ソース | padUsersCount実装と_getRoomSockets |
| pad.ts | `src/tests/backend/specs/api/pad.ts` | テスト | APIテストケース（266-272行目） |
