# 機能設計書 43-コメント作成

## 概要

本ドキュメントは、Fat Free CRMのコメント作成機能（CommentsController#create）の設計仕様を定義するものである。

### 本機能の処理概要

コメント作成機能は、取引先・キャンペーン・リード・連絡先・商談などのエンティティに対して新規コメントを追加する機能である。コメント追加時には購読ユーザーへの通知、メンションユーザーの購読追加などの連携処理も実行される。

**業務上の目的・背景**：CRMシステムにおいて、顧客や商談に関する情報共有は業務効率化の要である。コメント機能により、チームメンバーは重要な情報や進捗状況を記録し、他のメンバーに共有できる。メンション機能（@username）で特定ユーザーに通知することで、タイムリーな情報伝達を実現する。

**機能の利用シーン**：ユーザーがエンティティ詳細画面のコメント入力欄に内容を入力し、「追加」ボタンをクリックしてコメントを保存する場面で利用される。商談の進捗報告、顧客からのフィードバック記録、チーム内での情報共有などに使用される。

**主要な処理内容**：
1. コメントパラメータを受信してCommentオブジェクトを生成
2. 対象エンティティへのアクセス権限を検証
3. コメント本文内のメンション（@username）を検出して購読者追加
4. コメントをデータベースに保存
5. 購読ユーザーにメール通知を送信

**関連システム・外部連携**：メール送信（SubscriptionMailer）

**権限による制御**：ユーザーは自分がアクセス可能なエンティティ（my scopeで絞り込み）にのみコメントを追加可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | 取引先詳細画面 | 主画面 | 取引先へのコメント追加 |
| 12 | キャンペーン詳細画面 | 主画面 | キャンペーンへのコメント追加 |
| 16 | リード詳細画面 | 主画面 | リードへのコメント追加 |
| 21 | 連絡先詳細画面 | 主画面 | 連絡先へのコメント追加 |
| 25 | 商談詳細画面 | 主画面 | 商談へのコメント追加 |

## 機能種別

データ作成処理 / CREATE操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| comment[commentable_id] | Integer | Yes | 対象エンティティのID | 存在するエンティティであること |
| comment[commentable_type] | String | Yes | 対象エンティティの種別（Account, Campaign等） | 有効なモデル名であること |
| comment[comment] | Text | Yes | コメント本文 | 空でないこと |
| comment[private] | Boolean | No | プライベートフラグ | true/false |
| comment[title] | String | No | コメントタイトル | - |

### 入力データソース

- 画面入力：コメント入力フォーム
- セッション：current_user（作成者情報）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | 作成されたコメントID |
| user_id | Integer | 作成者ユーザーID |
| commentable_id | Integer | 対象エンティティID |
| commentable_type | String | 対象エンティティ種別 |
| comment | Text | コメント本文 |
| state | String | 表示状態（デフォルト: Expanded） |
| created_at | DateTime | 作成日時 |

### 出力先

- データベース：commentsテーブルへINSERT
- 画面表示：AJAXレスポンスでコメント一覧を更新
- メール：購読ユーザーへ通知メール送信

## 処理フロー

### 処理シーケンス

```
1. コメント作成リクエスト受信
   └─ comment_paramsでパラメータをサニタイズ
   └─ current_user.idをuser_idにマージ

2. 対象エンティティのアクセス権限検証
   └─ find_classでモデルクラス取得
   └─ my(current_user)スコープで検索

3. アクセス権限あり？
   ├─ Yes: コメント保存処理へ
   └─ No: 関連エンティティ未発見エラー応答

4. コメント保存（before_create: subscribe_mentioned_users）
   └─ コメント本文から@usernameを検出
   └─ 該当ユーザーをエンティティの購読者リストに追加

5. コメント保存完了（after_create）
   └─ subscribe_user_to_entity: 作成者を購読者リストに追加
   └─ notify_subscribers: 購読者にメール通知送信

6. レスポンス生成
   └─ create.js.hamlでJavaScript応答
```

### フローチャート

```mermaid
flowchart TD
    A[コメント作成リクエスト] --> B[パラメータ取得・サニタイズ]
    B --> C[対象エンティティ取得]
    C --> D{アクセス権限あり?}
    D -->|No| E[エラー応答]
    D -->|Yes| F[メンションユーザー検出]
    F --> G[メンションユーザーを購読者追加]
    G --> H[コメント保存]
    H --> I{保存成功?}
    I -->|No| J[バリデーションエラー応答]
    I -->|Yes| K[作成者を購読者追加]
    K --> L[購読者にメール通知]
    L --> M[JavaScript応答生成]
    E --> N[終了]
    J --> N
    M --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-43-01 | メンション購読 | コメント本文に@usernameが含まれる場合、該当ユーザーをエンティティの購読者に追加 | コメント作成時 |
| BR-43-02 | 作成者購読 | コメント作成者を自動的にエンティティの購読者に追加 | コメント作成成功時 |
| BR-43-03 | 通知送信 | コメント作成時、購読者（作成者以外）にメール通知を送信 | 購読者が存在し、通知設定がONの場合 |
| BR-43-04 | アクセス制御 | ユーザーは自分がアクセス可能なエンティティにのみコメント追加可能 | 常時 |

### 計算ロジック

メンション検出ロジック：
```ruby
comment.scan(/@([a-zA-Z0-9_-]+)/).map(&:first)
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| コメント作成 | comments | INSERT | 新規コメントレコード作成 |
| 購読者更新 | entities※ | UPDATE | subscribed_usersフィールド更新 |
| バージョン履歴 | versions | INSERT | 変更履歴レコード作成（paper_trail） |

