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

## 概要

本ドキュメントは、カードにコメントを追加する機能の設計を定義するものである。

### 本機能の処理概要

**業務上の目的・背景**：カンバンボードでのタスク管理において、コメント機能はチームメンバー間のコミュニケーションの中核を担う。コメント作成機能は、タスクに対する質問、回答、進捗報告、議論などを記録し、チームの情報共有を実現する。リッチテキスト形式により、画像添付や書式設定、メンション（@ユーザー名）による通知も可能で、効率的なコラボレーションを支援する。

**機能の利用シーン**：
- タスクに対する質問や意見を投稿したいとき
- 進捗報告や作業内容を記録したいとき
- チームメンバーにメンションで通知したいとき
- 画像やファイルを共有したいとき

**主要な処理内容**：
1. POSTリクエストでコメント本文を受け取る
2. カードがコメント可能な状態かを確認
3. コメントをデータベースに新規登録
4. コメント作成者をカードのウォッチャーに自動追加
5. Turbo StreamまたはJSONレスポンスで結果を返却

**関連システム・外部連携**：
- メンション機能により、対象ユーザーに通知が送信される
- カードのウォッチャーへの通知トリガーとなる

**権限による制御**：
- カードへのアクセス権限を持つユーザーのみがコメント可能
- カードが「commentable」な状態である必要がある（ドラフト状態等でない）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 19 | カード詳細画面 | 主機能 | カードにコメントを追加 |

## 機能種別

CRUD操作（Create）/ コミュニケーション機能

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| card_id | Integer | Yes | 対象カードのID（URLパラメータ） | 存在確認、アクセス権限確認 |
| comment[body] | RichText | Yes | コメント本文（リッチテキスト） | 必須 |
| comment[created_at] | DateTime | No | 作成日時（API経由での指定用） | - |

### 入力データソース

- URLパラメータからカードID
- フォームデータまたはJSONからコメント本文

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| @comment | Comment | 作成されたコメントオブジェクト |

### 出力先

- Turbo Stream: 画面の動的更新（コメント挿入 + 新規コメントフォームリセット）
- JSON: API利用時のレスポンス（201 Created + Locationヘッダー）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ POST /cards/:card_id/comments へのリクエストを受信

2. 認証・認可確認
   └─ CardScopedモジュールのset_cardでカードアクセス権限を確認

3. コメント可能性確認
   └─ ensure_card_is_commentableでカードがコメント可能か確認

4. コメント作成
   └─ @card.comments.create!(comment_params) でコメントを新規作成

5. ウォッチャー自動追加
   └─ after_create_commit :watch_card_by_creatorでウォッチ登録

6. レスポンス生成
   ├─ Turbo Stream: create.turbo_stream.erb をレンダリング
   └─ JSON: 201 Created + Locationヘッダーを返却
```

### フローチャート

```mermaid
flowchart TD
    A[POST /cards/:card_id/comments] --> B{認証済み?}
    B -->|No| C[401 Unauthorized]
    B -->|Yes| D[set_card: カード取得]
    D --> E{カードアクセス可能?}
    E -->|No| F[404 Not Found]
    E -->|Yes| G{カードはcommentable?}
    G -->|No| H[403 Forbidden]
    G -->|Yes| I[comment_paramsでパラメータ抽出]
    I --> J[comments.create!でコメント作成]
    J --> K[watch_card_by_creator]
    K --> L{リクエスト形式}
    L -->|Turbo Stream| M[create.turbo_stream.erb]
    L -->|JSON| N[201 Created + Location]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-46-01 | カードスコープ | コメントはカードに紐づいて作成される | 常時 |
| BR-46-02 | コメント可能確認 | カードがcommentable?を満たす必要がある | 常時 |
| BR-46-03 | 作成者自動設定 | コメントの作成者はCurrent.userが自動設定される | コメント作成時 |
| BR-46-04 | アカウント自動設定 | コメントのaccount_idはカードのaccount_idが自動設定される | コメント作成時 |
| BR-46-05 | ウォッチャー自動追加 | コメント作成者は自動的にカードのウォッチャーになる | コメント作成後 |
| BR-46-06 | カード更新日時 | コメント作成時にカードのupdated_atが更新される | touch: trueにより |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カード取得 | cards | SELECT | 対象カードの存在確認とアクセス権限確認 |
| コメント作成 | comments | INSERT | 新しいコメントレコードを登録 |
| リッチテキスト作成 | action_text_rich_texts | INSERT | コメント本文を保存 |
| カード更新 | cards | UPDATE | touch: trueによりupdated_atを更新 |
| ウォッチ登録 | watches | INSERT/UPDATE | コメント作成者をウォッチャーに追加 |

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

#### comments

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | id | UUID自動生成 | 主キー |
| INSERT | account_id | card.account_id | 自動設定 |
| INSERT | card_id | params[:card_id] | URLパラメータから |
| INSERT | creator_id | Current.user.id | 自動設定 |
| INSERT | created_at | 現在時刻またはparams指定値 | - |
| INSERT | updated_at | 現在時刻 | 自動設定 |

#### action_text_rich_texts

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | name | 'body' | リッチテキスト属性名 |
| INSERT | body | params[:comment][:body] | コメント本文 |
| INSERT | record_type | 'Comment' | ポリモーフィック |
| INSERT | record_id | @comment.id | ポリモーフィック |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | 未認証状態でアクセス | ログイン画面へリダイレクト |
| 403 | Forbidden | カードがコメント不可状態 | エラーメッセージ表示 |
| 404 | Not Found | カードが存在しない/アクセス権限なし | エラーメッセージ表示 |
| 422 | Unprocessable Entity | バリデーションエラー | エラーメッセージ表示 |

