# 機能設計書 41-カスタム属性

## 概要

本ドキュメントは、Chatwootにおける会話毎のカスタム属性値を管理する機能の設計を記述します。

### 本機能の処理概要

会話に対してユーザー定義のカスタム属性を設定・更新・参照する機能を提供します。カスタム属性は、組織固有の情報（契約タイプ、優先顧客フラグ、案件番号など）を会話に紐付けて管理するために使用されます。

**業務上の目的・背景**：標準の会話属性だけでは管理できない組織固有の情報を、会話に関連付けて保存・活用する必要があります。例えば、顧客の契約タイプ、問い合わせの緊急度レベル、関連する案件番号など、業務プロセスに応じた柔軟な情報管理を実現します。これにより、エージェントは会話の文脈をより深く理解でき、適切な対応が可能になります。

**機能の利用シーン**：
- エージェントが会話を開いた際に、関連する業務情報を確認・入力する場面
- 自動化ルールで特定の属性値に基づいて会話を分類・ルーティングする場面
- レポートでカスタム属性を軸にした分析を行う場面
- APIを通じて外部システムから会話に属性情報を付与する場面

**主要な処理内容**：
1. カスタム属性値のJSONB形式での保存
2. APIエンドポイントを通じた属性値の更新
3. 会話更新イベントの発火による関連コンポーネントへの通知
4. カスタム属性定義との連携による属性値の検証

**関連システム・外部連携**：カスタム属性定義（CustomAttributeDefinition）モデルと連携し、事前に定義された属性キーに基づいて値を管理します。WebSocketを通じてリアルタイムで更新が通知されます。

**権限による制御**：会話の表示権限を持つユーザー（エージェント以上）がカスタム属性を更新できます。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 11 | 会話詳細画面 | 主画面 | カスタム属性の表示・編集 |
| 45 | 一般設定画面 | 参照画面 | カスタム属性の定義設定 |
| 73 | カスタム属性一覧画面 | 参照画面 | カスタム属性定義の管理 |

## 機能種別

CRUD操作 / データ更新

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| custom_attributes | Hash(JSONB) | Yes | カスタム属性のキーと値のペア | JSONB属性長の制限（jsonb_attributes_length） |

### 入力データソース

- 画面入力：会話詳細画面のカスタム属性編集フォーム
- API：`PATCH /api/v1/accounts/{account_id}/conversations/{id}/custom_attributes`

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| custom_attributes | Hash(JSONB) | 更新後のカスタム属性値 |
| conversation | Object | 更新された会話オブジェクト全体 |

### 出力先

- API レスポンス：JSON形式で更新された会話情報を返却
- WebSocket：CONVERSATION_UPDATEDイベントを購読クライアントにブロードキャスト
- データベース：conversationsテーブルのcustom_attributesカラム

## 処理フロー

### 処理シーケンス

