# 機能設計書 24-カードをカラムに振り分け（トリアージ）

## 概要

本ドキュメントは、Fizzyアプリケーションにおけるカードのトリアージ機能（カラムへの振り分け）の設計を記載するものです。

### 本機能の処理概要

トリアージ機能は、トリアージ待ち（ストリーム）のカードを特定のカラムに振り分ける機能です。カンバンボードのワークフローにおいて、新規に作成されたカードを適切なカラムに分類する際に使用されます。

**業務上の目的・背景**：カンバンボードでは、新しいタスク（カード）は最初にストリーム（トリアージ待ち）に配置されます。チームメンバーは定期的にストリームを確認し、各カードを適切なカラム（例：To Do、In Progress等）に振り分けることで、タスクの進捗管理を行います。この作業を「トリアージ」と呼び、タスクの優先順位付けと整理を効率的に行うことができます。

**機能の利用シーン**：
- 新規カードをストリームから特定のカラムに移動する場合
- カードの優先順位を変更してカラムを切り替える場合
- ボードの整理作業を行う場合

**主要な処理内容**：
1. 対象カラムの検証（同じボード内のカラムであること）
2. カードの延期状態（Not Now）の解除
3. カードをカラムに紐付け
4. トリアージイベントの記録

**関連システム・外部連携**：Webhookが設定されている場合、トリアージイベントが通知されます。

**権限による制御**：カードにアクセス可能なユーザーであれば誰でもトリアージ操作を実行できます。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 10 | ボード詳細画面 | 主画面 | ストリームからカラムへのドラッグ＆ドロップ |
| 19 | カード詳細画面 | 参照画面 | カラム選択UI |
| 24 | カードカラム変更画面 | 参照画面 | カラム選択ボタン |

## 機能種別

CRUD操作（UPDATE） / ステータス変更

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| card_id | String | Yes | カード番号（number） | 存在するカードであること |
| column_id | UUID | Yes | 振り分け先のカラムID | 同じボード内のカラムであること |

### 入力データソース

- URL パラメータからカード番号を取得
- リクエストパラメータから column_id を取得

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| redirect_to | URL | カード詳細画面へのリダイレクト（HTML形式） |
| HTTP Status | Integer | 成功時: 204 No Content（JSON形式） |

### 出力先

- HTML形式：カード詳細画面へリダイレクト
- JSON形式：HTTP 204 No Content

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信（POST /cards/:card_id/triage）
   └─ カード番号と振り分け先カラムIDを取得

2. カード取得
   └─ CardScoped concernによりカードを取得

3. カラム取得と検証
   └─ @card.board.columns から対象カラムを取得
   └─ 同じボードに属するカラムであることを検証

4. トリアージ処理実行
   └─ @card.triage_into(column)
       ├─ 延期状態（Not Now）を解除
       ├─ カラムを更新
       └─ トリアージイベントを記録

5. レスポンス返却
   └─ HTML: カード詳細画面へリダイレクト
   └─ JSON: HTTP 204 No Content
```

### フローチャート

```mermaid
flowchart TD
    A[トリアージリクエスト受信] --> B[カード取得]
    B --> C[カラム取得]
    C --> D{同じボードのカラム?}
    D -->|No| E[エラー: 例外発生]
    D -->|Yes| F[resume: 延期解除]
    F --> G[カラム更新]
    G --> H[イベント記録]
    H --> I{レスポンス形式}
    I -->|HTML| J[カード画面へリダイレクト]
    I -->|JSON| K[204 No Content]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-24-01 | 同一ボード制約 | カラムは同じボード内のカラムでなければならない | すべてのトリアージ操作 |
| BR-24-02 | 延期解除 | トリアージ時にNot Now状態は自動解除される | 延期中のカードをトリアージした場合 |
| BR-24-03 | クローズ解除 | トリアージ時にクローズ状態は自動解除される | クローズ済みのカードをトリアージした場合 |
| BR-24-04 | イベント記録 | トリアージイベントにはカラム名が記録される | すべてのトリアージ操作 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| カラム更新 | cards | UPDATE | column_id を更新 |
| 延期解除 | card_not_nows | DELETE | 延期レコードを削除 |
| クローズ解除 | closures | DELETE | クローズレコードを削除 |
| イベント記録 | events | INSERT | トリアージイベントを記録 |

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

#### cards

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | column_id | 指定されたカラムのID | カラム紐付け |
| UPDATE | updated_at | 現在時刻 | touch |

