# 機能設計書 60-ユーザー情報更新

## 概要

本ドキュメントは、Etherpadにおけるユーザー情報更新機能の設計仕様を記載する。ユーザー名や色などの情報更新を同期する機能について詳細に解説する。

### 本機能の処理概要

ユーザー情報更新機能は、ユーザーが自分の表示名やカラーを変更した際に、その変更を同一パッドの他のユーザーにリアルタイムで同期し、データベースに永続化する機能を提供する。

**業務上の目的・背景**：
共同編集において、各ユーザーを識別することは重要である。ユーザー名により「誰が何を編集しているか」が明確になり、カラーによりテキスト上でどの部分を誰が編集したかが視覚的に分かる。ユーザーはいつでもこれらの情報を変更でき、変更は即座に他の参加者に反映される必要がある。

**機能の利用シーン**：
- ユーザーが最初にパッドに参加した際に名前を設定
- ユーザーが表示名を変更する場合
- ユーザーが自分のカラーを変更する場合
- 匿名ユーザーに名前を提案する場合（suggestUserName）

**主要な処理内容**：
1. クライアントからUSERINFO_UPDATEメッセージを受信
2. カラーIDの形式を検証（#RRGGBB）
3. AuthorManagerで著者情報を更新
4. 同一パッドの他ユーザーにUSER_NEWINFOをブロードキャスト

**関連システム・外部連携**：
- Socket.IO: WebSocket経由のリアルタイム通信
- AuthorManager: 著者情報の永続化

**権限による制御**：
認証済みユーザーのみが自分の情報を更新可能。他ユーザーの情報は更新不可。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | パッド編集画面 | 主機能 | ユーザー名や色などの情報変更を同期 |

## 機能種別

リアルタイム通信 / ユーザー管理 / データ永続化

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| type | string | Yes | メッセージタイプ（COLLABROOM） | 固定値 |
| data.type | string | Yes | データタイプ（USERINFO_UPDATE） | 固定値 |
| data.userInfo.name | string | No | 新しいユーザー名 | 空文字列の場合はnull扱い |
| data.userInfo.colorId | string | Yes | 新しいカラー | #RRGGBB形式必須 |

### 入力データソース

- Socket.IOメッセージ（クライアントからのWebSocket通信）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| type | string | COLLABROOM |
| data.type | string | USER_NEWINFO |
| data.userInfo.userId | string | 更新したユーザーの著者ID |
| data.userInfo.name | string | 新しいユーザー名 |
| data.userInfo.colorId | string | 新しいカラー |

### 出力先

- Socket.IO経由で同一パッドの他クライアントにブロードキャスト
- AuthorManager経由でデータベースに永続化

## 処理フロー

### 処理シーケンス

```
1. USERINFO_UPDATEメッセージ受信
   └─ handleMessage内で振り分け
2. colorId検証
   └─ #RRGGBB形式の正規表現チェック
3. name処理
   └─ 空文字列の場合はnullに変換
4. セッション検証
   └─ author、padIdの存在確認
5. 著者情報更新（並列実行）
   ├─ authorManager.setAuthorColorId
   └─ authorManager.setAuthorName
6. USER_NEWINFOメッセージ構築
7. 他クライアントにブロードキャスト
   └─ socket.broadcast.to(padId).emit
8. 更新完了待機
   └─ Promise.allで並列処理完了を待機
```

### フローチャート

