# 機能設計書 124-Todoリスト

## 概要

本ドキュメントは、GitLabにおけるTodoリスト機能の設計仕様を記述したものである。Todoリストは、ユーザーに対してアクション（確認・対応）が必要な項目を一元管理し、効率的なタスク管理を支援するパーソナルナビゲーション機能である。

### 本機能の処理概要

**業務上の目的・背景**：開発プロジェクトでは、イシューへのアサイン、マージリクエストのレビュー依頼、メンション通知など、多様なアクションがユーザーに要求される。Todoリストは、これらのアクション項目を「やることリスト」として集約し、ユーザーが見落としなく対応できるようにする。タスクの完了（Done）状態管理により、進捗の可視化も実現する。

**機能の利用シーン**：ナビゲーションメニューの「To-Do」リンクから表示される。日々の業務開始時に未対応タスクを確認する場面、特定のイシュー・MRに関連するTodoを検索する場面、完了済みTodoを確認する場面などで利用される。

**主要な処理内容**：
1. ユーザーに紐づくTodoの一覧取得
2. 状態（pending/done）によるフィルタリング
3. アクション種別（assigned/mentioned/review_requested等）によるフィルタリング
4. スヌーズ（一時非表示）状態の管理
5. Todoの完了（Done）マーク処理
6. 内部イベントの追跡（view_todo_list）

**関連システム・外部連携**：GraphQL APIからもTodo一覧にアクセス可能。イベント発生時に自動でTodoが生成される（TodoService経由）。

**権限による制御**：ユーザーは自身のTodoのみ表示・操作可能。Todoの対象（イシュー・MR等）への閲覧権限がない場合、そのTodoは非表示となる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 15 | To-Do一覧 | 主画面 | ユーザーのTo-Doリスト表示・管理 |
| 13 | 課題一覧 | 遷移先 | Todo対象イシューへの遷移 |
| 14 | マージリクエスト一覧 | 遷移先 | Todo対象MRへの遷移 |

## 機能種別

データ取得・表示（READ操作）/ 状態更新（UPDATE操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| state | String/Array | No | 状態フィルタ（pending/done/all） | pending, done, allのいずれか |
| action_id | Integer/Array | No | アクション種別ID | Todoモデルの有効なaction値 |
| author_id | Integer | No | 作成者ID（NONE='0'で指定なし） | 有効なユーザーID |
| project_id | Integer | No | プロジェクトID | 有効なプロジェクトID |
| group_id | Integer | No | グループID | 有効なグループID |
| type | String/Array | No | 対象種別（Issue/MergeRequest等） | TODO_TYPESに含まれる種別 |
| target_id | Integer | No | 対象ID | 正の整数 |
| is_snoozed | Boolean | No | スヌーズ中のみ表示 | true/false |
| sort | String | No | ソート順 | created_desc/created_asc等 |

### 入力データソース

- セッション情報（current_user）
- リクエストパラメータ

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| todos | ActiveRecord::Relation<Todo> | フィルタ適用済みTodo一覧 |
| count | Hash | 状態別のTodo件数 |

### 出力先

- HTML画面（Todoリストビュー）
- JSON API（Vue.jsフロントエンド向け）

## 処理フロー

### 処理シーケンス

```
1. 認証チェック
   └─ 未認証の場合はログイン画面へリダイレクト
2. 内部イベント追跡
   └─ track_internal_event('view_todo_list')
3. フィルタパラメータ解析
   └─ TodosFinder初期化
4. Todo一覧取得
   └─ フィルタ・ソート適用
   └─ 非表示ユーザー除外（without_hidden）
5. レスポンス生成
   └─ HTML/JSON形式で返却
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{認証済み?}
    B -->|No| C[ログイン画面へリダイレクト]
    B -->|Yes| D[内部イベント追跡]
    D --> E[TodosFinder初期化]
    E --> F{stateパラメータ}
    F -->|pending| G[pending状態 + not_snoozed]
    F -->|done| H[done状態のみ]
    F -->|all| I[全状態]
    G --> J[追加フィルタ適用]
    H --> J
    I --> J
    J --> K[ソート適用]
    K --> L[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-124-01 | デフォルト状態 | state未指定時はpendingのみ表示 | stateパラメータなし |
| BR-124-02 | スヌーズ除外 | pending表示時はsnoozed_until > 現在時刻のTodoを除外 | pending状態表示時 |
| BR-124-03 | 非表示ユーザー除外 | BAN済みユーザーが作成したTodoを非表示 | pending状態表示時 |
| BR-124-04 | ソートカスタマイズ | pending時はsnoozed_untilとcreated_atの複合ソート | pending + created_desc/asc |
| BR-124-05 | 対象種別制限 | TODO_TYPESに定義された種別のみ許可 | typeパラメータ指定時 |

### 計算ロジック

**スヌーズ・作成日複合ソート**:
```
COALESCE(snoozed_until, created_at) でソート
→ スヌーズ解除時刻がある場合はその時刻、なければ作成日時を使用
```

**ラベル優先度ソート**:
```
highest_label_priority = 関連Issue/MRに付与されたラベルの最小priority値
priority ASC でソート → 優先度が高い（数値が小さい）Todoが先頭
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 一覧取得 | todos | SELECT | ユーザーのTodo取得 |
| 一覧取得 | banned_users | LEFT JOIN | BAN済みユーザー除外 |
| 完了処理 | todos | UPDATE | state = 'done'に更新 |

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

