# 機能設計書 105-CSATサーベイ

## 概要

本ドキュメントは、Chatwootにおける顧客満足度調査（CSAT: Customer Satisfaction）機能について記述する。会話終了後に顧客に満足度調査を送信し、回答を収集・管理する機能である。

### 本機能の処理概要

**業務上の目的・背景**：カスタマーサポートの品質向上には、顧客からの直接的なフィードバックが不可欠である。CSATサーベイにより、各会話に対する顧客の満足度を定量的に測定でき、エージェントのパフォーマンス評価やサービス改善の指標として活用できる。

**機能の利用シーン**：
- 会話解決後に顧客にCSATメッセージを送信
- 顧客が1-5段階の評価とフィードバックメッセージを提出
- 管理者・エージェントがCSATレポートで結果を確認
- エージェント別、インボックス別、チーム別のCSATスコアを分析

**主要な処理内容**：
1. CSATサーベイ回答の一覧取得（フィルタリング、ページネーション付き）
2. CSATメトリクスの集計（評価別カウント、総数）
3. CSATレポートのCSVダウンロード
4. 期間、エージェント、インボックス、チーム、評価による絞り込み

**関連システム・外部連携**：
- メッセージシステム: CSATサーベイはメッセージとして送信される
- レポートシステム: CSATメトリクスはレポート画面で表示

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 43 | CSATレポート画面 | 主画面 | CSATサーベイレポートの表示 |

## 機能種別

CRUD操作（参照） / 集計処理 / 帳票出力

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| 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 の範囲 |
| page | Integer | No | ページ番号（デフォルト: 1） | - |
| sort | String | No | ソート対象（created_at） | - |

### 入力データソース

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

## 出力仕様

### 出力データ

#### 一覧（index）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | CSATサーベイ回答ID |
| rating | Integer | 評価値（1-5） |
| feedback_message | String | フィードバックメッセージ |
| conversation | Object | 会話情報 |
| assigned_agent | Object | 担当エージェント情報 |
| contact | Object | 連絡先情報 |
| created_at | DateTime | 回答日時 |

#### メトリクス（metrics）

| 項目名 | 型 | 説明 |
|--------|-----|------|
| total_count | Integer | 回答総数 |
| ratings_count | Object | 評価値別カウント |
| total_sent_messages_count | Integer | 送信したCSATメッセージ総数 |

### 出力先

- 画面表示: JSON形式でAPIレスポンス
- ファイル: CSVダウンロード

## 処理フロー

### 処理シーケンス

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

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

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

4. アクション別処理
   ├─ index: ページネーション後に一覧返却
   ├─ metrics: 集計処理後にメトリクス返却
   └─ download: CSVテンプレートでダウンロード
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B[認可チェック]
    B --> C[CSATサーベイ回答取得]
    C --> D[フィルタ適用]
    D --> E{アクション種別}
    E -->|index| F[ページネーション]
    E -->|metrics| G[集計処理]
    E -->|download| H[CSV生成]
    F --> I[JSON レスポンス]
    G --> J[JSON レスポンス]
    H --> K[CSVダウンロード]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-105-01 | 評価範囲 | CSATの評価値は1-5の整数のみ許可 | 回答登録時 |
| BR-105-02 | ページサイズ | 一覧は25件/ページ | index アクション |
| BR-105-03 | 送信数カウント | CSATメッセージ（input_csat）の送信数を集計 | metrics アクション |
| BR-105-04 | 回答率計算 | 回答率 = 回答数 / 送信数 | フロントエンドで計算 |

### 計算ロジック

評価別カウント:
```ruby
@csat_survey_responses.group(:rating).count
```

