# 機能設計書 80-キャンドレスポンス

## 概要

本ドキュメントは、Chatwootにおけるキャンドレスポンス（定型文）機能の仕様を定義する機能設計書である。

### 本機能の処理概要

よく使う返答テンプレート（定型文）を登録・管理する機能。エージェントがショートコードを入力することで、事前に登録した定型文を素早く挿入できるようにし、返信作業の効率化と品質の均一化を実現する。

**業務上の目的・背景**：カスタマーサポートでは、同じ質問に対して同じ回答をすることが多い。毎回同じ文章を入力するのは非効率であり、タイプミスや文章のばらつきも発生しやすい。本機能により、よく使う返答を定型文として登録し、ショートコードで呼び出すことで、エージェントの入力作業を大幅に削減し、回答品質を統一する。

**機能の利用シーン**：
- 「/hello」で挨拶文を挿入
- 「/thanks」でお礼メッセージを挿入
- 「/faq_shipping」で配送に関するFAQ回答を挿入
- 「/escalation」でエスカレーション案内を挿入

**主要な処理内容**：
1. キャンドレスポンスのCRUD操作（作成・読取・更新・削除）
2. ショートコード（short_code）の設定
3. 定型文内容（content）の設定
4. ショートコードによる検索
5. 内容による検索

**関連システム・外部連携**：
- メッセージ入力UI（ショートコード入力時の補完機能）

**権限による制御**：認証済みユーザーがアクセス可能（特別な権限制御なし）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 54 | 定型文一覧画面 | 主画面 | 定型文の一覧表示・管理 |
| 11 | 会話詳細画面 | 参照画面 | 定型文の挿入（メッセージ入力時） |

## 機能種別

CRUD操作 / マスタ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| short_code | String | Yes | ショートコード | 必須、アカウント内で一意 |
| content | Text | Yes | 定型文の内容 | 必須 |

### 検索パラメータ

| パラメータ名 | 型 | 必須 | 説明 |
|-------------|-----|-----|------|
| search | String | No | ショートコードまたは内容で部分一致検索 |

### 入力データソース

- 管理画面からのフォーム入力
- REST API経由

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | キャンドレスポンスID |
| short_code | String | ショートコード |
| content | Text | 定型文の内容 |
| created_at | DateTime | 作成日時 |
| updated_at | DateTime | 更新日時 |
| account_id | Integer | アカウントID |

### 出力先

- APIレスポンス（JSON形式）
- データベース（canned_responsesテーブル）

## 処理フロー

### 処理シーケンス

