# 機能設計書 49-メンション機能

## 概要

本ドキュメントは、Chatwootにおける@メンション通知機能の設計を記述します。

### 本機能の処理概要

プライベートノート内で@メンション記法を使用して、特定のユーザーまたはチームに通知を送信する機能を提供します。メンションされたユーザーは通知を受け取り、自動的に会話の参加者として追加されます。

**業務上の目的・背景**：エージェントが複雑な問い合わせを処理する際、専門知識を持つ他のエージェントやチームの助けが必要になることがあります。メンション機能により、プライベートノートで特定のユーザーやチームを呼び出し、迅速に協力を得ることができます。これにより、チーム間のコラボレーションが促進され、顧客への回答品質が向上します。

**機能の利用シーン**：
- エージェントが技術的な質問に対して専門家の助けを求める場面
- 上司やチームリーダーにエスカレーションする場面
- 特定のチーム全体に対応を依頼する場面
- 複数のエージェントで協力して顧客対応を行う場面

**主要な処理内容**：
1. プライベートノート内のメンション記法の解析
2. ユーザーメンションとチームメンションの展開
3. メンション通知の生成
4. メンションされたユーザーの会話参加者への追加
5. Slackへの同期（統合が有効な場合）

**関連システム・外部連携**：
- 通知システム（NotificationBuilder）
- Slack統合（Conversations::UserMentionJob）
- 会話参加者管理

**権限による制御**：メンション可能なユーザーはインボックスのメンバーまたはアカウント管理者に限定されます。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 11 | 会話詳細画面 | 主画面 | @メンションによる通知 |
| 15 | メンション会話一覧 | 参照画面 | メンションされた会話の一覧表示 |

## 機能種別

通知 / データ処理

## 入力仕様

### 入力パラメータ

メッセージ内容に含まれるメンション記法：

| パターン | 形式 | 説明 |
|---------|------|------|
| ユーザーメンション | `(mention://user/{user_id}/{user_name})` | 特定ユーザーへのメンション |
| チームメンション | `(mention://team/{team_id}/{team_name})` | チーム全体へのメンション |

### 入力データソース

- 画面入力：会話詳細画面のプライベートノート入力
- API：メッセージ作成APIのcontent内のメンション記法

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| notifications | Array | メンション通知レコード |
| conversation_participants | Array | 追加された会話参加者レコード |
| mentions | Array | メンションレコード（Slackへの同期用） |

### 出力先

- データベース：notifications, conversation_participants, mentionsテーブル
- Slack：統合が有効な場合

## 処理フロー

### 処理シーケンス

```
1. メッセージ作成
   └─ プライベートノートとして作成される
2. MentionService呼び出し
   └─ MESSAGE_CREATEDイベントでトリガー
3. バリデーションチェック
   └─ private? && content.present? && mentioned_ids.present?
4. チームメンション展開
   └─ チームIDからユーザーIDリストを取得
5. メンション可能ユーザーでフィルタリング
   └─ インボックスメンバー + 管理者に限定
6. Slack同期ジョブエンキュー
   └─ Conversations::UserMentionJob.perform_later
7. 通知生成
   └─ NotificationBuilderでconversation_mention通知を作成
8. 参加者追加
   └─ conversation_participants.find_or_create_by
```

### フローチャート

