# 機能設計書 107-サーベイメトリクス

## 概要

本ドキュメントは、Chatwootにおける CSAT（顧客満足度調査）メトリクス集計機能について記述する。CSATサーベイ回答データを集計し、統計情報を提供する機能である。

### 本機能の処理概要

**業務上の目的・背景**：カスタマーサポートの品質管理において、CSAT スコアの推移や回答率を定量的に把握することが重要である。本機能は、CSATサーベイの回答データを集計し、評価値別のカウント、総回答数、送信数などの統計情報を提供することで、サービス品質の可視化と改善施策の立案を支援する。

**機能の利用シーン**：
- CSATレポート画面でメトリクスダッシュボードを表示
- 期間を指定してCSATスコアの推移を確認
- エージェント別、インボックス別、チーム別のCSATパフォーマンスを比較

**主要な処理内容**：
1. CSATサーベイ回答の総数カウント
2. 評価値（1-5）別の回答数カウント
3. 送信したCSATメッセージの総数カウント
4. フィルタ条件に基づく集計

**関連システム・外部連携**：特になし（内部集計処理）

**権限による制御**：CSATサーベイの閲覧にはレポート閲覧権限が必要。check_authorization によりアクセス制御される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 43 | CSATレポート画面 | 主画面 | CSATメトリクスの表示 |

## 機能種別

集計処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| since | Integer (Unix timestamp) | No | 期間開始（秒） | - |
| until | Integer (Unix timestamp) | No | 期間終了（秒） | - |
| user_ids | Array | No | エージェントID配列 | - |
| inbox_id | Integer | No | インボックスID | - |
| team_id | Integer | No | チームID | - |
| rating | Integer | No | 評価値（1-5） | 1-5 の範囲 |

### 入力データソース

- 画面入力: レポート画面のフィルタ条件
- DBテーブル: csat_survey_responses テーブルからサーベイ回答を取得
- DBテーブル: messages テーブルからCSATメッセージを取得

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| total_count | Integer | フィルタ条件に合致する回答の総数 |
| ratings_count | Object | 評価値別の回答数 { "1": n, "2": n, "3": n, "4": n, "5": n } |
| total_sent_messages_count | Integer | 送信したCSATメッセージの総数（回答率計算用） |

### 出力先

- 画面表示: JSON形式でAPIレスポンス

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ APIエンドポイント: /api/v1/accounts/:account_id/csat_survey_responses/metrics

2. 認可チェック
   └─ check_authorization でレポート閲覧権限確認

3. CSATサーベイ回答取得
   ├─ 基本クエリ: Current.account.csat_survey_responses
   ├─ フィルタ適用: 期間、エージェント、インボックス、チーム、評価
   └─ ソート適用: Sift gemによるソート

4. メトリクス集計
   ├─ total_count: @csat_survey_responses.count
   └─ ratings_count: @csat_survey_responses.group(:rating).count

5. 送信メッセージ数カウント
   ├─ 基本クエリ: Current.account.messages.input_csat
   └─ フィルタ適用: 期間

6. JSON レスポンス返却
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[認可チェック]
    B --> C[CSATサーベイ回答取得]
    C --> D[フィルタ適用]
    D --> E[total_count 集計]
    E --> F[ratings_count 集計]
    F --> G[送信メッセージ数カウント]
    G --> H[JSON レスポンス]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-107-01 | 評価範囲 | ratings_count は 1-5 の評価値をキーとする | 常時 |
| BR-107-02 | 送信数対象 | 送信メッセージ数は input_csat タイプのメッセージをカウント | 常時 |
| BR-107-03 | 回答率計算 | 回答率 = total_count / total_sent_messages_count | フロントエンドで計算 |

### 計算ロジック

回答総数:
```ruby
@total_count = @csat_survey_responses.count
```

評価値別カウント:
```ruby
@ratings_count = @csat_survey_responses.group(:rating).count
# 結果例: { 1 => 5, 2 => 10, 3 => 25, 4 => 40, 5 => 20 }
```

