# 機能設計書 112-ラベル別レポート

## 概要

本ドキュメントは、Chatwootのラベル別レポート機能の設計を記述する。この機能は、会話に付与されたラベルごとのパフォーマンスメトリクスを集計・分析し、ラベルベースでのサポート業務分析を支援するレポーティング機能である。

### 本機能の処理概要

ラベル別レポート機能は、アカウント内で定義されたラベルごとに、そのラベルが付与された会話の統計情報（会話数、解決数、平均応答時間、平均解決時間、平均返信時間）を集計し、ラベルによる分類でサポート業務のパフォーマンスを可視化する機能である。

**業務上の目的・背景**：カスタマーサポート業務では、問い合わせの種類や優先度をラベルで分類することが一般的である。例えば「バグ報告」「機能リクエスト」「請求関連」などのラベルを使用して会話を分類している場合、ラベル別レポートによってどの種類の問い合わせが多いか、どの種類の対応に時間がかかっているかを分析できる。これにより、リソース配分の最適化やFAQ・ナレッジベースの充実すべき領域の特定に活用できる。

**機能の利用シーン**：
- 問い合わせ種別（ラベル）ごとの対応状況分析
- 特定ラベル（例：「緊急」「VIP」）の対応品質モニタリング
- 製品カテゴリ別の問い合わせ傾向分析
- CSV出力による外部ツールでの詳細分析

**主要な処理内容**：
1. アカウント内の全ラベルを取得
2. 各ラベルに紐づく会話数をTaggingsテーブル経由で集計
3. ReportingEventテーブルから解決数・平均応答時間・平均解決時間・平均返信時間を算出
4. 営業時間ベースの計算オプションに対応
5. JSON形式またはCSV形式でレポートデータを出力

**関連システム・外部連携**：ActsAsTaggableOnジェムを使用したラベル（タグ）管理機能と連携

**権限による制御**：管理者（Administrator）ロールを持つユーザーのみがレポートを閲覧可能（ReportPolicy#view?で制御）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 39 | ラベルレポート画面 | 主機能 | ラベル別統計の表示 |
| 40 | ラベルレポート概要 | 主機能 | ラベルレポート概要一覧 |
| 41 | ラベルレポート詳細 | 主機能 | 特定ラベルの詳細レポート |

## 機能種別

データ集計・レポート出力

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| since | String | Yes | 集計開始日時（UNIXタイムスタンプ文字列） | 数値変換可能な文字列 |
| until | String | Yes | 集計終了日時（UNIXタイムスタンプ文字列） | 数値変換可能な文字列 |
| business_hours | Boolean | No | 営業時間ベースで計算するか | true/false |
| timezone_offset | Float | No | タイムゾーンオフセット | 数値 |

### 入力データソース

- HTTPリクエストパラメータ（API呼び出し時）
- Current.account（認証済みユーザーのアカウント情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | ラベルID |
| name | String | ラベル名（title） |
| conversations_count | Integer | 期間内の会話数 |
| resolved_conversations_count | Integer | 期間内の解決会話数 |
| avg_first_response_time | Float | 平均初回応答時間（秒） |
| avg_resolution_time | Float | 平均解決時間（秒） |
| avg_reply_time | Float | 平均返信時間（秒） |

### 出力先

- JSON形式：SummaryReportsコントローラー（内部利用）
- CSV形式：ダウンロード用ファイル（`labels_report.csv`）

## 処理フロー

### 処理シーケンス

```
1. API呼び出し（GET /api/v2/accounts/:account_id/reports/labels）
   └─ ReportsController#labels アクションが起動

2. 認可チェック
   └─ ReportPolicy#view? で管理者権限を確認

3. レポートデータ生成
   └─ ReportsHelper#generate_labels_report を呼び出し
       └─ LabelSummaryBuilder でデータを集計

4. ラベル一覧の取得
   └─ account.labels から全ラベルを取得

5. 会話数の集計
   └─ ActsAsTaggableOn::Tagging経由でラベル別会話数をカウント

6. 解決数の集計
   └─ ReportingEventとTaggingsをJOINして解決イベント数をカウント

7. メトリクス算出
   └─ 平均応答時間、平均解決時間、平均返信時間を計算

8. ラベル一覧との結合
   └─ 各ラベルの名前と統計値をマッピング

9. CSV出力
   └─ レポートデータをCSV形式でレンダリング
```

