# バッチ設計書 9-milestones-check

## 概要

本ドキュメントは、Ghostプラットフォームにおけるマイルストーン達成チェックバッチ「milestones-check」の設計仕様を記載したものです。このバッチは、メンバー数やARR（年間経常収益）のマイルストーン達成をチェックし、達成時に通知を行う役割を担います。

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

**業務上の目的・背景**：Ghostはニュースレタープラットフォームとして、サイト運営者の成長を支援しています。メンバー数やARRが特定のマイルストーン（100人、1000人、$1000 ARR等）に達した際に、運営者に達成通知を送ることで、モチベーション向上とコミュニティの成長を促進します。このバッチは、定期的にメトリクスをチェックし、新たなマイルストーン達成を検出・通知します。

**バッチの実行タイミング**：起動時にランダムな遅延（0〜4日）の後に1回実行されます。開発環境では5秒後に実行されます。

**主要な処理内容**：
1. メンバー数を取得し、設定されたマイルストーン値と比較
2. ARR（Stripe Live有効時のみ）を取得し、設定されたマイルストーン値と比較
3. 新たに達成したマイルストーンがあれば、milestonesテーブルに記録
4. 条件を満たす場合は達成通知メールを送信

**前後の処理との関連**：メンバー登録・Stripe決済処理の結果として数値が更新されます。達成時はStaffサービス経由でメール通知が送信されます。

**影響範囲**：milestonesテーブルが更新されます。管理者への通知メールに影響します。

## バッチ種別

集計処理 / 通知配信

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 起動時に1回 |
| 実行時刻 | 起動後0〜4日のランダム遅延（開発環境は5秒） |
| 実行曜日 | - |
| 実行日 | - |
| トリガー | setTimeout（起動時） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| サービス初期化 | MilestonesServiceが初期化されていること |
| Stripe Live設定 | ARRチェックにはStripe Liveキーが設定されていること |

### 実行可否判定

- 常に実行（タイムアウト後）
- ARRチェックはStripe Live有効時のみ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| customTimeout | number | No | JOB_TIMEOUT | カスタムタイムアウト値（ms） |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| members テーブル | DB | メンバー数の取得 |
| Stripe データ | API | ARR（年間経常収益）の取得 |
| milestones テーブル | DB | 既存マイルストーンの確認 |
| milestonesConfig | 設定 | マイルストーン定義 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| milestones テーブル | DB | 達成マイルストーンの記録 |
| メール | SMTP | 達成通知メール（条件付き） |

### 出力ファイル仕様

ファイル出力なし

## 処理フロー

### 処理シーケンス

```
1. init()
   └─ MilestonesService、Repository、Queries を初期化

2. scheduleRun()
   └─ タイムアウト計算（0-4日ランダム、開発環境は5秒）
   └─ setTimeout でrun()をスケジュール

3. run()
   └─ checkMilestones('members') でメンバーマイルストーンをチェック
   └─ Stripe Live有効なら checkMilestones('arr') でARRマイルストーンをチェック

4. checkMilestones('members')
   └─ getMembersCount()でメンバー数取得
   └─ getMatchedMilestones()で達成マイルストーンを抽出
   └─ 各マイルストーンについて:
      └─ checkMilestoneExists()で既存チェック
      └─ 初回実行: メールなしで保存
      └─ スキップ対象: メールなしで保存
      └─ 新規達成: shouldSendEmail()でメール送信判定

5. checkMilestones('arr')
   └─ getARR()でARR取得（通貨別）
   └─ デフォルト通貨のマイルストーンをチェック
   └─ 同様の処理を実行

6. 結果返却
   └─ {members, arr}オブジェクトを返す
```

### フローチャート

```mermaid
flowchart TD
    A[init] --> B[scheduleRun]
    B --> C[タイムアウト待機]
    C --> D[run開始]
    D --> E[メンバーマイルストーンチェック]
    E --> F{Stripe Live有効?}
    F -->|Yes| G[ARRマイルストーンチェック]
    F -->|No| H[結果返却]
    G --> H

    subgraph checkMilestones
    I[現在値取得] --> J[達成マイルストーン抽出]
    J --> K{初回実行?}
    K -->|Yes| L[メールなしで保存]
    K -->|No| M{新規達成?}
    M -->|Yes| N[メール送信判定]
    M -->|No| O[スキップ]
    N --> P[保存]
    end
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| メンバー数取得 | members | SELECT | COUNT |
| 既存マイルストーン取得 | milestones | SELECT | type, value, currency条件 |
| マイルストーン保存 | milestones | INSERT | 新規達成マイルストーン |
| 最新マイルストーン取得 | milestones | SELECT | type別の最新レコード |
| 最終メール送信確認 | milestones | SELECT | emailSentAtが設定されているレコード |

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

#### milestones

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | type = 'members' または 'arr' | |
| SELECT | * | value = マイルストーン値, currency = 通貨 | getByARR/getByCount |
| INSERT | id, type, value, currency, emailSentAt, meta | 新規マイルストーン | |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 初期化エラー | サービス初期化失敗 | エラーログ出力 |
| - | クエリエラー | DB接続・クエリ失敗 | エラーログ出力 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | - |
| リトライ対象エラー | - |

### 障害時対応

- エラー発生時はログ出力
- 次回起動時に再度チェック

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | マイルストーン保存単位 |
| コミットタイミング | 各マイルストーン保存時 |
| ロールバック条件 | 保存失敗時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | マイルストーン定義数に依存 |
| 目標処理時間 | 数秒 |
| メモリ使用量上限 | 特になし |

## 排他制御

- setTimeoutによる遅延実行
- 同時実行制御は特になし

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | スケジュール時 | `Running milestone emails job on ${jobDate}` |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| なし | - | - |

## 備考

- JOB_TIMEOUT: 0〜4日のランダム値（1000 * 60 * 60 * 24 * Math.random() * 4）
- 開発環境（NODE_ENV === 'development'）では5秒後に実行
- Stripe Live判定: pk_live_（本番）またはpk_test_（開発環境のみ）
- メール送信しない条件:
  - 初回実行（reason: 'initial'）
  - 既に高いマイルストーンを達成済み（reason: 'skipped'）
  - 前回メールから minDaysSinceLastEmail 日以内
  - 過去1週間以内にメンバーインポートがあった場合（reason: 'import'または'email'）
- ARRマイルストーンはデフォルト通貨（getDefaultCurrency）のみチェック
- マイルストーン値は設定ファイル（milestonesConfig）で定義