#### events

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | action | "triaged" | イベント種別 |
| INSERT | particulars | { column: column.name } | カラム名を記録 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 404 | RecordNotFound | 指定されたカードまたはカラムが存在しない | 適切なエラー画面を表示 |
| 500 | RuntimeError | カラムがカードのボードに属していない | 例外メッセージを表示 |

### リトライ仕様

冪等性あり（同じカラムへの再トリアージは状態を変えない）

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

triage_into メソッド内で transaction ブロックを使用し、resume処理、カラム更新、イベント記録を一括で処理します。いずれかが失敗した場合はロールバックされます。

## パフォーマンス要件

- トリアージ処理は500ms以内に完了すること

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

- 認証：ログインユーザーのみアクセス可能
- 認可：カードにアクセス可能なユーザーのみ操作可能
- CSRF対策：Rails標準のCSRFトークン検証を適用

## 備考

- ドラッグ＆ドロップによる直感的な操作が可能
- 複数のカードを連続してトリアージ可能

---

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

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

### 推奨読解順序

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

トリアージ処理の仕組みを理解するために、Triageableモジュールを把握することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | triageable.rb | `app/models/card/triageable.rb` | Triageableモジュールでトリアージ関連メソッドを定義 |
| 1-2 | card.rb | `app/models/card.rb` | Cardモデルの全体構造。Triageableモジュールのinclude |

**読解のコツ**: Card::Triageable モジュールには `triage_into` メソッドがあり、これがメインの処理です。`belongs_to :column` の関連も確認してください。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | triages_controller.rb | `app/controllers/cards/triages_controller.rb` | createアクションがトリアージのエントリーポイント |
| 2-2 | card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | CardScopedでカード取得処理を共通化 |

**主要処理フロー**:
1. **5行目**: カラムの取得（`@card.board.columns.find`）
2. **6行目**: `@card.triage_into(column)` - トリアージ処理の実行

#### Step 3: トリアージ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | triageable.rb | `app/models/card/triageable.rb` | triage_intoメソッドの実装詳細 |
| 3-2 | postponable.rb | `app/models/card/postponable.rb` | resumeメソッドの実装（延期解除） |

**主要処理フロー**:
- **19-27行目**: `triage_into`メソッド
  - **20行目**: ボードチェック（同一ボード制約）
  - **22-26行目**: トランザクション内で処理
    - **23行目**: `resume` - 延期・クローズ状態を解除
    - **24行目**: `update! column: column` - カラム更新
    - **25行目**: `track_event "triaged"` - イベント記録

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

```
Cards::TriagesController#create
    │
    ├─ CardScoped#set_card (before_action)
    │      └─ Current.user.accessible_cards.find_by!(number: params[:card_id])
    │
    ├─ @card.board.columns.find(params[:column_id])
    │      └─ Column (取得)
    │
    └─ @card.triage_into(column)
           └─ Card::Triageable#triage_into
                  ├─ ボードチェック（例外発生の可能性）
                  ├─ resume
                  │      └─ Card::Postponable#resume
                  │             ├─ reopen (クローズ解除)
                  │             ├─ activity_spike&.destroy
                  │             └─ not_now&.destroy
                  ├─ update!(column: column)
                  └─ track_event "triaged"
```

### データフロー図

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

POST /cards/:id/triage ──▶ TriagesController#create ──▶ redirect/204
  │                              │
  │ card_id                      ├─ @card.board.columns.find
  │ column_id                    │    └─ Column (取得)
  │                              │
  │                              └─ @card.triage_into
  │                                   ├─ resume
  │                                   │    └─ not_now/closure 削除
  │                                   ├─ column 更新
  │                                   └─ イベント記録
  │
  └─ cards (UPDATE), card_not_nows/closures (DELETE), events (INSERT)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| triages_controller.rb | `app/controllers/cards/triages_controller.rb` | コントローラー | トリアージリクエストの処理 |
| card_scoped.rb | `app/controllers/concerns/card_scoped.rb` | コントローラー(concern) | カード取得処理の共通化 |
| triageable.rb | `app/models/card/triageable.rb` | モデル(concern) | トリアージ処理ロジック |
| postponable.rb | `app/models/card/postponable.rb` | モデル(concern) | 延期解除処理（resume） |
| closeable.rb | `app/models/card/closeable.rb` | モデル(concern) | クローズ解除処理（reopen） |
| card.rb | `app/models/card.rb` | モデル | カードモデル定義 |
| edit.html.erb | `app/views/cards/columns/edit.html.erb` | ビュー | カラム選択UI |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（88行目） |