### フローチャート

```mermaid
flowchart TD
    A[API呼び出し] --> B{認可チェック}
    B -->|権限あり| C[LabelSummaryBuilder初期化]
    B -->|権限なし| Z[403 Forbidden]
    C --> D[ラベル一覧を取得]
    D --> E{ラベルが存在?}
    E -->|なし| F[空配列を返却]
    E -->|あり| G[collect_report_data]
    G --> H[会話数を集計]
    H --> I[解決数を集計]
    I --> J[各種メトリクスを算出]
    J --> K[ラベル別にレポート構築]
    K --> L[CSV形式でレンダリング]
    L --> M[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-112-01 | 期間指定必須 | since/untilパラメータは必須 | 常時 |
| BR-112-02 | 営業時間計算 | business_hours=trueの場合、value_in_business_hoursカラムを使用 | business_hoursパラメータがtrue |
| BR-112-03 | 全ラベル対象 | アカウント内の全ラベルを対象に集計 | 常時 |
| BR-112-04 | Taggableコンテキスト | context='labels'のタギングのみ対象 | 常時 |

### 計算ロジック

**会話数の計算**：
```sql
SELECT tags.name, COUNT(taggings.*)
FROM taggings
INNER JOIN conversations ON taggings.taggable_id = conversations.id
INNER JOIN tags ON taggings.tag_id = tags.id
WHERE taggable_type = 'Conversation'
  AND context = 'labels'
  AND conversations.account_id = :account_id
  AND conversations.created_at BETWEEN :since AND :until
GROUP BY tags.name
```

**平均時間の計算**：
- 通常モード：`AVG(reporting_events.value)`
- 営業時間モード：`AVG(reporting_events.value_in_business_hours)`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ラベル取得 | labels | SELECT | アカウント内の全ラベルを取得 |
| 会話数集計 | taggings, conversations, tags | SELECT | ラベル別会話数をカウント |
| 解決数集計 | reporting_events, conversations, taggings, tags | SELECT | ラベル別解決イベント数をカウント |
| メトリクス集計 | reporting_events, conversations, taggings, tags | SELECT | 各種平均時間を算出 |

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

#### taggings (ActsAsTaggableOn)

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | tag_id, taggable_id | taggable_type = 'Conversation', context = 'labels' | 会話とラベルの関連 |

#### reporting_events

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | AVG(value), COUNT(*) | JOINでラベル別に集計 | イベント種別ごとに集計 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | Forbidden | 管理者権限がない場合 | 権限のあるユーザーでログイン |
| 422 | Unprocessable Entity | パラメータ不正 | パラメータを確認して再リクエスト |

### リトライ仕様

該当なし（同期処理のため）

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

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

## パフォーマンス要件

- ラベル数が多い場合（100以上）でも応答可能であること
- Taggingsテーブルとの複数JOINがあるため、適切なインデックスが必要

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

- 管理者ロールのみがアクセス可能（ReportPolicyで制御）
- アカウント間のデータ分離はaccount_idフィルタリングで担保
- CSVダウンロード時もサーバーサイドで認証・認可チェックを実施

## 備考

- ラベルはActsAsTaggableOnジェムで管理されており、会話に対して`labels`コンテキストでタギングされる
- 同一会話に複数ラベルが付与されている場合、それぞれのラベルにカウントされる
- CSV出力時は、時間値が人間可読形式（HH:MM:SS）に変換される

---

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

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

### 推奨読解順序

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

まず、ラベル管理の仕組みとレポートイベントモデルを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | label.rb | `app/models/label.rb` | Labelモデルの定義（titleカラム等） |
| 1-2 | reporting_event.rb | `app/models/reporting_event.rb` | レポートイベントのスキーマ |

**読解のコツ**: ChatwootではActsAsTaggableOnジェムを使用してラベル機能を実装している。Taggingsテーブルが会話とラベルの中間テーブルとして機能する。

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

APIコントローラーからの処理フローを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | reports_controller.rb | `app/controllers/api/v2/accounts/reports_controller.rb` | labelsアクション（31-34行目） |

**主要処理フロー**:
1. **31-34行目**: `labels`アクションでgenerate_labels_reportを呼び出し、CSVレンダリング
2. **73-75行目**: `check_authorization`で権限チェック

#### Step 3: ヘルパー層を理解する

レポート生成のヘルパーメソッドを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | reports_helper.rb | `app/helpers/api/v2/accounts/reports_helper.rb` | generate_labels_report（38-47行目） |

**主要処理フロー**:
- **38-42行目**: LabelSummaryBuilderを初期化してbuildを呼び出し
- **44-46行目**: 各ラベルのデータを整形

#### Step 4: ビルダー層を理解する

実際のデータ集計ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | label_summary_builder.rb | `app/builders/v2/reports/label_summary_builder.rb` | ラベル固有の集計ロジック |

**主要処理フロー**:
- **6-12行目**: initializeでタイムゾーン設定を初期化
- **15-21行目**: buildメソッドでラベル一覧を取得し、各ラベルのレポートを構築
- **25-35行目**: collect_report_dataで会話数、解決数、各種メトリクスを一括取得
- **38-48行目**: build_label_reportで個別ラベルのレポートを構築
- **61-94行目**: fetch_conversation_counts、fetch_resolved_counts等でデータを取得
- **96-111行目**: fetch_metricsでReportingEventから平均値を算出

#### Step 5: CSVテンプレートを理解する

CSV出力形式を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | labels.csv.erb | `app/views/api/v2/accounts/reports/labels.csv.erb` | CSV出力テンプレート |

**主要処理フロー**:
- **1行目**: 期間情報をヘッダー行として出力
- **3-11行目**: カラムヘッダーを定義（label_title, conversations_count等）
- **13-15行目**: レポートデータを行ごとに出力

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

```
ReportsController#labels
    │
    ├─ check_authorization
    │      └─ ReportPolicy#view?
    │
    ├─ ReportsHelper#generate_labels_report
    │      │
    │      ├─ LabelSummaryBuilder#new
    │      │
    │      └─ LabelSummaryBuilder#build
    │             │
    │             ├─ account.labels.to_a
    │             │
    │             └─ collect_report_data
    │                    │
    │                    ├─ fetch_conversation_counts
    │                    │      └─ ActsAsTaggableOn::Tagging.joins...
    │                    │
    │                    ├─ fetch_resolved_counts
    │                    │      └─ ReportingEvent.joins(conversation: {taggings: :tag})
    │                    │
    │                    └─ fetch_metrics (x3)
    │                           └─ ReportingEvent.joins...group...select(AVG)
    │
    └─ generate_csv
           └─ render labels.csv.erb
