# 機能設計書 14-インボックスメンバー管理

## 概要

本ドキュメントは、Chatwootのインボックスメンバー管理機能について、その処理内容、データベース操作、およびコード構成を詳細に記述した設計書である。

### 本機能の処理概要

インボックスメンバー管理機能は、各インボックス（受信トレイ）に対してエージェントを割り当て、どのエージェントがどのインボックスの会話を処理できるかを制御する機能である。

**業務上の目的・背景**：カスタマーサポート組織では、チャネルごとに専門のエージェントを配置することが一般的である。例えば、メール対応専門のエージェント、SNS対応専門のエージェントなど、業務の専門性に応じたインボックスへのアサインが必要となる。また、エージェントがインボックスに追加された際にラウンドロビン（順番制）の自動割り当てキューに自動登録することで、公平な業務配分を実現する。

**機能の利用シーン**：
- 新規インボックス作成後のエージェント割り当て
- 既存インボックスへのエージェント追加・削除
- エージェントの担当インボックス変更
- インボックス設定画面でのメンバー管理

**主要な処理内容**：
1. InboxMemberモデルでのインボックス-ユーザー間の紐付け管理
2. インボックスへのメンバー追加時のラウンドロビンキュー登録
3. インボックスからのメンバー削除時のラウンドロビンキュー除去
4. インボックスのメンバー一覧取得
5. 一括でのメンバー追加・削除処理

**関連システム・外部連携**：
- Redis（ラウンドロビン自動割り当てキュー）
- AutoAssignment::InboxRoundRobinService

**権限による制御**：
- メンバー管理（追加・削除）はAdministratorロールのみ
- メンバー一覧の閲覧は割り当てられたインボックスへのアクセス権を持つユーザー

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 51 | インボックスエージェント追加 | 主画面 | インボックスへのエージェント追加 |
| 50 | インボックス設定画面 | 参照画面 | メンバー管理設定 |

## 機能種別

CRUD操作 / 自動割り当て連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| inbox_id | Integer | Yes | 対象インボックスID | 存在確認 |
| user_ids | Array | Yes | 割り当てるユーザーID配列 | 存在確認 |

### 入力データソース

- 画面入力（インボックス設定画面からの操作）
- API経由（REST API）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| agents | Array | インボックスに割り当てられたエージェント（User）の配列 |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ InboxMembersController経由でメンバー操作リクエスト受信

2. インボックス取得
   └─ Current.account.inboxes.find(inbox_id)

3. 認可チェック
   └─ InboxPolicyで操作者の権限を確認

4. メンバー操作実行
   ├─ Create: add_members呼び出し
   ├─ Update: add_members + remove_members
   └─ Destroy: remove_members呼び出し

5. ラウンドロビンキュー更新（コールバック）
   ├─ 追加時: add_agent_to_round_robin
   └─ 削除時: remove_agent_from_round_robin

6. レスポンス返却
   └─ 更新後のエージェント一覧をJSON形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[メンバー操作リクエスト] --> B[インボックス取得]
    B --> C{認可チェック}
    C -->|権限あり| D{操作種別}
    C -->|権限なし| E[403エラー]
    D -->|Show| F[メンバー一覧取得]
    D -->|Create| G[add_members実行]
    D -->|Update| H[add + remove実行]
    D -->|Destroy| I[remove_members実行]
    G --> J[InboxMember作成]
    J --> K[ラウンドロビンキュー追加]
    H --> L[差分計算]
    L --> J
    L --> M[InboxMember削除]
    I --> M
    M --> N[ラウンドロビンキュー削除]
    K --> O[レスポンス返却]
    N --> O
    F --> O
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | ユニーク制約 | 同一インボックスに同一ユーザーは1回のみ割り当て可能 | メンバー追加時 |
| BR-002 | 自動キュー登録 | メンバー追加時にラウンドロビンキューに自動登録 | メンバー追加時 |
| BR-003 | 自動キュー削除 | メンバー削除時にラウンドロビンキューから自動削除 | メンバー削除時 |
| BR-004 | インボックス存在確認 | 削除時はインボックスの存在を確認してからキュー操作 | メンバー削除時 |

### 計算ロジック

**メンバー更新時の差分計算**:
- 追加対象: `params[:user_ids] - @current_agents_ids`
- 削除対象: `@current_agents_ids - params[:user_ids]`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| メンバー一覧取得 | inbox_members | SELECT | インボックスのメンバー一覧取得 |
| メンバー追加 | inbox_members | INSERT | インボックスメンバーレコード作成 |
| メンバー削除 | inbox_members | DELETE | インボックスメンバーレコード削除 |

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

#### inbox_members

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | inbox_id | リクエストパラメータ | 必須 |
| INSERT | user_id | リクエストパラメータ | 必須、インボックス内でユニーク |
| SELECT | * | inbox_id条件 | - |
| DELETE | - | inbox_id, user_id条件 | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | 操作権限がない | 権限確認メッセージ表示 |
| 404 | NotFound | 指定インボックスが存在しない | 対象確認メッセージ表示 |
| 422 | UnprocessableEntity | バリデーションエラー（重複追加等） | エラー内容を表示 |

