# 通知設計書 7-ニュースレターメール

## 概要

本ドキュメントは、Ghost CMSにおけるニュースレターメール（Newsletter Email）の設計仕様を記載する。投稿が公開された際に購読者へ配信されるニュースレターメールの送信ロジック、バッチ処理、およびトラッキング機能を定義する。

### 本通知の処理概要

本通知は、投稿（Post）が公開された際に、該当ニュースレターの購読者全員に対してメールを配信する大規模メール送信機能である。

**業務上の目的・背景**：コンテンツクリエイターが作成した投稿を、購読者に直接届けることでエンゲージメントを高める。Ghost CMSの中核機能であり、有料会員へのコンテンツ配信、無料購読者への情報発信など、多様なビジネスモデルを支える。

**通知の送信タイミング**：投稿のステータスが'published'に変更された時点、かつニュースレター配信が有効化されている場合にトリガーされる。スケジュール投稿の場合は、指定日時に自動的に配信される。

**通知の受信者**：該当ニュースレターを購読している会員（購読フィルターに合致する会員）に送信される。無料会員のみ、有料会員のみ、または全会員などのセグメント設定が可能。

**通知内容の概要**：投稿の内容（タイトル、本文、アイキャッチ画像等）がメールテンプレートに展開されて送信される。開封トラッキング、クリックトラッキング、購読解除リンクなどが自動的に挿入される。

**期待されるアクション**：受信者はメールを開封し、本文を読む。必要に応じてサイトへのリンクをクリックしてフルコンテンツを閲覧する。

## 通知種別

メール通知（バルクメール / ニュースレター）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（バッチ処理） |
| 優先度 | 中 |
| リトライ | 有（バッチ単位で最大6回、10秒間隔） |

### 送信先決定ロジック

1. 投稿に紐づくニュースレターを取得
2. email_recipient_filterに基づいて対象会員をフィルタリング
3. ニュースレターを購読中（subscribed=true）の会員を抽出
4. バッチサイズ（最大1000件/バッチ）に分割して送信

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | ニュースレター設定のsender_emailまたはサイト設定 |
| 送信元名称 | ニュースレター設定のsender_name |
| 件名 | 投稿タイトル（カスタム設定可能） |
| 形式 | HTML + テキスト（マルチパート） |

### 本文テンプレート