```

### データフロー図

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

since/until パラメータ ───▶ DateRangeHelper#range ───────────┐
                                                              │
business_hours パラメータ ─▶ use_business_hours? ────────────┤
                                                              │
                              ┌──────────────────────────────┘
                              ▼
Current.account ────────────▶ LabelSummaryBuilder
                              │
account.labels ───────────────┤
                              │
                              ├─▶ ActsAsTaggableOn::Tagging ──────────┐
                              │      (会話-ラベル関連)                 │
                              │                                        │
                              ├─▶ ReportingEvent                       │
                              │      .joins(conversation: {taggings})  │
                              │                                        │
                              └─────────────────────────────────────────┤
                                                                        │
                              ┌─────────────────────────────────────────┘
                              ▼
                         build_label_report (per label)
                              │
                              ▼
                         CSVSafe.generate_line ───▶ labels_report.csv
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| reports_controller.rb | `app/controllers/api/v2/accounts/reports_controller.rb` | コントローラー | APIエンドポイント |
| reports_helper.rb | `app/helpers/api/v2/accounts/reports_helper.rb` | ヘルパー | レポート生成ヘルパー |
| label_summary_builder.rb | `app/builders/v2/reports/label_summary_builder.rb` | ビルダー | ラベル集計ロジック |
| date_range_helper.rb | `app/helpers/date_range_helper.rb` | ヘルパー | 日付範囲処理 |
| label.rb | `app/models/label.rb` | モデル | ラベルモデル |
| reporting_event.rb | `app/models/reporting_event.rb` | モデル | レポートイベントデータ |
| report_policy.rb | `app/policies/report_policy.rb` | ポリシー | 認可制御 |
| labels.csv.erb | `app/views/api/v2/accounts/reports/labels.csv.erb` | ビュー | CSV出力テンプレート |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（441行目） |