※ entities = accounts, campaigns, leads, contacts, opportunities のいずれか

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

#### comments

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | user_id | current_user.id | 作成者ID |
| INSERT | commentable_id | params[:commentable_id] | 対象エンティティID |
| INSERT | commentable_type | params[:commentable_type] | 対象エンティティ種別 |
| INSERT | comment | params[:comment] | コメント本文 |
| INSERT | private | params[:private] | プライベートフラグ |
| INSERT | state | "Expanded" | デフォルト値 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | アクセス権限エラー | 対象エンティティにアクセス権限なし | エラーメッセージ表示 |
| - | バリデーションエラー | コメント本文が空 | 入力フォームにエラー表示 |

### リトライ仕様

リトライ不要（ユーザーが再入力して再送信）

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

- コメント保存と購読者更新は個別のトランザクションで実行
- メール送信は非同期（deliver_later）

## パフォーマンス要件

- コメント保存は500ms以内に応答
- メール送信は非同期で遅延実行

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

- 認証必須：ApplicationControllerで認証チェック
- アクセス制御：my(current_user)スコープでユーザーのアクセス可能なエンティティに限定
- パラメータサニタイズ：Strong Parametersで許可パラメータを制限
- XSS対策：コメント本文はビューでエスケープ処理

## 備考

- メール通知はsubscribe_to_comment_replies設定がONのユーザーにのみ送信
- 変更履歴はpaper_trail gemで自動記録

---

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

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

### 推奨読解順序

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

コメントモデルの構造とコールバックを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | comment.rb | `app/models/polymorphic/comment.rb` | バリデーション（30行目）、コールバック（34-35行目） |

**読解のコツ**: before_createでメンションユーザーの購読追加、after_createで作成者の購読追加と通知送信が行われる。

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

createアクションの処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | comments_controller.rb | `app/controllers/comments_controller.rb` | createアクション（45-58行目） |

**主要処理フロー**:
1. **46-48行目**: Commentオブジェクト生成、user_idをマージ
2. **50-51行目**: 対象エンティティのモデルクラスとID取得
3. **52行目**: my(current_user)でアクセス権限チェック
4. **53-54行目**: 保存成功時のレスポンス
5. **56行目**: アクセス権限なしの場合のエラー処理

#### Step 3: コールバック処理を理解する

メンション検出と通知送信のロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | comment.rb | `app/models/polymorphic/comment.rb` | subscribe_mentioned_usersメソッド（63-70行目） |
| 3-2 | comment.rb | `app/models/polymorphic/comment.rb` | subscribe_user_to_entityメソッド（48-51行目） |
| 3-3 | comment.rb | `app/models/polymorphic/comment.rb` | notify_subscribersメソッド（54-59行目） |

**主要処理フロー**:
- **66行目**: 正規表現でコメント本文から@usernameを検出
- **67-69行目**: ユーザー名でユーザーを検索し、購読者リストに追加
- **55行目**: 購読者リストからコメント作成者を除外
- **57行目**: メール通知を非同期送信

#### Step 4: ビューレスポンスを理解する

JavaScript応答の生成を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | create.js.haml | `app/views/comments/create.js.haml` | DOM更新処理（1-14行目） |

**主要処理フロー**:
- **4行目**: バリデーション成功時、コメントをDOMに追加
- **5行目**: 新規コメントをハイライト表示
- **6-8行目**: 入力フォームをリセット

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

```
CommentsController#create
    │
    ├─ Comment.new (with user_id merge)
    │
    ├─ find_class (inherited from ApplicationController)
    │      └─ モデルクラス取得
    │
    ├─ my(current_user).find_by_id
    │      └─ アクセス権限チェック
    │
    └─ @comment.save
           │
           ├─ before_create: subscribe_mentioned_users
           │      └─ メンションユーザーを購読者追加
           │
           └─ after_create
                  ├─ subscribe_user_to_entity
                  │      └─ 作成者を購読者追加
                  │
                  └─ notify_subscribers
                         └─ SubscriptionMailer.comment_notification
```

### データフロー図

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

フォーム入力 ───▶ CommentsController#create ───▶ データベース
  ├─ commentable_id           │                             └─ comments
  ├─ commentable_type         ├─ アクセス権限チェック
  └─ comment                  ├─ メンション検出               ───▶ メール通知
                               ├─ コメント保存                    └─ 購読者へ
                               └─ 購読者更新
                                                              ───▶ JavaScript応答
                                                                   └─ DOM更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| comments_controller.rb | `app/controllers/comments_controller.rb` | コントローラー | createアクション |
| comment.rb | `app/models/polymorphic/comment.rb` | モデル | バリデーション、コールバック |
| create.js.haml | `app/views/comments/create.js.haml` | ビュー | JavaScript応答テンプレート |
| _new.html.haml | `app/views/comments/_new.html.haml` | ビュー | コメント入力フォーム |
| routes.rb | `config/routes.rb` | 設定 | ルーティング設定（41行目） |