### リトライ仕様

特になし

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

- `create!` メソッドにより、コメント作成、リッチテキスト保存、カードtouch更新が同一トランザクション内で実行される
- `after_create_commit` によりウォッチャー登録はコミット後に実行される

## パフォーマンス要件

- コメント作成のレスポンス時間: 500ms以内

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

- 認証必須: ログイン済みユーザーのみアクセス可能
- 認可: CardScopedモジュールにより、アクセス可能なカードのみ操作可能
- コメント可能確認: ensure_card_is_commentableでカード状態を検証
- Strong Parameters: `params.expect(comment: [ :body, :created_at ])` でパラメータをホワイトリスト制御
- XSS対策: Action Textによるサニタイズ

## 備考

- コメント本文はリッチテキストで、画像添付やリンク、書式設定が可能
- @ユーザー名でメンション可能（Mentionsモジュール）
- ローカルストレージにドラフト保存可能（local_save controller）

---

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

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

### 推奨読解順序

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

まず、コメントのデータ構造とリレーションを理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | comment.rb | `app/models/comment.rb` | Commentモデルの定義、includes、バリデーション、コールバック |
| 1-2 | schema.rb | `db/schema.rb` | commentsテーブルの構造（252-260行目） |

**読解のコツ**:
- `has_rich_text :body` によりAction Textでリッチテキストを管理
- `belongs_to :creator, class_name: "User", default: -> { Current.user }` で作成者を自動設定
- `validate :card_is_commentable` でカードの状態を検証
- `after_create_commit :watch_card_by_creator` でコミット後にウォッチャー登録

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

処理の起点となるコントローラーを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | comments_controller.rb | `app/controllers/cards/comments_controller.rb` | createアクションの実装（12-19行目） |
| 2-2 | card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | カードスコープの共通処理 |

**主要処理フロー**:
1. **6行目**: `before_action :ensure_card_is_commentable, only: :create` でコメント可能性確認
2. **12-19行目**: `create` アクションでコメント作成
3. **13行目**: `@comment = @card.comments.create!(comment_params)` で作成
4. **54-56行目**: `ensure_card_is_commentable` でカード状態を検証

#### Step 3: ビューレンダリングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | create.turbo_stream.erb | `app/views/cards/comments/create.turbo_stream.erb` | Turbo Streamレスポンス |
| 3-2 | _new.html.erb | `app/views/cards/comments/_new.html.erb` | 新規コメントフォーム |
| 3-3 | _comment.html.erb | `app/views/cards/comments/_comment.html.erb` | コメント部分テンプレート |

**主要処理フロー**:
- **create.turbo_stream.erb 1行目**: `turbo_stream.before` でnew_comment位置の前にコメントを挿入
- **create.turbo_stream.erb 3行目**: `turbo_stream.update` でnew_commentフォームをリセット
- **_new.html.erb**: ローカルストレージ保存対応のコメントフォーム

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

```
POST /cards/:card_id/comments
    │
    ├─ CardScoped#set_card
    │      └─ Current.user.accessible_cards.find_by!(number: params[:card_id])
    │
    ├─ Cards::CommentsController#ensure_card_is_commentable
    │      └─ head :forbidden unless @card.commentable?
    │
    └─ Cards::CommentsController#create
           │
           ├─ @card.comments.create!(comment_params)
           │      └─ Comment.create!
           │             ├─ validates card_is_commentable
           │             ├─ belongs_to :card, touch: true
           │             └─ after_create_commit :watch_card_by_creator
           │                    └─ card.watch_by creator
           │
           └─ respond_to
                  ├─ format.turbo_stream
                  │      └─ create.turbo_stream.erb
                  │             ├─ turbo_stream.before (コメント挿入)
                  │             └─ turbo_stream.update (フォームリセット)
                  │
                  └─ format.json
                         └─ head :created, location: ...
```

### データフロー図

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

POST リクエスト ───────▶ Cards::CommentsController#create
  ├─ card_id                   │
  └─ comment[body]             ├─▶ ensure_card_is_commentable
                               │      └─▶ @card.commentable?
                               │
                               ├─▶ comment_params
                               │      └─▶ { body:, created_at: }
                               │
                               └─▶ @card.comments.create!
                                      │
                                      ├─▶ DB保存
                                      │   (comments, action_text_rich_texts)
                                      │
                                      └─▶ after_create_commit
                                             └─▶ watch_card_by_creator
                                                    │
                                                    ▼
                                             ウォッチ登録 (watches)
                                                    │
                                                    ▼
                                    ┌───────────────┴───────────────┐
                                    │                               │
                             Turbo Stream                        JSON
                             (create.turbo_stream.erb)     (201 Created)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| comments_controller.rb | `app/controllers/cards/comments_controller.rb` | コントローラー | コメントCRUD処理 |
| card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | Concern | カードスコープ共通処理 |
| comment.rb | `app/models/comment.rb` | モデル | コメントのデータモデル定義 |
| create.turbo_stream.erb | `app/views/cards/comments/create.turbo_stream.erb` | ビュー | Turbo Streamレスポンス |
| _new.html.erb | `app/views/cards/comments/_new.html.erb` | ビュー | 新規コメントフォーム |
| _comment.html.erb | `app/views/cards/comments/_comment.html.erb` | ビュー | コメント部分テンプレート |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義 |
| schema.rb | `db/schema.rb` | スキーマ | commentsテーブル定義 |