送信メッセージ数:
```ruby
Current.account.messages.input_csat.where(created_at: range).count
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| index | csat_survey_responses | SELECT | フィルタ付き一覧取得 |
| index | conversations | JOIN | 会話情報取得 |
| index | users | JOIN | エージェント情報取得 |
| index | contacts | JOIN | 連絡先情報取得 |
| metrics | csat_survey_responses | SELECT + COUNT | 集計クエリ |
| metrics | messages | SELECT + COUNT | 送信数カウント |

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

#### csat_survey_responses

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | account_id = Current.account.id | 基本条件 |
| SELECT | * | created_at BETWEEN since AND until | 期間フィルタ |
| SELECT | * | assigned_agent_id IN (user_ids) | エージェントフィルタ |
| SELECT | * | JOIN conversations WHERE inbox_id = ? | インボックスフィルタ |
| SELECT | * | JOIN conversations WHERE team_id = ? | チームフィルタ |
| SELECT | * | rating = ? | 評価フィルタ |
| COUNT | rating | GROUP BY rating | 評価別集計 |

## エラー処理

### エラーケース一覧

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

### リトライ仕様

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

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

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

## パフォーマンス要件

- レスポンス時間: 1秒以内（大量データ時）
- インデックス: account_id, assigned_agent_id, conversation_id, message_id に個別インデックス
- ページネーション: 25件/ページで負荷軽減

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

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

## 備考

- CSATサーベイは会話のメッセージとして送信され、message_id で紐付け
- Enterprise版では review_notes カラムが追加される
- CSVエクスポートにはエージェント名、評価、フィードバック、連絡先情報、会話リンクが含まれる

---

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

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

### 推奨読解順序

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

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

**読解のコツ**:
- **1-23行目**: スキーマ情報（テーブル構造、インデックス）
- **24-30行目**: アソシエーション（conversation, contact, message, assigned_agent）
- **32行目**: rating バリデーション（1-5の範囲）
- **37-42行目**: フィルタ用スコープ（filter_by_*）

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | csat_survey_responses_controller.rb | `app/controllers/api/v1/accounts/csat_survey_responses_controller.rb` | API エンドポイント |

**主要処理フロー**:
- **5行目**: `RESULTS_PER_PAGE = 25` - ページサイズ定数
- **7-11行目**: before_action でフィルタとページネーション設定
- **15行目**: `index` - 一覧取得
- **17-20行目**: `metrics` - 集計処理
- **22-26行目**: `download` - CSV ダウンロード

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

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

**主要処理フロー**:
- **36-43行目**: `set_csat_survey_responses` - フィルタチェーン
  - `filtrate(base_query)` - Sift gem によるソート
  - `filter_by_created_at(range)` - 期間フィルタ
  - `filter_by_assigned_agent_id` - エージェントフィルタ
  - `filter_by_inbox_id` - インボックスフィルタ
  - `filter_by_team_id` - チームフィルタ
  - `filter_by_rating` - 評価フィルタ

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.json.jbuilder | `app/views/api/v1/accounts/csat_survey_responses/index.json.jbuilder` | 一覧JSONレスポンス |
| 4-2 | metrics.json.jbuilder | `app/views/api/v1/accounts/csat_survey_responses/metrics.json.jbuilder` | メトリクスJSONレスポンス |
| 4-3 | download.csv.erb | `app/views/api/v1/accounts/csat_survey_responses/download.csv.erb` | CSVテンプレート |

**主要処理フロー**:
- `metrics.json.jbuilder`: total_count, ratings_count, total_sent_messages_count
- `download.csv.erb`: ヘッダー行 + データ行 + 期間情報

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

```
CsatSurveyResponsesController
    │
    ├─ 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_current_page
    │      ├─ set_current_page_surveys
    │      └─ set_total_sent_messages_count
    │
    ├─ index
    │      └─ index.json.jbuilder
    │
    ├─ metrics
    │      ├─ @csat_survey_responses.count
    │      ├─ @csat_survey_responses.group(:rating).count
    │      └─ metrics.json.jbuilder
    │
    └─ download
           └─ download.csv.erb
```

### データフロー図

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

フィルタ条件 ───────▶ CsatSurveyResponsesController ───▶ JSON/CSV
(期間, agent,               │
 inbox, team,               │
 rating, page)              ▼
                    csat_survey_responses テーブル
                    (SELECT + JOIN + GROUP BY)
                            │
                            ▼
                    conversations, users, contacts
                    (関連データ取得)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| 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` | コントローラー | API エンドポイント |
| index.json.jbuilder | `app/views/api/v1/accounts/csat_survey_responses/index.json.jbuilder` | ビュー | 一覧JSONレスポンス |
| metrics.json.jbuilder | `app/views/api/v1/accounts/csat_survey_responses/metrics.json.jbuilder` | ビュー | メトリクスJSONレスポンス |
| download.csv.erb | `app/views/api/v1/accounts/csat_survey_responses/download.csv.erb` | ビュー | CSVダウンロードテンプレート |
| _csat_survey_response.json.jbuilder | `app/views/api/v1/models/_csat_survey_response.json.jbuilder` | パーシャル | 単一回答のJSON |