```
件名: {post.title}

本文:
{ニュースレターテンプレートに基づくレンダリング}

- ヘッダー（サイトロゴ、サイト名）
- 投稿タイトル
- 投稿者情報
- アイキャッチ画像
- 投稿本文（HTML）
- フッター（購読解除リンク、サイト情報）
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 本通知には添付ファイルはない（画像はインライン/外部参照） |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| post.title | 投稿タイトル | posts.title | Yes |
| post.html | 投稿本文HTML | posts.html（レンダリング後） | Yes |
| post.feature_image | アイキャッチ画像URL | posts.feature_image | No |
| post.url | 投稿URL | urlService.getUrlByResourceId() | Yes |
| member.name | 会員名 | members.name | No |
| member.email | 会員メールアドレス | members.email | Yes |
| member.uuid | 会員UUID | members.uuid | Yes |
| unsubscribe_url | 購読解除URL | 動的生成 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 投稿公開 | post.published | newsletter_id が設定されている | 即時公開 |
| スケジュール公開 | scheduled job | published_at到達時 | 予約投稿 |
| 手動リトライ | Admin UI | status='failed'のEmailをリトライ | 送信失敗時の再送 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| ニュースレター未設定 | 投稿にnewsletter_idが設定されていない |
| アーカイブ済みニュースレター | ニュースレターのstatusが'archived' |
| 送信上限到達 | ホスト制限（emails limit）に到達 |
| 検証必要 | email_verification_required=trueでアカウント検証中 |
| 購読者ゼロ | 対象となる購読者が0人 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[投稿公開] --> B[EmailService.createEmail]
    B --> C{ニュースレター有効?}
    C -->|No| D[エラー: アーカイブ済み]
    C -->|Yes| E[購読者数カウント]
    E --> F{制限チェック}
    F -->|超過| G[エラー: 制限到達]
    F -->|OK| H[Emailレコード作成]
    H --> I[BatchSendingService.scheduleEmail]
    I --> J[バッチ作成]
    J --> K[バッチごとに並列送信]
    K --> L{全バッチ成功?}
    L -->|Yes| M[status='submitted']
    L -->|No| N[status='failed']
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| posts | 投稿情報取得 | 本文、タイトル等 |
| newsletters | ニュースレター設定取得 | 送信元、テンプレート設定 |
| members | 購読者取得 | フィルター条件に合致する会員 |
| members_newsletters | 購読状態確認 | subscribed=true |
| settings | サイト設定取得 | トラッキング設定等 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| emails | INSERT | メール送信ジョブレコード作成 |
| email_batches | INSERT | バッチレコード作成 |
| email_recipients | INSERT | 受信者レコード作成 |

#### emailsテーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | post_id | 投稿ID | |
| INSERT | newsletter_id | ニュースレターID | |
| INSERT | status | 'pending' → 'submitting' → 'submitted' / 'failed' | |
| INSERT | submitted_at | 送信完了日時 | |
| INSERT | email_count | 送信対象数 | |
| INSERT | track_opens | 開封トラッキングフラグ | |
| INSERT | track_clicks | クリックトラッキングフラグ | |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| EmailError | ニュースレターがアーカイブ済み | BadRequestErrorをthrow |
| EmailError | 制限到達 | HostLimitErrorをthrow |
| EmailError | バッチ送信部分失敗 | 失敗バッチをfailed、全体もfailed |
| EmailError | 全バッチ失敗 | email.status='failed'、エラーメッセージ保存 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | バッチ: 最大6回、DB操作: 最大10〜20回 |
| リトライ間隔 | バッチ: 10秒、DB操作: 2秒（指数バックオフ） |
| リトライ対象エラー | Mailgun APIエラー、DBエラー |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| バッチサイズ | 最大1000件/バッチ |
| 並列送信数 | 最大2バッチ同時 |
| ホスト制限 | プラン依存（emails limit） |

### 配信時間帯

特定の配信時間帯制限はない。ただし、ドメインウォームアップ機能により、新規ドメインでは段階的に配信数を増加させる。

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

- 購読解除リンクはUUID付きでパーソナライズ
- 開封/クリックトラッキングはサイト設定で有効/無効の切り替え可能
- メール本文に会員の個人情報（名前、メールアドレス等）が含まれる
- Mailgun APIキーは環境変数/設定ファイルで管理

## 備考

- バッチ送信はジョブキューを使用して非同期実行
- ドメインウォームアップ機能により、新規ドメインでは段階的に配信
- フィードバック機能（いいね/悪いね）がニュースレターごとに設定可能
- Lexical/Mobiledocの両形式に対応

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | email-service.js | `ghost/core/core/server/services/email-service/email-service.js` | EmailServiceクラスの依存関係、createEmail()のシグネチャ |
| 1-2 | Email model | `ghost/core/core/server/models/` | emailsテーブルの構造 |

**読解のコツ**: EmailServiceは多くの依存を持つ。batchSendingService, emailRenderer, emailSegmenterが主要コンポーネント。

#### Step 2: バッチ処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | batch-sending-service.js | `ghost/core/core/server/services/email-service/batch-sending-service.js` | scheduleEmail(), emailJob(), createBatches() |

**主要処理フロー**:
- **122-129行目**: scheduleEmail() - ジョブキューへの登録
- **135-192行目**: emailJob() - メイン送信ジョブ
- **239-342行目**: createBatches() - バッチ作成処理
- **430-480行目**: sendBatches() - バッチ送信処理

#### Step 3: レンダリング処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | email-renderer.js | `ghost/core/core/server/services/email-service/email-renderer.js` | renderBody()、変数置換 |
| 3-2 | sending-service.js | `ghost/core/core/server/services/email-service/sending-service.js` | Mailgun APIへの送信 |

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

```
投稿公開イベント
    │
    └─ EmailService.createEmail()
           │
           ├─ checkCanSendEmail() - 制限チェック
           │      │
           │      ├─ emailSegmenter.getMembersCount()
           │      │
           │      └─ checkLimits()
           │
           ├─ Email.add() - DBレコード作成
           │
           └─ BatchSendingService.scheduleEmail()
                  │
                  └─ jobsService.addJob()
                         │
                         └─ emailJob()
                                │
                                ├─ createBatches()
                                │      │
                                │      ├─ emailRenderer.getSegments()
                                │      │
                                │      ├─ Member.getFilteredCollectionQuery()
                                │      │
                                │      └─ EmailBatch.add() / EmailRecipient INSERT
                                │
                                └─ sendBatches()
                                       │
                                       └─ sendBatch() × N
                                              │
                                              ├─ getBatchMembers()
                                              │
                                              └─ sendingService.send()
                                                     │
                                                     ├─ emailRenderer.renderBody()
                                                     │
                                                     └─ Mailgun API呼び出し
```

### データフロー図

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

投稿公開 ───────────────▶ EmailService
  - post                       │
  - newsletter                 ▼
                         購読者カウント
                               │
membersテーブル ◀────── フィルタリング
  - email                      │
  - name                       ▼
  - uuid                 バッチ分割（1000件/バッチ）
                               │
postsテーブル ─────────▶ コンテンツレンダリング
  - title                      │
  - html                       ▼
  - feature_image        Mailgun API送信 ─────────▶ 購読者のメールボックス
                               │
emailsテーブル ◀──────── ステータス更新
email_batchesテーブル
email_recipientsテーブル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| email-service.js | `ghost/core/core/server/services/email-service/email-service.js` | サービス | メール送信のエントリーポイント |
| batch-sending-service.js | `ghost/core/core/server/services/email-service/batch-sending-service.js` | サービス | バッチ処理 |
| email-renderer.js | `ghost/core/core/server/services/email-service/email-renderer.js` | レンダラー | HTML/テキスト生成 |
| email-segmenter.js | `ghost/core/core/server/services/email-service/email-segmenter.js` | セグメンター | 購読者フィルタリング |
| sending-service.js | `ghost/core/core/server/services/email-service/sending-service.js` | サービス | Mailgun連携 |
| Email model | `ghost/core/core/server/models/email.js` | モデル | emailsテーブルアクセス |