```mermaid
flowchart TD
    A[USERINFO_UPDATEメッセージ受信] --> B{colorIdあり?}
    B -->|No| Z1[エラー: missing colorId]
    B -->|Yes| C{colorId形式正しい?}
    C -->|No| Z2[エラー: malformed color]
    C -->|Yes| D{name空文字?}
    D -->|Yes| E[name = null]
    D -->|No| F[nameそのまま]
    E --> G{セッション有効?}
    F --> G
    G -->|No| Z3[エラー: session not ready]
    G -->|Yes| H[Promise.all開始]
    H --> I[setAuthorColorId]
    H --> J[setAuthorName]
    I --> K[USER_NEWINFO構築]
    J --> K
    K --> L[broadcast.to.emit]
    L --> M[Promise.all待機]
    M --> N[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-60-01 | カラー形式必須 | colorIdは#RRGGBB形式（大文字小文字不問）必須 | 常時 |
| BR-60-02 | 空名前処理 | 空文字列の名前はnullとして保存 | name指定時 |
| BR-60-03 | 自己更新のみ | ユーザーは自分の情報のみ更新可能 | 常時 |
| BR-60-04 | 永続化必須 | 更新された情報はデータベースに永続化 | 常時 |

### 計算ロジック

カラー形式検証の正規表現:
```javascript
/(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i
```
- #で始まる
- 6桁または3桁の16進数
- 大文字小文字不問

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カラー更新 | globalAuthor:{authorId} | UPDATE | colorId更新 |
| 名前更新 | globalAuthor:{authorId} | UPDATE | name更新 |

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

#### globalAuthor:{authorId}

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | colorId | userInfo.colorId | #RRGGBB形式 |
| UPDATE | name | userInfo.name | nullの場合は空として保存 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| missing colorId | パラメータ不正 | colorIdがnull/undefined | クライアント実装確認 |
| malformed color | 形式不正 | colorIdが#RRGGBB形式でない | 正しい形式で再送信 |
| session not ready | セッション不正 | author/padIdがない | 再接続が必要 |

### リトライ仕様

自動リトライは実装されていない。エラー発生時はクライアントで再試行が必要。

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

setAuthorColorIdとsetAuthorNameは並列実行されるが、それぞれ独立したDB操作。一方が失敗しても他方は実行される。

## パフォーマンス要件

- 著者情報更新はPromise.allで並列実行
- ブロードキャストはDB更新完了前に実行可能
- 更新完了はawait pで保証

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

- colorIdの形式検証によるインジェクション防止
- セッション検証による不正更新防止
- 自分の著者IDのみ更新可能（他ユーザーの情報は更新不可）

## 備考

- suggestUserName機能も関連（他ユーザーに名前を提案）
- 名前提案はクライアント間で直接やり取り

---

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

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

### 推奨読解順序

#### Step 1: メッセージハンドラを理解する

USERINFO_UPDATEメッセージの処理フローを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | handleMessage内のUSERINFO_UPDATE振り分け |

**主要処理フロー**:
- **408行目**: USERINFO_UPDATEの場合、handleUserInfoUpdateを呼び出し

#### Step 2: handleUserInfoUpdate関数を詳細に理解する

ユーザー情報更新の核心処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | handleUserInfoUpdate関数（579-611行目） |

**主要処理フロー**:
- **579行目**: 関数定義、name/colorIdのデストラクチャリング
- **580行目**: colorIdのnullチェック
- **581行目**: nameが空文字列の場合はnullに変換
- **582-583行目**: セッション検証
- **585-586行目**: カラー形式の正規表現検証
- **590-593行目**: Promise.allで並列更新
  - authorManager.setAuthorColorId
  - authorManager.setAuthorName
- **597-603行目**: USER_NEWINFOメッセージ構築
- **607行目**: 他クライアントにブロードキャスト
- **610行目**: DB更新完了待機

#### Step 3: AuthorManagerを理解する

著者情報の永続化処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AuthorManager.ts | `src/node/db/AuthorManager.ts` | setAuthorName、setAuthorColorId |

**読解のコツ**: AuthorManagerはglobalAuthor:{authorId}キーで著者情報を管理。setメソッドはdb.setで直接更新。

#### Step 4: suggestUserName機能を理解する

名前提案機能を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | handleSuggestUserName関数（559-571行目） |

**主要処理フロー**:
- **560行目**: newName、unnamedIdを取得
- **565-570行目**: unnamedIdに該当するソケットを探してメッセージ転送

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

```
Socket.IO message受信
    │
    └─ handleMessage (PadMessageHandler.ts:273)
           │
           └─ [USERINFO_UPDATE] (408)
                  │
                  └─ handleUserInfoUpdate (579)
                         │
                         ├─ colorId検証 (585)
                         │
                         ├─ Promise.all
                         │      ├─ authorManager.setAuthorColorId (591)
                         │      │      └─ db.set(globalAuthor:{id})
                         │      │
                         │      └─ authorManager.setAuthorName (592)
                         │             └─ db.set(globalAuthor:{id})
                         │
                         ├─ USER_NEWINFO構築 (597-603)
                         │
                         └─ socket.broadcast.emit (607)
```

### データフロー図

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

クライアントA        ┌─────────────────┐
USERINFO_UPDATE ───▶│  handleMessage  │
(name, colorId)     │  振り分け       │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  colorId検証    │
                    │  (#RRGGBB)      │
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  Promise.all    │
                    │  ┌───────────┐  │
                    │  │setColorId │──┼──▶ Database
                    │  └───────────┘  │    (globalAuthor)
                    │  ┌───────────┐  │
                    │  │setName    │──┼──▶ Database
                    │  └───────────┘  │    (globalAuthor)
                    └────────┬────────┘
                             │
                             ▼
                    ┌─────────────────┐
                    │  USER_NEWINFO   │
                    │  ブロードキャスト │ ───────▶ 他クライアント
                    └─────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PadMessageHandler.ts | `src/node/handler/PadMessageHandler.ts` | ソース | メッセージ処理・ユーザー情報更新 |
| AuthorManager.ts | `src/node/db/AuthorManager.ts` | ソース | 著者情報管理・永続化 |
| SocketIORouter.ts | `src/node/handler/SocketIORouter.ts` | ソース | Socket.IOルーティング |
| DB.ts | `src/node/db/DB.ts` | ソース | データベースアクセス |