### リトライ仕様

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

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

- メンバー追加・削除はActiveRecord::Base.transactionでラップされている
- ラウンドロビンキュー操作はRedis操作のため、トランザクション対象外（after_create/after_destroyコールバック）

## パフォーマンス要件

- メンバー一覧取得: 100ms以内のレスポンス
- 複合インデックス（inbox_id, user_id）による高速検索

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

- InboxPolicyによる認可制御
- 監査ログ（Enterprise機能）による操作追跡

## 備考

- InboxMember.include_mod_with('Audit::InboxMember') により監査ログ機能が拡張される
- Inbox.add_members / Inbox.remove_members メソッドで一括操作が可能

---

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

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

### 推奨読解順序

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

InboxMemberモデルのスキーマとコールバックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | inbox_member.rb | `app/models/inbox_member.rb` | スキーマ、バリデーション、コールバック |

**読解のコツ**:
- `validates :user_id, uniqueness: { scope: :inbox_id }` でユニーク制約を確認
- `after_create :add_agent_to_round_robin` でラウンドロビンキュー自動登録を確認
- `after_destroy :remove_agent_from_round_robin` でキュー自動削除を確認

**主要処理フロー**:
1. **17-37行目**: InboxMemberクラス全体の定義
2. **18-20行目**: バリデーション（inbox_id, user_id必須、ユニーク制約）
3. **22-23行目**: アソシエーション（belongs_to :user, :inbox）
4. **25-26行目**: コールバック定義
5. **30-32行目**: add_agent_to_round_robin - ラウンドロビンキュー追加
6. **34-36行目**: remove_agent_from_round_robin - ラウンドロビンキュー削除

#### Step 2: Inboxモデルのメンバー操作メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | inbox.rb | `app/models/inbox.rb` | add_members, remove_members メソッド |

**主要処理フロー**:
1. **90-93行目**: add_members - inbox_members.create!で一括追加
2. **98-101行目**: remove_members - inbox_members.where.destroy_allで一括削除

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

コントローラでのメンバー操作の入り口を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | inbox_members_controller.rb | `app/controllers/api/v1/accounts/inbox_members_controller.rb` | メンバー管理API |

**主要処理フロー**:
1. **5-8行目**: show - メンバー一覧取得
2. **10-16行目**: create - メンバー追加（トランザクション内）
3. **18-22行目**: update - メンバー一括更新
4. **24-30行目**: destroy - メンバー削除
5. **38-46行目**: update_agents_list - 差分計算で追加・削除
6. **49-55行目**: agents_to_be_added_ids / agents_to_be_removed_ids - 差分計算

#### Step 4: 認可ポリシーを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | inbox_policy.rb | `app/policies/inbox_policy.rb` | インボックス操作の認可ルール |

**主要処理フロー**:
- `show?`: 割り当てられたインボックスへのアクセス権チェック
- `create?`, `update?`, `destroy?`: Administrator権限チェック

#### Step 5: ラウンドロビンサービスを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | inbox_round_robin_service.rb | `app/services/auto_assignment/inbox_round_robin_service.rb` | ラウンドロビンキュー操作 |

**主要処理フロー**:
- `add_agent_to_queue(user_id)` - Redisキューへのエージェント追加
- `remove_agent_from_queue(user_id)` - Redisキューからのエージェント削除

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

```
Api::V1::Accounts::InboxMembersController
    │
    ├─ InboxPolicy (認可チェック)
    │      └─ show?, create?, update?, destroy?
    │
    └─ Inbox (モデル操作)
           ├─ add_members(user_ids)
           │      └─ inbox_members.create!
           │             └─ after_create: add_agent_to_round_robin
           │                    └─ InboxRoundRobinService.add_agent_to_queue
           │
           └─ remove_members(user_ids)
                  └─ inbox_members.destroy_all
                         └─ after_destroy: remove_agent_from_round_robin
                                └─ InboxRoundRobinService.remove_agent_from_queue
```

### データフロー図

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

APIリクエスト ────────▶ InboxMembersController ────────▶ JSONレスポンス
(user_ids)                    │                           (agents配列)
                              ▼
                         InboxPolicy
                        (認可チェック)
                              │
                              ▼
                    Inbox.add_members / remove_members
                              │
                              ├───────▶ inbox_members テーブル
                              │
                              └───────▶ InboxRoundRobinService
                                              │
                                              ▼
                                         Redis キュー
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| inbox_member.rb | `app/models/inbox_member.rb` | モデル | インボックスメンバー中間テーブル |
| inbox.rb | `app/models/inbox.rb` | モデル | インボックスモデル（add_members, remove_members） |
| inbox_members_controller.rb | `app/controllers/api/v1/accounts/inbox_members_controller.rb` | コントローラ | メンバー管理API |
| inbox_policy.rb | `app/policies/inbox_policy.rb` | ポリシー | インボックス操作の認可 |
| inbox_round_robin_service.rb | `app/services/auto_assignment/inbox_round_robin_service.rb` | サービス | ラウンドロビンキュー操作 |