送信メッセージ数:
```ruby
@csat_messages = Current.account.messages.input_csat
@csat_messages = @csat_messages.where(created_at: range) if range.present?
@total_sent_messages_count = @csat_messages.count
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| metrics | csat_survey_responses | SELECT + COUNT | 回答総数集計 |
| metrics | csat_survey_responses | SELECT + GROUP BY + COUNT | 評価値別集計 |
| metrics | messages | SELECT + COUNT | 送信メッセージ数 |

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

#### csat_survey_responses

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| COUNT | * | フィルタ条件適用後 | 回答総数 |
| COUNT | rating | GROUP BY rating | 評価値別集計 |

#### messages

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| COUNT | * | content_type = 'input_csat' AND created_at BETWEEN since AND until | 送信数 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | Unauthorized | 未認証状態でアクセス | ログインページへリダイレクト |
| 403 | Forbidden | レポート閲覧権限なし | エラーメッセージ表示 |

### リトライ仕様

特になし。同期処理のため即時エラー返却。

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

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

## パフォーマンス要件

- レスポンス時間: 500ms以内
- インデックス: csat_survey_responses.account_id, messages.account_id, messages.content_type

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

- 認証必須: Devise TokenAuthによる認証
- 認可: Punditによるレポート閲覧権限チェック
- アカウントスコープ: Current.account.id で他アカウントのデータにアクセス不可

## 備考

- metrics アクションは csat_survey_responses_controller.rb の一部として実装
- ratings_count のキーは整数型（1, 2, 3, 4, 5）

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | csat_survey_response.rb | `app/models/csat_survey_response.rb` | モデル構造とスコープ |

**読解のコツ**: `filter_by_*` スコープでフィルタリング条件を確認。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | csat_survey_responses_controller.rb | `app/controllers/api/v1/accounts/csat_survey_responses_controller.rb` | metrics アクション |

**主要処理フロー**:
- **8行目**: `before_action :set_csat_survey_responses, only: [:index, :metrics, :download]`
- **11行目**: `before_action :set_total_sent_messages_count, only: [:metrics]`
- **17-20行目**: `metrics` アクション
  - `@total_count = @csat_survey_responses.count`
  - `@ratings_count = @csat_survey_responses.group(:rating).count`

#### Step 3: フィルタリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | csat_survey_responses_controller.rb | `app/controllers/api/v1/accounts/csat_survey_responses_controller.rb` | フィルタ処理 |

**主要処理フロー**:
- **30-34行目**: `set_total_sent_messages_count`
  - `Current.account.messages.input_csat` でCSATメッセージを取得
  - `where(created_at: range)` で期間フィルタ

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | metrics.json.jbuilder | `app/views/api/v1/accounts/csat_survey_responses/metrics.json.jbuilder` | JSONレスポンス |

**主要処理フロー**:
- **1行目**: `json.total_count @total_count`
- **2行目**: `json.ratings_count @ratings_count`
- **3行目**: `json.total_sent_messages_count @total_sent_messages_count`

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

```
CsatSurveyResponsesController#metrics
    │
    ├─ before_action
    │      ├─ check_authorization
    │      ├─ set_csat_survey_responses
    │      │      ├─ filtrate (Sift gem)
    │      │      ├─ filter_by_created_at
    │      │      ├─ filter_by_assigned_agent_id
    │      │      ├─ filter_by_inbox_id
    │      │      ├─ filter_by_team_id
    │      │      └─ filter_by_rating
    │      └─ set_total_sent_messages_count
    │             └─ Current.account.messages.input_csat.count
    │
    └─ metrics アクション
           ├─ @csat_survey_responses.count
           ├─ @csat_survey_responses.group(:rating).count
           └─ metrics.json.jbuilder
```

### データフロー図

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

フィルタ条件 ───────▶ CsatSurveyResponsesController ───▶ JSON Response
(期間, agent,               │                              {
 inbox, team,               │                                total_count,
 rating)                    │                                ratings_count,
                           │                                total_sent_messages_count
                           ▼                              }
        ┌─────────────────┴─────────────────┐
        ▼                                   ▼
csat_survey_responses                  messages
(COUNT, GROUP BY)                 (COUNT WHERE input_csat)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| csat_survey_response.rb | `app/models/csat_survey_response.rb` | モデル | CSATサーベイ回答モデル |
| csat_survey_responses_controller.rb | `app/controllers/api/v1/accounts/csat_survey_responses_controller.rb` | コントローラー | metrics アクション |
| metrics.json.jbuilder | `app/views/api/v1/accounts/csat_survey_responses/metrics.json.jbuilder` | ビュー | JSONレスポンステンプレート |
| message.rb | `app/models/message.rb` | モデル | input_csat スコープ |