```mermaid
flowchart TD
    A[プライベートノート作成] --> B{valid_mention_message?}
    B -->|No| C[処理終了]
    B -->|Yes| D[メンション記法解析]
    D --> E[ユーザーID抽出]
    D --> F[チームID抽出]
    F --> G[チームメンバー展開]
    E --> H[ユーザーIDマージ]
    G --> H
    H --> I[メンション可能ユーザーでフィルタ]
    I --> J{フィルタ結果あり?}
    J -->|No| C
    J -->|Yes| K[Slack同期ジョブ]
    K --> L[通知生成]
    L --> M[参加者追加]
    M --> N[処理完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-49-001 | プライベートノート限定 | メンションはプライベートノートでのみ有効 | message.private? |
| BR-49-002 | メンション可能ユーザー | インボックスメンバーまたはアカウント管理者のみメンション可能 | 常に適用 |
| BR-49-003 | チームメンション展開 | チームメンションはそのチームの全メンバーに展開される | チームメンション時 |
| BR-49-004 | 重複排除 | 同一ユーザーへの複数メンションは重複排除される | ユーザーIDマージ時 |
| BR-49-005 | 参加者自動追加 | メンションされたユーザーは自動的に会話参加者になる | メンション成功時 |

### 計算ロジック

**メンションID抽出**:
```ruby
# ユーザーメンション
user_mentions = content.scan(%r{\(mention://user/(\d+)/(.+?)\)}).map(&:first)
# チームメンション
team_mentions = content.scan(%r{\(mention://team/(\d+)/(.+?)\)}).map(&:first)
```

**メンション可能ユーザー**:
```ruby
inbox.account.administrators.pluck(:id) + inbox.members.pluck(:id)
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 通知作成 | notifications | INSERT | メンション通知の作成 |
| 参加者追加 | conversation_participants | INSERT | 会話参加者の追加 |
| メンション記録 | mentions | INSERT | Slack同期用のメンション記録 |

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

#### notifications

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | notification_type | conversation_mention | メンション通知タイプ |
| INSERT | user_id | メンションされたユーザーID | - |
| INSERT | primary_actor | conversation | ポリモーフィック |
| INSERT | secondary_actor | message | ポリモーフィック |

#### conversation_participants

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | conversation_id | 対象会話ID | - |
| INSERT | user_id | メンションされたユーザーID | find_or_create_by |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 無視 | メンション可能でないユーザーがメンションされた | フィルタリングで除外（エラーではない） |
| - | 無視 | プライベートノート以外でメンション | 処理をスキップ（エラーではない） |

### リトライ仕様

Conversations::UserMentionJobはSidekiqのデフォルトリトライに従います。

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

各通知と参加者の追加は個別に行われ、1つの失敗が他に影響しません。

## パフォーマンス要件

- チームメンバー展開はJOINクエリで効率的に実行
- 重複ユーザーIDは配列操作で除外

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

- メンション可能ユーザーをインボックスメンバーと管理者に制限
- 正規表現でメンション記法を厳密に解析
- プライベートノートは顧客に表示されない

## 備考

- メンション記法はフロントエンドで入力時に生成されます
- Slack統合が有効な場合、メンションはSlackにも通知されます
- メンションされた会話は「メンション会話一覧」で確認できます

---

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

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

### 推奨読解順序

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

まず、メンションに関連するモデルを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | mention.rb | `app/models/mention.rb` | メンションモデルの構造（存在する場合） |
| 1-2 | notification.rb | `app/models/notification.rb` | 通知タイプ（conversation_mention） |
| 1-3 | conversation_participant.rb | `app/models/conversation_participant.rb` | 会話参加者モデル |

**読解のコツ**: メンション機能は複数のモデル（通知、参加者、メンション）に影響します。

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

MentionServiceの処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | mention_service.rb | `app/services/messages/mention_service.rb` | 全体（1-68行目） |

**主要処理フロー**:
1. **4-13行目**: performメソッドで全体の流れを制御
2. **17-19行目**: valid_mention_message?でバリデーション
3. **21-28行目**: mentioned_idsでメンションIDを抽出
4. **30-38行目**: expand_team_mentions_to_usersでチームメンション展開
5. **40-45行目**: valid_mentionable_user_idsでメンション可能ユーザーを取得
6. **47-49行目**: filter_mentioned_ids_by_inboxでフィルタリング

#### Step 3: 通知生成を理解する

NotificationBuilderの呼び出しを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | mention_service.rb | `app/services/messages/mention_service.rb` | generate_notifications_for_mentions（51-60行目） |

**主要処理フロー**:
- **51-60行目**: 各メンションユーザーに対してNotificationBuilderを呼び出し
- **54行目**: notification_type: 'conversation_mention'を設定

#### Step 4: 参加者追加を理解する

会話参加者への追加処理を確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | mention_service.rb | `app/services/messages/mention_service.rb` | add_mentioned_users_as_participants（63-67行目） |

**主要処理フロー**:
- **63-67行目**: find_or_create_byで参加者を追加（既存の場合は何もしない）

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

```
MESSAGE_CREATED Event
    │
    └─ Messages::MentionService#perform
           │
           ├─ valid_mention_message?
           │      └─ message.private? && content.present? && mentioned_ids.present?
           │
           ├─ mentioned_ids
           │      ├─ user_mentions (正規表現で抽出)
           │      └─ team_mentions (正規表現で抽出)
           │             │
           │             └─ expand_team_mentions_to_users
           │                    └─ team.team_members.pluck(:user_id)
           │
           ├─ filter_mentioned_ids_by_inbox
           │      └─ valid_mentionable_user_ids
           │             └─ administrators.pluck(:id) + inbox.members.pluck(:id)
           │
           ├─ Conversations::UserMentionJob.perform_later
           │
           ├─ generate_notifications_for_mentions
           │      └─ NotificationBuilder#perform
           │             └─ Notification.create!
           │
           └─ add_mentioned_users_as_participants
                  └─ conversation_participants.find_or_create_by
```

### データフロー図

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

message.content     ───▶ MentionService#perform        ───▶ mentioned_ids (抽出)
(プライベートノート)          │
                              │
                              ├─▶ チームメンション展開      ───▶ expanded_user_ids
                              │
                              ├─▶ フィルタリング           ───▶ validated_mentioned_ids
                              │
                              ├─▶ UserMentionJob          ───▶ Slack (Webhook)
                              │
                              ├─▶ NotificationBuilder     ───▶ DB (notifications)
                              │
                              └─▶ find_or_create_by       ───▶ DB (conversation_participants)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| mention_service.rb | `app/services/messages/mention_service.rb` | サービス | メンション処理の中核ロジック |
| notification_builder.rb | `app/builders/notification_builder.rb` | ビルダー | 通知の生成 |
| user_mention_job.rb | `app/jobs/conversations/user_mention_job.rb` | ジョブ | Slack同期 |
| conversation_participant.rb | `app/models/conversation_participant.rb` | モデル | 会話参加者 |
| notification.rb | `app/models/notification.rb` | モデル | 通知 |
| mention.rb | `app/models/mention.rb` | モデル | メンション記録 |
