# 機能設計書 100-スヌーズ

## 概要

本ドキュメントは、Chatwoot における通知スヌーズ機能の設計を記載する。

### 本機能の処理概要

スヌーズ機能は、通知を指定した日時まで一時的に非表示にする機能である。スヌーズ中の通知は一覧から除外され、指定日時を過ぎると再び表示される。

**業務上の目的・背景**：すべての通知が即座に対応を必要とするわけではない。後で対応したい通知をスヌーズすることで、今すぐ対応が必要な通知に集中できる。また、特定の日時に再度リマインドを受けることで、対応漏れを防ぐことができる。

**機能の利用シーン**：
- 今は対応できないが、明日の朝に確認したい通知をスヌーズする
- 会議後に対応したい通知を数時間後までスヌーズする
- 週明けに対応したい通知を週末までスヌーズする

**主要な処理内容**：
1. スヌーズ終了日時の設定
2. 通知のsnoozed_until更新
3. メタ情報のlast_snoozed_atリセット

**関連システム・外部連携**：特になし（内部データ操作のみ）

**権限による制御**：自分の通知のみスヌーズ可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 26 | 通知一覧画面 | 主画面 | 通知のスヌーズ設定 |

## 機能種別

データ更新 / 日時処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | Integer | Yes | 通知ID | 必須 |
| snoozed_until | String | Yes | スヌーズ終了日時 | 日時文字列としてパース可能 |

### 入力データソース

画面入力（API経由）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | 通知ID |
| notification_type | String | 通知種別 |
| primary_actor_type | String | 主体タイプ |
| primary_actor_id | Integer | 主体ID |
| read_at | DateTime | 既読日時 |
| snoozed_until | DateTime | スヌーズ終了日時 |
| meta | Object | メタ情報 |
| ... | ... | その他の通知属性 |

### 出力先

API レスポンス（JSON形式）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ NotificationsController#snoozeがリクエストを受信
2. 通知取得
   └─ current_user.notifications.find(params[:id])
3. メタ情報更新
   └─ last_snoozed_at を nil にリセット
4. スヌーズ日時更新
   └─ snoozed_until を指定日時に更新
5. レスポンス返却
   └─ render json: @notification
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{認証確認}
    B -->|未認証| C[401エラー]
    B -->|認証済| D[通知取得]
    D --> E{通知存在?}
    E -->|なし| F[404エラー]
    E -->|あり| G{snoozed_until指定?}
    G -->|なし| H[更新なし]
    G -->|あり| I[日時パース]
    I --> J[メタ情報更新]
    J --> K[snoozed_until更新]
    K --> L[通知保存]
    L --> M[JSON返却]
    H --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-100-01 | ユーザー制限 | 自分の通知のみスヌーズ可能 | 全操作 |
| BR-100-02 | 日時パース | snoozed_untilは文字列から日時にパース | 更新時 |
| BR-100-03 | メタリセット | last_snoozed_atをnilにリセット | 更新時 |
| BR-100-04 | 一覧除外 | スヌーズ中の通知はデフォルトで一覧から除外 | 一覧取得時 |

### 計算ロジック

日時パース：
```ruby
parse_date_time(params[:snoozed_until].to_s)
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| snooze | notifications | UPDATE | snoozed_until, meta更新 |

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

#### notifications

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | snoozed_until | parse_date_time(params[:snoozed_until]) | - |
| UPDATE | meta | 既存meta + { 'last_snoozed_at' => nil } | マージ更新 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 401 | Unauthorized | 未認証 | ログインが必要 |
| 404 | Not Found | 通知が存在しない | 正しいIDを指定 |

### リトライ仕様

特になし。

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

単一レコード更新のため、Rails標準のトランザクション管理で処理。

## パフォーマンス要件

- 単一レコード更新のため高速に処理可能

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

- current_user.notifications.find で自分の通知のみアクセス

## 備考

DateRangeHelper の parse_date_time を使用して日時文字列をパースしている。スヌーズ解除は snoozed_until に nil を設定するか、指定日時を過ぎることで自動的に解除される。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notifications_controller.rb | `app/controllers/api/v1/accounts/notifications_controller.rb` | snoozeアクションの実装 |

**読解のコツ**:
- **3行目**: include DateRangeHelper - 日時パースヘルパーを含む
- **5行目**: before_action :fetch_notification で通知取得
- **56-60行目**: snooze アクション

**主要処理フロー**:
1. **57行目**: 既存metaとlast_snoozed_at: nil をマージ
2. **58行目**: snoozed_untilが指定されている場合のみ更新
3. **58行目**: parse_date_time で日時文字列をパース
4. **59行目**: render json: @notification でレスポンス返却

#### Step 2: 日時パースを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | date_range_helper.rb | `app/helpers/date_range_helper.rb` | parse_date_timeメソッド |

**読解のコツ**:
- parse_date_time メソッドは文字列を DateTime にパースする

#### Step 3: フィルタロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | notification_finder.rb | `app/finders/notification_finder.rb` | スヌーズフィルタの実装 |

**読解のコツ**:
- **43-44行目**: filter_snoozed_notifications - snoozed_until: nil でフィルタ

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

```
Api::V1::Accounts::NotificationsController
    │
    ├─ include DateRangeHelper
    │      └─ parse_date_time メソッド
    │
    ├─ before_action :fetch_notification
    │      └─ current_user.notifications.find(params[:id])
    │
    └─ snooze
           │
           ├─ updated_meta = (@notification.meta || {}).merge('last_snoozed_at' => nil)
           │
           └─ @notification.update(
                  snoozed_until: parse_date_time(params[:snoozed_until]),
                  meta: updated_meta
              )
                  │
                  └─ render json: @notification
```

### データフロー図

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

id ──────────────────▶ fetch_notification
                              │
                              ▼
                       @notification取得
                              │
snoozed_until ───────▶ parse_date_time
                              │
                              ▼
                       DateTime変換
                              │
                              ▼
                       メタ情報マージ
                       {
                         ...existing_meta,
                         'last_snoozed_at': nil
                       }
                              │
                              ▼
                       @notification.update
                              │
                              ▼
                       render json: @notification
                              │
                              ▼
                       {
                         id: ...,
                         snoozed_until: "2024-12-20T10:00:00Z",
                         meta: { last_snoozed_at: null },
                         ...
                       }
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| notifications_controller.rb | `app/controllers/api/v1/accounts/notifications_controller.rb` | コントローラー | snoozeアクション |
| notification.rb | `app/models/notification.rb` | モデル | snoozed_until, meta定義 |
| notification_finder.rb | `app/finders/notification_finder.rb` | Finder | スヌーズフィルタ |
| date_range_helper.rb | `app/helpers/date_range_helper.rb` | ヘルパー | 日時パース |
| routes.rb | `config/routes.rb` | 設定 | POST snooze ルーティング |