#### todos

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | user_id, state, action, target_type, target_id, snoozed_until | user_id = current_user.id | 各種フィルタ適用 |
| UPDATE | state, resolved_by_action, updated_at | state = 'done', resolved_by_action = 指定値 | 完了処理時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | 認証エラー | 未ログイン状態でアクセス | ログイン画面へリダイレクト |
| 400 | 不正なtype | TODO_TYPESに含まれない種別指定 | ArgumentError発生 |

### リトライ仕様

データベースタイムアウト時は標準的なRailsリトライ機構に従う。

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

- 一覧取得: 読み取り専用、トランザクション不要
- 完了処理: batch_updateで一括更新、暗黙的トランザクション

## パフォーマンス要件

- urgency: low
- keyset paginationによる効率的ページネーション
- with_entity_associationsによるN+1クエリ防止

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

- ユーザー自身のTodoのみアクセス可能
- BAN済みユーザーのTodoは非表示
- 対象エンティティへの閲覧権限チェック（cross_project_access）

## 備考

- Todoのアクション種別は定数で定義（ASSIGNED=1, MENTIONED=2等）
- EE版では追加のアクション種別が存在（APPROVAL_REQUIRED, MERGE_TRAIN_REMOVED等）
- スヌーズ機能は一時的にTodoを非表示にする機能

---

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

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

### 推奨読解順序

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

Todoモデルの構造とアクション種別を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Todo | `app/models/todo.rb` | アクション種別定数（ASSIGNED等）とstate_machine |

**読解のコツ**:
- **14-31行目**: アクション種別の定数定義（ASSIGNED=1, MENTIONED=2等）
- **118-125行目**: state_machineによるpending/done状態遷移
- **64-70行目**: polymorphic associationによるtarget参照

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

コントローラーの処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Dashboard::TodosController | `app/controllers/dashboard/todos_controller.rb` | indexアクションと内部イベント追跡 |

**主要処理フロー**:
1. **9-14行目**: indexアクション - 内部イベント追跡のみでビュー表示

#### Step 3: Todo検索処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | TodosFinder | `app/finders/todos_finder.rb` | Todoフィルタリングのメインロジック |

**主要処理フロー**:
- **47-66行目**: executeメソッド - フィルタチェーンの適用
- **163-178行目**: sortメソッド - スヌーズ対応ソート
- **234-246行目**: by_stateメソッド - pending/done状態フィルタ
- **262-267行目**: without_hiddenメソッド - BAN済みユーザー除外

#### Step 4: ソートロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Todo.sort_by_attribute | `app/models/todo.rb` | ソート実装（215-232行目） |

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

```
Dashboard::TodosController#index
    │
    └─ track_internal_event('view_todo_list')

(フロントエンドからAPI呼び出し)
TodosFinder.new(users: [current_user], params).execute
    │
    ├─ by_action_id
    ├─ by_action
    ├─ by_author
    ├─ by_state
    │      ├─ pending (デフォルト)
    │      ├─ done
    │      └─ all
    ├─ by_snoozed_status
    │      ├─ snoozed (is_snoozed=true)
    │      └─ not_snoozed (pending時)
    ├─ by_target_id
    ├─ by_types
    ├─ by_group
    ├─ by_project
    └─ sort
           ├─ sort_by_snoozed_and_creation_dates (pending)
           ├─ order_by_labels_priority
           └─ order_by (標準ソート)
```

### データフロー図

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

current_user ───────────▶ Dashboard::TodosController ───────▶ HTML View
                               │
params ────────────────────────│
                               │
                         (Internal Event)
                               │
                         TodosFinder
                               │
                         ┌─────┴─────┐
                         │           │
                    by_state    by_action
                         │           │
                         └─────┬─────┘
                               │
                    without_hidden (BAN除外)
                               │
                            sort
                               │
                               ▼
                         @todos (Array<Todo>)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Dashboard::TodosController | `app/controllers/dashboard/todos_controller.rb` | コントローラー | エントリーポイント |
| TodosFinder | `app/finders/todos_finder.rb` | ファインダー | Todoフィルタリング |
| Todo | `app/models/todo.rb` | モデル | Todoデータ構造・状態遷移 |
| TodoService | `app/services/todo_service.rb` | サービス | Todo作成・更新処理 |
| TodoEntity | `app/serializers/todo_entity.rb` | シリアライザー | API用JSON変換 |
| API::Todos | `lib/api/todos.rb` | API | REST API エンドポイント |