```
1. キャンドレスポンス作成リクエスト受信
   └─ POST /api/v1/accounts/:account_id/canned_responses
2. パラメータ検証
   └─ short_code, content必須チェック、一意性チェック
3. キャンドレスポンス保存
   └─ canned_responsesテーブルへINSERT
4. レスポンス返却
   └─ 作成されたキャンドレスポンス情報をJSON形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[キャンドレスポンス作成リクエスト] --> B[パラメータ検証]
    B --> C{short_code必須?}
    C -->|No| D[422 Unprocessable Entity]
    C -->|Yes| E{content必須?}
    E -->|No| D
    E -->|Yes| F{short_code一意?}
    F -->|No| D
    F -->|Yes| G[キャンドレスポンス保存]
    G --> H[成功レスポンス]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-80-01 | ショートコード一意性 | 同一アカウント内でshort_codeは一意 | 作成・更新時 |
| BR-80-02 | 必須項目 | short_codeとcontentは両方とも必須 | 作成・更新時 |
| BR-80-03 | 検索順序 | 検索時はshort_codeの前方一致を優先してソート | 検索時 |
| BR-80-04 | ILIKE検索 | 検索は大文字小文字を区別しない | 検索時 |

### 計算ロジック

検索時のソート優先度（order_by_search）：
```ruby
scope :order_by_search, lambda { |search|
  short_code_starts_with = "WHEN short_code ILIKE '#{search}%' THEN 1"
  short_code_like = "WHEN short_code ILIKE '%#{search}%' THEN 0.5"
  content_like = "WHEN content ILIKE '%#{search}%' THEN 0.2"
  order("CASE #{short_code_starts_with} #{short_code_like} #{content_like} ELSE 0 END DESC")
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| キャンドレスポンス作成 | canned_responses | INSERT | 新規レコードの挿入 |
| キャンドレスポンス更新 | canned_responses | UPDATE | 既存レコードの更新 |
| キャンドレスポンス削除 | canned_responses | DELETE | レコードの削除 |
| キャンドレスポンス一覧取得 | canned_responses | SELECT | 全件または検索条件で取得 |

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

#### canned_responses

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | short_code, content | リクエストパラメータ | account_idは自動設定 |
| UPDATE | short_code, content | リクエストパラメータ | 既存レコードを更新 |
| DELETE | - | id = :id | 物理削除 |
| SELECT (検索あり) | * | short_code ILIKE '%search%' OR content ILIKE '%search%' | order_by_searchでソート |
| SELECT (検索なし) | * | account_id = current_account.id | 全件取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 422 | Unprocessable Entity | バリデーションエラー（必須、一意性） | エラーメッセージに従い修正 |
| 404 | Not Found | 指定キャンドレスポンスが存在しない | 正しいIDを指定 |
| 401 | Unauthorized | 未認証 | ログイン後に再試行 |

### リトライ仕様

特になし（CRUD操作のみ）

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

単一レコード操作のため、ActiveRecordの標準トランザクション制御に依存。

## パフォーマンス要件

- 一覧取得：100ms以内
- 検索：200ms以内（ILIKE検索のため、データ量が多い場合は要インデックス検討）
- 作成/更新：200ms以内

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

- 認証済みユーザーのみアクセス可能
- アカウント単位でデータ分離
- 検索クエリはサニタイズ処理済み（sanitize_sql_array使用）

## 備考

- フロントエンドでは「/」で始まる入力時にキャンドレスポンスの候補を表示
- contentにはHTML/マークダウンを含めることも可能
- 検索はshort_codeまたはcontentのいずれかにマッチすれば結果に含まれる

---

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

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

### 推奨読解順序

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

CannedResponseモデルの構造を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | canned_response.rb | `app/models/canned_response.rb` | モデル定義、バリデーション、スコープ |

**読解のコツ**:
- **1-11行目**: スキーマ情報でカラム定義確認
- **13-17行目**: バリデーション（content, short_code, account必須、short_codeのaccount内一意性）
- **19行目**: belongs_to :account
- **21-28行目**: order_by_searchスコープで検索時のソート優先度を定義

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

APIコントローラーのCRUD操作を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | canned_responses_controller.rb | `app/controllers/api/v1/accounts/canned_responses_controller.rb` | CRUD操作の実装 |

**主要処理フロー**:
1. **4-6行目**: indexアクションでcanned_responsesメソッドを呼び出し
2. **8-12行目**: createアクションで新規作成
3. **14-17行目**: updateアクションで更新
4. **19-22行目**: destroyアクションで削除
5. **30-33行目**: permitted_paramsでshort_code, contentを許可
6. **35-43行目**: canned_responsesメソッドで検索有無による分岐

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

```
CannedResponsesController
    │
    ├─ index: キャンドレスポンス一覧/検索
    │      └─ canned_responses
    │             ├─ [検索あり] where(ILIKE).order_by_search
    │             └─ [検索なし] Current.account.canned_responses
    │
    ├─ create: キャンドレスポンス作成
    │      └─ Current.account.canned_responses.new(params).save!
    │
    ├─ update: キャンドレスポンス更新
    │      └─ @canned_response.update!(params)
    │
    └─ destroy: キャンドレスポンス削除
           └─ @canned_response.destroy!

[フロントエンド]
メッセージ入力欄
    │
    └─ "/" 入力時
           └─ GET /canned_responses?search=xxx
                  └─ 候補表示 → 選択で content を挿入
```

### データフロー図

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

管理者設定                  キャンドレスポンス保存          DBへ永続化
(short_code,       ───▶ CannedResponsesController ───▶ canned_responses
 content)                                                   テーブル

検索クエリ                 検索・ソート                    JSONレスポンス
(search)           ───▶ index ───────────────────▶ キャンドレスポンス
                            │                              配列
                            ├─ ILIKE検索
                            └─ order_by_search ソート
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| canned_response.rb | `app/models/canned_response.rb` | モデル | データ構造・バリデーション・検索スコープ |
| canned_responses_controller.rb | `app/controllers/api/v1/accounts/canned_responses_controller.rb` | コントローラー | CRUD API |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（105行目） |
