# バッチ設計書 16-Notification::ReopenSnoozedNotificationsJob

## 概要

本ドキュメントは、Chatwootシステムにおけるスヌーズ期限切れ通知の再オープン処理を実行するバッチ処理の設計を記述するものです。

### 本バッチの処理概要

本バッチは、スヌーズ期限（snoozed_until）が過ぎた通知を自動的に再オープンし、通知の活動時刻を更新する処理を実行します。

**業務上の目的・背景**：エージェントは重要でない通知や、後で確認したい通知を一時的にスヌーズすることができます。本バッチにより、設定したスヌーズ期限が過ぎた通知が自動的に再表示されるようになり、重要な通知の見落としを防止します。また、スヌーズ解除時に活動時刻を更新することで、通知一覧の上位に表示されるようになります。

**バッチの実行タイミング**：TriggerScheduledItemsJobから5分毎にトリガーされます。

**主要な処理内容**：
1. snoozed_untilが過去3日〜現在時刻の範囲にある通知を検索
2. 各通知のmetaにlast_snoozed_at情報を追記
3. snoozed_untilをnilに設定（スヌーズ解除）
4. updated_atとlast_activity_atを現在時刻に更新
5. read_atをnilに設定（未読に戻す）

**前後の処理との関連**：本バッチはTriggerScheduledItemsJobから呼び出される子ジョブとして機能します。会話のスヌーズ解除処理（Conversations::ReopenSnoozedConversationsJob）と類似の役割を担います。

**影響範囲**：notificationsテーブルの該当レコードが更新されます。通知一覧の表示順序やユーザーの通知バッジに影響を与えます。

## バッチ種別

ステータス更新 / 通知管理

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 5分毎 |
| 実行時刻 | 毎時0分、5分、10分、15分、... |
| 実行曜日 | 毎日 |
| 実行日 | 毎日 |
| トリガー | TriggerScheduledItemsJob経由 |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| TriggerScheduledItemsJobが稼働していること | 親ジョブからのトリガーが必要 |
| データベース接続が可能であること | notificationsテーブルへのアクセスに必要 |

### 実行可否判定

本バッチは無条件で実行されます。対象データが存在しない場合も正常終了します。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| なし | - | - | - | パラメータは不要 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| notifications | DB | スヌーズ状態の通知レコード |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| notifications | DB | snoozed_until, updated_at, last_activity_at, meta, read_atが更新される |

### 出力ファイル仕様

ファイル出力はありません。

## 処理フロー

### 処理シーケンス

```
1. バッチ開始
   └─ lowキューから起動
2. スヌーズ期限範囲の通知を検索
   └─ Notification.where(snoozed_until: 3.days.ago..Time.current)
3. バッチ単位で処理
   └─ find_in_batches(batch_size: 100)
4. 各通知の更新処理（update_notification）
   └─ 4-1. metaにlast_snoozed_atを追記
   └─ 4-2. snoozed_untilをnilに設定
   └─ 4-3. updated_atを現在時刻に設定
   └─ 4-4. last_activity_atを現在時刻に設定
   └─ 4-5. read_atをnilに設定
5. バッチ終了
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[snoozed_until範囲で通知を検索]
    B --> C[find_in_batches batch_size:100]
    C --> D{バッチあり?}
    D -->|No| G[バッチ終了]
    D -->|Yes| E[各通知をループ]
    E --> F[update_notification呼び出し]
    F --> H[metaにlast_snoozed_at追記]
    H --> I[snoozed_until=nil]
    I --> J[updated_at=現在時刻]
    J --> K[last_activity_at=現在時刻]
    K --> L[read_at=nil]
    L --> E
    E --> C
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| スヌーズ通知検索 | notifications | SELECT | snoozed_until範囲で検索 |
| 通知更新 | notifications | UPDATE | 複数カラムを一括更新 |

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

#### notifications

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, snoozed_until, meta | snoozed_until BETWEEN 3.days.ago AND Time.current | find_in_batchesで取得 |
| UPDATE | snoozed_until | nil | スヌーズ解除 |
| UPDATE | updated_at | Time.current | 更新時刻 |
| UPDATE | last_activity_at | Time.current | 活動時刻 |
| UPDATE | meta | 既存meta + {last_snoozed_at: snoozed_until} | スヌーズ履歴保持 |
| UPDATE | read_at | nil | 未読に戻す |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | StandardError | DB操作失敗時 | Sidekiqの標準リトライ機構により再実行 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Sidekiqのデフォルト設定 |
| リトライ間隔 | 指数バックオフ |
| リトライ対象エラー | StandardError全般 |

### 障害時対応

バッチ失敗時は次回の定期実行（5分後）で自動的にリカバリされます。緊急時はRailsコンソールから手動実行可能です：
```ruby
Notification::ReopenSnoozedNotificationsJob.perform_now
```

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | 各通知のupdate!操作は個別トランザクション |
| コミットタイミング | 各update!完了時 |
| ロールバック条件 | 個別通知の更新失敗時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 通常は少数〜数十件 |
| 目標処理時間 | 数秒〜1分 |
| メモリ使用量上限 | find_in_batches(batch_size: 100)により一定に制御 |

## 排他制御

- 同時実行制御なし
- 各通知のupdate!は個別トランザクションで実行
- 5分間隔の実行であるため、重複実行のリスクは低い

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | バッチ開始時 | Sidekiq標準ログ |
| 進捗ログ | - | 明示的なログ出力なし |
| 終了ログ | バッチ終了時 | Sidekiq標準ログ |
| エラーログ | エラー発生時 | Sidekiq標準エラーログ |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 処理時間 | 5分以上 | Sidekiq監視システム |
| エラー件数 | 1件以上 | Sidekiq監視システム |

## 備考

- snoozed_until範囲：3.days.ago..Time.current（過去3日〜現在）
- metaにlast_snoozed_atを保存することで、スヌーズ履歴を追跡可能
- read_atをnilに設定することで、通知が未読状態に戻り、ユーザーの注意を引く
- last_activity_atを更新することで、通知一覧の上位に表示される
- update!を使用しているため、バリデーション失敗時は例外がスローされる
