# 機能設計書 167-Widgetメンバー

## 概要

本ドキュメントは、Chatwoot WebウィジェットにおけるWidgetメンバー（インボックスメンバー）表示機能の設計を記載する。この機能はエンドユーザー（訪問者）がWebサイト上のチャットウィジェットでインボックスに所属するエージェント一覧を取得するためのAPIを提供する。

### 本機能の処理概要

Widgetメンバー機能は、Webサイト訪問者に対してインボックスに所属するエージェント（サポートメンバー）の一覧を提供する機能である。これにより、訪問者は対応可能なエージェントの存在や、オンラインステータスを確認することができる。

**業務上の目的・背景**：Webサイト訪問者に対して、サポートチームの存在と可用性を表示することで、安心感を提供し、コミュニケーションへの期待値を適切に設定するため、エージェント一覧の取得機能が必要である。これにより、訪問者は「誰が対応してくれるか」「今対応可能な人がいるか」を知ることができる。

**機能の利用シーン**：
- チャットウィジェットを開いた際にオンラインエージェントを表示する場面
- 「○○さんがサポートします」のようなメッセージを表示する場面
- エージェントのアバターをウィジェット上に表示する場面
- オンライン/オフラインステータスを訪問者に提示する場面

**主要な処理内容**：
1. インボックスメンバー一覧の取得（index）

**関連システム・外部連携**：
- ActiveStorage（アバター画像）
- AccountUser（可用性ステータス）

**権限による制御**：
- website_tokenによるウィジェット認証が必要

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 114 | ホーム画面 | 主画面 | エージェント情報の表示 |
| 116 | メッセージ画面 | 参照画面 | 会話中のエージェント情報表示 |

## 機能種別

データ取得（Read Only）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| website_token | String | Yes | ウェブウィジェットの識別トークン | 有効なトークンである必要がある |

### 入力データソース

- Webウィジェット（JavaScript SDK）からのHTTPリクエスト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| payload | Array | エージェント一覧 |
| payload[].id | Integer | ユーザーID |
| payload[].name | String | エージェントの表示名（available_name） |
| payload[].avatar_url | String | アバター画像URL |
| payload[].availability_status | String | 可用性ステータス（online/offline/busy） |

### 出力先

- JSON形式のHTTPレスポンス

## 処理フロー

### 処理シーケンス

```
1. インボックスメンバー取得（index）
   └─ インボックスのinbox_membersを取得
   └─ userとavatar_attachmentをinclude
   └─ 各メンバーの情報をJSON形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[インボックスメンバー取得]
    B --> C[user情報をinclude]
    C --> D[avatar_attachmentをinclude]
    D --> E[レスポンス生成]
    E --> F[JSON返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-167-01 | ユーザー存在チェック | ユーザーがblankの場合はスキップ | レスポンス生成時 |
| BR-167-02 | 表示名の使用 | available_nameを使用（プライバシー保護） | レスポンス生成時 |
| BR-167-03 | 可用性ステータス | 現在のアカウントにおけるステータスを返却 | レスポンス生成時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| index | inbox_members | SELECT | インボックスメンバー取得 |
| index | users | SELECT | ユーザー情報取得（includes） |
| index | active_storage_attachments | SELECT | アバター取得（includes） |
| index | active_storage_blobs | SELECT | アバターブロブ取得（includes） |
| index | account_users | SELECT | 可用性ステータス取得 |

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

#### inbox_members

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | - | inbox_id = @web_widget.inbox.id | インボックス条件 |

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id | inbox_member.user_id | ユーザーID |
| SELECT | available_name | - | 表示名 |
| SELECT | avatar | - | ActiveStorage添付 |

#### account_users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | availability_status | account_id = @current_account.id | 可用性ステータス |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | Unauthorized | website_tokenが無効 | 正しいトークンを使用 |

### リトライ仕様

特になし（同期処理のため）

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

読み取り専用のため、トランザクション制御は不要

## パフォーマンス要件

- レスポンス時間: 200ms以内

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

- website_tokenによるウィジェット認証
- 連絡先情報のset_contactをスキップ（skip_before_action）
- available_nameを使用してプライバシーを保護

## 備考

- set_contactがスキップされているため、連絡先が不要なAPIである
- ユーザーがblank（削除済み等）の場合はレスポンスから除外される
- availability_statusは現在のアカウントにおける状態を返却
- avatar_urlはActiveStorageのURLを返却

---

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

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

### 推奨読解順序

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

インボックスメンバーに関連するデータモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | inbox_member.rb | `app/models/inbox_member.rb` | インボックスメンバーモデル |
| 1-2 | user.rb | `app/models/user.rb` | ユーザーモデル、available_name |
| 1-3 | account_user.rb | `app/models/account_user.rb` | 可用性ステータス（availability_status） |

**読解のコツ**: availability_statusのenum定義（online, offline, busy）を確認すること。

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

処理の起点となるコントローラーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | inbox_members_controller.rb | `app/controllers/api/v1/widget/inbox_members_controller.rb` | インボックスメンバー取得の実装 |

**主要処理フロー**:
1. **2行目**: skip_before_action :set_contact（連絡先設定をスキップ）
2. **4-6行目**: index処理（includes付きでinbox_members取得）

#### Step 3: ビュー（レスポンス形式）を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.json.jbuilder | `app/views/api/v1/widget/inbox_members/index.json.jbuilder` | レスポンス形式 |

**主要処理フロー**:
- **3行目**: user.blank?のチェック（next if）
- **5-8行目**: id, name, avatar_url, availability_statusの出力

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

```
Api::V1::Widget::InboxMembersController
    │
    ├─ BaseController (認証・共通処理)
    │      └─ set_web_widget (set_contactはスキップ)
    │
    └─ @web_widget.inbox.inbox_members.includes(...)
           │
           ├─ user (ユーザー情報)
           │      └─ available_name
           │      └─ avatar_url
           │
           └─ user.account_users.find_by(account_id:)
                  └─ availability_status
```

### データフロー図

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

website_token ────────▶ BaseController#set_web_widget ────▶ @web_widget
                              │
                        inbox.inbox_members.includes ─────▶ @inbox_members
                              │
                        [user存在チェック] ───────────────▶ フィルタリング
                              │
                        user.available_name ──────────────▶ name
                        user.avatar_url ──────────────────▶ avatar_url
                        account_user.availability_status ─▶ availability_status
                              │
                        JSON Response ────────────────────▶ クライアント
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| inbox_members_controller.rb | `app/controllers/api/v1/widget/inbox_members_controller.rb` | ソース | Widgetメンバーのメインコントローラー |
| base_controller.rb | `app/controllers/api/v1/widget/base_controller.rb` | ソース | Widget APIの基底コントローラー |
| inbox_member.rb | `app/models/inbox_member.rb` | ソース | インボックスメンバーモデル |
| user.rb | `app/models/user.rb` | ソース | ユーザーモデル |
| account_user.rb | `app/models/account_user.rb` | ソース | アカウントユーザーモデル（可用性） |
| index.json.jbuilder | `app/views/api/v1/widget/inbox_members/index.json.jbuilder` | テンプレート | メンバー一覧レスポンス |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（411行目） |
