# バッチ設計書 2-Card::ActivitySpike::DetectionJob

## 概要

本ドキュメントは、カードのアクティビティスパイク（活動の急増）を検出するバックグラウンドジョブ `Card::ActivitySpike::DetectionJob` の設計仕様を記載したものである。

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

このバッチは、カードに対する活動パターンを分析し、通常よりも活発な活動（アクティビティスパイク）が発生しているかどうかを検出する処理を行う。検出されたスパイクは、カードのステール（停滞）判定に使用される重要な指標となる。

**業務上の目的・背景**：fizzyでは、長期間活動のないカードを自動的にクローズするエントロピー機能がある。しかし、活発に議論されているカードは、たとえ古くても重要な場合がある。アクティビティスパイクを検出することで、活発なカードを誤ってクローズすることを防ぎ、ユーザーの重要な議論を保護する。スパイク検出から14日以上経過し、かつカード更新から14日以上経過したカードのみが「ステール」と判定される。

**バッチの実行タイミング**：カードの`last_active_at`が更新され、かつカードが公開状態（published）の場合に、`after_update_commit`コールバックにより非同期で実行される。

**主要な処理内容**：
1. 対象カードを受け取り、スパイク検出処理を開始
2. カードがエントロピー設定を持つか確認
3. 複数人による最近のコメント活動を分析
4. カードの直近のアサインまたはリオープンイベントを確認
5. スパイク条件を満たす場合、ActivitySpikeレコードを作成または更新

**前後の処理との関連**：カードの更新（コメント追加、状態変更など）により本ジョブがトリガーされる。検出されたスパイクは、エントロピーによる自動クローズ判定時に参照され、ステール状態の判定に使用される。

**影響範囲**：`card_activity_spikes`テーブル、カードのステール判定ロジック、エントロピー自動クローズ機能

## バッチ種別

データ分析 / 状態検出

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 随時（カード更新時） |
| 実行時刻 | N/A |
| 実行曜日 | N/A |
| 実行日 | N/A |
| トリガー | カードのlast_active_at更新時のコールバック |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| カードの存在 | 有効なCardレコードが存在すること |
| 公開状態 | カードがpublished状態であること |
| last_active_at変更 | カードのlast_active_atが変更されたこと |

### 実行可否判定

`ActiveJob::DeserializationError`が発生した場合（Cardレコードが削除済みの場合など）はジョブを破棄する。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| card | Card | Yes | - | スパイク検出対象のCardオブジェクト |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| cards | DB | 対象カード情報 |
| comments | DB | カードに対するコメント履歴 |
| events | DB | カードに対するイベント履歴 |
| card_activity_spikes | DB | 既存のスパイク記録 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| card_activity_spikes | DB | スパイク検出結果の記録（作成または更新） |

### 出力ファイル仕様

ファイル出力なし

## 処理フロー

### 処理シーケンス

```
1. ジョブ開始
   └─ card.detect_activity_spikesメソッドを呼び出し
2. Detectorインスタンス生成
   └─ Card::ActivitySpike::Detector.new(card)
3. スパイク条件チェック
   └─ has_activity_spike?メソッド
4. エントロピー設定確認
   └─ card.entropic?
5. 複数人コメント確認
   └─ 直近期間内に3件以上のコメント、2人以上の参加者
6. アサイン/リオープン確認
   └─ 直近1分以内のcard_assigned/card_reopenedイベント
7. スパイク記録
   └─ Card::ActivitySpike.find_or_create_by!でupsert
8. ジョブ終了
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B{エントロピー設定あり?}
    B -->|No| C[スキップ]
    B -->|Yes| D{スパイク条件チェック}
    D --> E{複数人コメント?}
    D --> F{直近アサイン?}
    D --> G{直近リオープン?}
    E -->|Yes| H[スパイク検出]
    F -->|Yes| H
    G -->|Yes| H
    E -->|No| I{他条件確認}
    F -->|No| I
    G -->|No| I
    I -->|全てNo| C
    H --> J[ActivitySpike upsert]
    J --> K[touch更新]
    C --> L[バッチ終了]
    K --> L
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| スパイク記録 | card_activity_spikes | INSERT/UPDATE | スパイク検出時に作成または更新 |
| コメント分析 | comments | SELECT | 直近コメントの集計 |
| イベント確認 | events | SELECT | 直近イベントの確認 |
| カード参照 | cards | SELECT | エントロピー設定の確認 |

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

#### card_activity_spikes

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | card_id | 対象カードのID | ユニーク制約あり |
| UPDATE | updated_at | 現在時刻 | touchで更新 |
| INSERT | account_id | カードのaccount_id | デフォルト値設定 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| DeserializationError | シリアライズエラー | カードが削除済み | ジョブを破棄 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0回（リトライなし） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

### 障害時対応

スパイク検出の失敗は、カードのステール判定に影響を与える可能性があるが、次回のカード更新時に再度検出が試行される。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | レコード単位 |
| コミットタイミング | find_or_create_by!実行時 |
| ロールバック条件 | 例外発生時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 1件/ジョブ実行 |
| 目標処理時間 | 1秒以内 |
| メモリ使用量上限 | 最小限 |

## 排他制御

同時実行の制限なし。`find_or_create_by!`によりレコードの重複は防止される。card_idにユニークインデックスが設定されている。

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | ジョブ開始時 | ActiveJob標準ログ |
| 終了ログ | ジョブ終了時 | ActiveJob標準ログ |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| ジョブ失敗率 | 異常な増加 | ジョブ監視システム |

## 備考

- スパイク検出条件：
  1. カードがエントロピー設定を持つ（entropic?）
  2. AND（以下のいずれか）
     - 直近期間内に3件以上のコメント、かつ2人以上の参加者
     - 直近1分以内にcard_assignedイベント
     - 直近1分以内にcard_reopenedイベント
- 直近期間 = エントロピーのauto_clean_period * 0.33
- STALLED_AFTER_LAST_SPIKE_PERIOD = 14日
- Turboブロードキャストは抑制される（Card.suppressing_turbo_broadcasts）