```
1. APIリクエスト受信
   └─ custom_attributesアクションが呼び出される
2. 認可チェック
   └─ ConversationPolicyでshow?権限を確認
3. パラメータ抽出
   └─ permit(custom_attributes: {})で安全に属性を取得
4. 属性値の設定
   └─ conversation.custom_attributes = params
5. 保存処理
   └─ conversation.save!でデータベースに永続化
6. イベント発火
   └─ after_update_commitでCONVERSATION_UPDATEDイベント発火
7. レスポンス返却
   └─ 更新された会話オブジェクトをJSON形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[APIリクエスト受信] --> B{認可チェック}
    B -->|権限なし| C[403 Forbidden]
    B -->|権限あり| D[パラメータ抽出]
    D --> E[custom_attributes設定]
    E --> F{バリデーション}
    F -->|失敗| G[422 Unprocessable Entity]
    F -->|成功| H[save!]
    H --> I[CONVERSATION_UPDATEDイベント発火]
    I --> J[200 OK レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-41-001 | JSONB長制限 | custom_attributesのJSON文字列長は制限される | 常に適用 |
| BR-41-002 | 会話更新通知 | custom_attributes変更時はCONVERSATION_UPDATEDイベントが発火する | custom_attributesがlist_of_keysに含まれるため |
| BR-41-003 | 属性定義との整合性 | 事前に定義されていない属性キーも保存可能 | カスタム属性定義は参照用であり強制ではない |

### 計算ロジック

特に複雑な計算ロジックはありません。JSONB形式でキーバリューペアとして保存されます。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カスタム属性更新 | conversations | UPDATE | custom_attributesカラムの更新 |

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

#### conversations

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | custom_attributes | リクエストで指定されたJSONオブジェクト | JSONB型 |
| UPDATE | updated_at | 現在時刻 | 自動更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | 権限エラー | 会話表示権限がない場合 | 適切な権限を持つユーザーでアクセス |
| 404 | 会話未検出 | 指定されたIDの会話が存在しない場合 | 正しい会話IDを指定 |
| 422 | バリデーションエラー | JSONB長制限を超えた場合 | 属性値を短縮 |

### リトライ仕様

特にリトライ処理は実装されていません。

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

単一レコードの更新のため、Railsのデフォルトトランザクション制御に従います。save!メソッドにより、バリデーション失敗時は例外が発生しロールバックされます。

## パフォーマンス要件

- JSONB型を使用しているため、部分的な更新時も全体を書き換えます
- custom_attributesカラムにはインデックスがないため、属性値でのフィルタリングは検索パフォーマンスに注意が必要です

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

- Strong Parametersによるパラメータフィルタリング（`permit(custom_attributes: {})`）
- ConversationPolicyによる認可チェック
- アカウント境界の自動適用（Current.accountによるスコーピング）

## 備考

- カスタム属性は会話だけでなく連絡先（Contact）にも設定可能です（別機能）
- 属性定義（CustomAttributeDefinition）は表示名や入力タイプを定義しますが、値の保存時に厳密な型検証は行われません

---

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

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

### 推奨読解順序

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

まず、Conversationモデルのカスタム属性がどのように定義されているかを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | conversation.rb | `app/models/conversation.rb` | custom_attributesカラムの定義（11行目）、バリデーション（71行目） |
| 1-2 | custom_attribute_definition.rb | `app/models/custom_attribute_definition.rb` | カスタム属性定義の構造、attribute_model enum（42行目） |

**読解のコツ**: Conversationモデルでは`validates :custom_attributes, jsonb_attributes_length: true`によりJSONB長の制限が適用されています。

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

APIエンドポイントの処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | conversations_controller.rb | `app/controllers/api/v1/accounts/conversations_controller.rb` | custom_attributesアクション（131-134行目） |

**主要処理フロー**:
1. **131行目**: `def custom_attributes`メソッドでリクエストを受け付け
2. **132行目**: `params.permit(custom_attributes: {})`でパラメータを安全に取得
3. **133行目**: `@conversation.save!`でデータベースに保存

#### Step 3: イベント通知を理解する

更新後のイベント発火処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | conversation.rb | `app/models/conversation.rb` | list_of_keys（278-280行目）、allowed_keys?（283-288行目）、notify_conversation_updation（272-276行目） |

**主要処理フロー**:
- **279行目**: `custom_attributes`がlist_of_keysに含まれている
- **273-275行目**: `previous_changes`に該当キーが含まれる場合、`dispatch_conversation_updated_event`が呼ばれる

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

```
ConversationsController#custom_attributes
    │
    ├─ params.permit(custom_attributes: {})
    │      └─ ActionController::Parameters#permit
    │
    ├─ @conversation.custom_attributes = params
    │      └─ ActiveRecord attribute setter
    │
    └─ @conversation.save!
           │
           └─ after_update_commit :execute_after_update_commit_callbacks
                  │
                  └─ notify_conversation_updation
                         │
                         └─ dispatch_conversation_updated_event
                                │
                                └─ Rails.configuration.dispatcher.dispatch(CONVERSATION_UPDATED)
```

### データフロー図

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

custom_attributes    ───▶ ConversationsController    ───▶ DB (conversations.custom_attributes)
(JSON Hash)               #custom_attributes
                               │
                               │
                               └──────────────────────▶ WebSocket
                                                        (CONVERSATION_UPDATED event)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| conversations_controller.rb | `app/controllers/api/v1/accounts/conversations_controller.rb` | コントローラー | APIエンドポイント定義 |
| conversation.rb | `app/models/conversation.rb` | モデル | データモデル定義、バリデーション |
| custom_attribute_definition.rb | `app/models/custom_attribute_definition.rb` | モデル | カスタム属性定義の管理 |
| _conversation.json.jbuilder | `app/views/api/v1/conversations/partials/_conversation.json.jbuilder` | ビュー | APIレスポンスのJSON生成 |
| jsonb_attributes_length_validator.rb | `app/validators/jsonb_attributes_length_validator.rb` | バリデータ | JSONB長制限の検証 |
