# 機能設計書 78-ラベル作成・管理

## 概要

本ドキュメントは、Chatwootにおけるラベルの作成・管理機能の仕様を定義する機能設計書である。

### 本機能の処理概要

会話や連絡先に付与するためのラベルを作成・管理する機能。ラベルはタグとして機能し、会話や連絡先のカテゴリ分け、フィルタリング、レポート分析に使用される。

**業務上の目的・背景**：カスタマーサポートでは、問い合わせの種類（技術的質問、返品、クレームなど）や顧客の属性（VIP、新規、リピーターなど）を分類して管理する必要がある。本機能により、柔軟にラベルを定義し、会話や連絡先に付与することで、効率的な分類・検索・分析が可能になる。

**機能の利用シーン**：
- 問い合わせの種類別にラベルを作成（例：billing、technical、feedback）
- 顧客セグメント用ラベルを作成（例：vip、enterprise、trial）
- キャンペーン追跡用ラベルを作成（例：summer_sale_2024）
- チーム内でのラベル体系の統一・管理

**主要な処理内容**：
1. ラベルのCRUD操作（作成・読取・更新・削除）
2. ラベル名（title）のバリデーション
3. ラベル色（color）の設定
4. サイドバー表示フラグの設定
5. ラベル名変更時の関連データ更新

**関連システム・外部連携**：
- ActsAsTaggableOn gemによるタギング機能
- レポート機能でのラベル別集計

**権限による制御**：
- 一覧表示：Administrator, Agent
- 作成・編集・削除：Administratorのみ

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 53 | ラベル一覧画面 | 主画面 | ラベルの一覧表示・管理 |
| 12 | ラベル別会話一覧 | 参照画面 | ラベルによる会話フィルタリング |
| 22 | ラベル別連絡先一覧 | 参照画面 | ラベルによる連絡先フィルタリング |
| 39 | ラベルレポート画面 | 参照画面 | ラベル別統計表示 |

## 機能種別

CRUD操作 / マスタ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| title | String | Yes | ラベル名 | 必須、Unicode文字・数字・ハイフン・アンダースコアのみ、アカウント内で一意 |
| description | Text | No | ラベルの説明 | - |
| color | String | No | ラベルの色（HEXカラーコード） | デフォルト: #1f93ff |
| show_on_sidebar | Boolean | No | サイドバーに表示するか | - |

### 入力データソース

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

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | ラベルID |
| title | String | ラベル名 |
| description | Text | ラベルの説明 |
| color | String | ラベルの色 |
| show_on_sidebar | Boolean | サイドバー表示フラグ |

### 出力先

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

## 処理フロー

### 処理シーケンス

```
1. ラベル作成リクエスト受信
   └─ POST /api/v1/accounts/:account_id/labels
2. パラメータ検証
   └─ title形式・一意性チェック
3. titleの小文字変換
   └─ before_validation callback
4. ラベル保存
   └─ labelsテーブルへINSERT
5. キャッシュ無効化
   └─ AccountCacheRevalidator concern
6. レスポンス返却
```

### フローチャート

```mermaid
flowchart TD
    A[ラベル作成リクエスト] --> B[権限チェック]
    B --> C{Administrator?}
    C -->|No| D[403 Forbidden]
    C -->|Yes| E[パラメータ検証]
    E --> F{title有効?}
    F -->|No| G[422 Unprocessable Entity]
    F -->|Yes| H[title小文字変換]
    H --> I[一意性チェック]
    I --> J{一意?}
    J -->|No| G
    J -->|Yes| K[ラベル保存]
    K --> L[キャッシュ無効化]
    L --> M[成功レスポンス]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-78-01 | title形式 | Unicode文字、数字、ハイフン、アンダースコアのみ許可 | 作成・更新時 |
| BR-78-02 | 小文字変換 | titleは自動的に小文字に変換される | 保存前 |
| BR-78-03 | 一意性 | 同一アカウント内でtitleは一意 | 作成・更新時 |
| BR-78-04 | デフォルト色 | color未指定時は#1f93ffが設定される | 作成時 |
| BR-78-05 | ラベル名変更時の連動更新 | title変更時は関連する会話・連絡先のタグも更新 | 更新時 |

### 計算ロジック

ラベル名変更時の連動更新はLabels::UpdateJobで非同期処理される：
```ruby
def update_associated_models
  return unless title_previously_changed?
  Labels::UpdateJob.perform_later(title, title_previously_was, account_id)
end
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ラベル作成 | labels | INSERT | 新規ラベルレコードの挿入 |
| ラベル更新 | labels | UPDATE | 既存ラベルの更新 |
| ラベル削除 | labels | DELETE | ラベルの削除 |
| ラベル一覧取得 | labels | SELECT | アカウント内ラベル全件取得 |
| 関連タグ更新 | tags, taggings | UPDATE | ラベル名変更時の連動更新 |

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

#### labels

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | title, description, color, show_on_sidebar | リクエストパラメータ | account_idは自動設定 |
| UPDATE | title, description, color, show_on_sidebar | リクエストパラメータ | 既存レコードを更新 |
| DELETE | - | id = :id | 物理削除 |
| SELECT | * | account_id = current_account.id | タイトル順でソート |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 422 | Unprocessable Entity | title形式不正、一意性違反 | エラーメッセージに従い修正 |
| 404 | Not Found | 指定ラベルが存在しない | 正しいIDを指定 |
| 403 | Forbidden | Administrator権限がない | 管理者権限でアクセス |
| 401 | Unauthorized | 未認証 | ログイン後に再試行 |

### リトライ仕様

特になし（CRUD操作のみ）

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

単一レコード操作のため、ActiveRecordの標準トランザクション制御に依存。ラベル名変更時の関連更新は非同期ジョブで処理。

## パフォーマンス要件

- ラベル一覧取得：100ms以内
- ラベル作成/更新：200ms以内
- default_scopeでタイトル順ソートを自動適用

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

- LabelPolicyによる権限制御
- index: Administrator, Agent
- create/update/delete: Administratorのみ

## 備考

- ActsAsTaggableOn gemと連携してタギング機能を実現
- labels.conversationsでラベルが付与された会話を取得可能
- ラベル削除時、関連するタグ付け（taggings）の処理は未定義（要確認）

---

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

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

### 推奨読解順序

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

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

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

**読解のコツ**:
- **1-18行目**: スキーマ情報でカラム定義確認
- **25-28行目**: titleのバリデーション（presence、format、uniqueness）
- **30行目**: after_update_commit :update_associated_models
- **31行目**: default_scope { order(:title) }
- **33-35行目**: before_validationでtitleを小文字変換

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

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

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

**主要処理フロー**:
1. **6-8行目**: indexアクションでpolicy_scopeを使用
2. **12-14行目**: createアクションでラベル作成
3. **16-18行目**: updateアクションでラベル更新
4. **20-23行目**: destroyアクションでラベル削除
5. **31-33行目**: permitted_paramsでtitle, description, color, show_on_sidebarを許可

#### Step 3: 権限制御を理解する

LabelPolicyを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | label_policy.rb | `app/policies/label_policy.rb` | 各アクションの権限制御 |

**主要処理フロー**:
- **2-4行目**: index? - Administrator または Agent
- **6-20行目**: create/update/show/destroy - Administratorのみ

#### Step 4: ラベル名変更時の連動更新を理解する

更新ジョブを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | update_job.rb | `app/jobs/labels/update_job.rb` | 非同期更新ジョブ |

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

```
LabelsController
    │
    ├─ index: ラベル一覧取得
    │      └─ policy_scope(Current.account.labels)
    │
    ├─ create: ラベル作成
    │      ├─ check_authorization (LabelPolicy#create?)
    │      └─ Current.account.labels.create!
    │             ├─ before_validation: title.downcase
    │             └─ after_commit: AccountCacheRevalidator
    │
    ├─ update: ラベル更新
    │      └─ @label.update!
    │             └─ after_update_commit: update_associated_models
    │                    └─ Labels::UpdateJob.perform_later
    │
    └─ destroy: ラベル削除
           └─ @label.destroy!

[ラベル名変更時]
Labels::UpdateJob
    │
    └─ Labels::UpdateService.perform
           └─ 関連タグの更新
```

### データフロー図

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

管理者設定                  ラベル保存                     DBへ永続化
(title, color,     ───▶ LabelsController ──────────▶ labelsテーブル
 description)              │
                           ├─ title.downcase (自動変換)
                           └─ uniqueness check

ラベル名変更              Labels::UpdateJob              タグ更新
(title変更時)    ───▶ Labels::UpdateService ───▶ tags, taggings
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| label.rb | `app/models/label.rb` | モデル | データ構造・バリデーション |
| labels_controller.rb | `app/controllers/api/v1/accounts/labels_controller.rb` | コントローラー | CRUD API |
| label_policy.rb | `app/policies/label_policy.rb` | ポリシー | 権限制御 |
| update_job.rb | `app/jobs/labels/update_job.rb` | ジョブ | ラベル名変更時の非同期更新 |
| show.json.jbuilder | `app/views/api/v1/accounts/labels/show.json.jbuilder` | ビュー | JSONレスポンス |
| regex_helper.rb | `app/models/concerns/regex_helper.rb` | concern | UNICODE_CHARACTER_NUMBER_HYPHEN_UNDERSCORE定数 |
