# 通知設計書 6-会員ウェルカムメール

## 概要

本ドキュメントは、Ghost CMSにおける会員ウェルカムメール（Member Welcome Email）の設計仕様を記載する。新規会員登録完了後に自動送信されるウェルカムメールの送信ロジック、テンプレート構造、およびデータフローを定義する。

### 本通知の処理概要

本通知は、新規会員（無料または有料）の登録が完了した際に、ウェルカムメッセージを送信する機能である。管理者がGhost Admin画面で設定したカスタムコンテンツを含むメールが送信される。

**業務上の目的・背景**：新規会員に対してパーソナライズされたウェルカムメッセージを送信することで、会員のエンゲージメントを高め、サイトへの帰属意識を醸成する。無料会員と有料会員で異なるメッセージを設定可能で、有料会員には特別な感謝のメッセージを送ることができる。

**通知の送信タイミング**：会員レコードが作成された直後、MemberCreatedEventがトリガーされた時点で送信される。無料会員登録完了時、または有料サブスクリプション完了時に自動で送信される。

**通知の受信者**：新規登録した会員のメールアドレスに送信される。会員ステータス（free/paid）に応じて異なるテンプレートが使用される。

**通知内容の概要**：管理者がGhost Admin画面で設定したカスタムコンテンツがLexical形式で保存されており、それがHTMLにレンダリングされて送信される。会員名やサイト名などの変数置換に対応。

**期待されるアクション**：受信者はウェルカムメッセージを読み、サイトへのアクセスや有料コンテンツの閲覧を開始する。

## 通知種別

メール通知（自動配信メール / Automated Email）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（イベント駆動） |
| 優先度 | 中 |
| リトライ | 無 |

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

1. MemberCreatedEventを受信
2. 会員ステータス（free/paid）を確認
3. 該当ステータスのウェルカムメールが有効（active）かどうかを確認
4. 有効な場合、該当会員のメールアドレスに送信

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | automated_emailsテーブルのsender_email、またはサイトのデフォルト送信元 |
| 送信元名称 | automated_emailsテーブルのsender_name、またはサイトタイトル |
| 件名 | automated_emailsテーブルのsubject（変数置換対応） |
| 形式 | HTML + テキスト（マルチパート） |

### 本文テンプレート

```
件名: {管理者設定による - 例: "Welcome to {site_title}!"}

本文:
{管理者がGhost Adminで設定したLexicalコンテンツ}

例:
Hey {first_name},

Welcome to our community! We're thrilled to have you here.

{サイトオーナーが設定した任意のコンテンツ}

Best regards,
The {site_title} Team
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 本通知には添付ファイルはない |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| first_name | 会員の名（ファーストネーム） | member.name から抽出 | No |
| name | 会員のフルネーム | member.name | No |
| email | 会員のメールアドレス | member.email | Yes |
| site_title | サイト名 | settingsCache.get('title') | Yes |
| site_url | サイトURL | urlUtils.urlFor('home', true) | Yes |

**注意**: 変数は `{変数名}` または `{変数名, "フォールバック値"}` の形式で使用可能。

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| イベント | MemberCreatedEvent | status='free' かつ free用ウェルカムメールが有効 | 無料会員登録完了 |
| イベント | MemberCreatedEvent | status='paid' かつ paid用ウェルカムメールが有効 | 有料会員登録完了 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| ウェルカムメール無効 | automated_emailsテーブルでstatus='inactive'の場合 |
| Lexicalコンテンツなし | automated_emailsテーブルのlexical列がnullの場合 |
| メールアドレスなし | 会員のメールアドレスが取得できない場合 |
| テスト用インボックス設定 | memberWelcomeEmailTestInbox設定がある場合は設定先に送信 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[MemberCreatedEvent発火] --> B[会員ステータス確認]
    B --> C{free or paid?}
    C -->|free| D[member-welcome-email-free slug検索]
    C -->|paid| E[member-welcome-email-paid slug検索]
    D --> F{ウェルカムメール有効?}
    E --> F
    F -->|No| G[送信スキップ]
    F -->|Yes| H[Lexicalコンテンツ取得]
    H --> I[変数置換処理]
    I --> J[HTMLレンダリング]
    J --> K[GhostMailer.send()]
    K --> L[完了]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| automated_emails | ウェルカムメール設定取得 | slug列で検索 |
| members | 会員情報取得 | id列で検索 |
| settings | サイト設定取得 | settingsCacheから取得 |

### テーブル別参照項目詳細

#### automated_emails

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| slug | メールタイプ識別 | 'member-welcome-email-free' または 'member-welcome-email-paid' |
| lexical | メール本文コンテンツ | - |
| subject | メール件名 | - |
| status | 有効/無効フラグ | 'active'の場合のみ送信 |
| sender_name | 送信者名 | - |
| sender_email | 送信元アドレス | - |
| sender_reply_to | 返信先アドレス | - |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| なし | - | 送信ログ等への書き込みは行われない |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| IncorrectUsageError | ウェルカムメール設定が見つからない | エラーをthrow |
| IncorrectUsageError | ウェルカムメールが非アクティブ | エラーをthrow |
| IncorrectUsageError | 受信者メールアドレスがない | エラーをthrow |
| Lexicalレンダリングエラー | Lexicalコンテンツが不正 | エラーログ出力 |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 設定なし |
| 1日あたり上限 | 設定なし |

### 配信時間帯

特定の配信時間帯制限はない。会員登録直後に即座に送信。

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

- 変数置換時にHTMLエスケープを実施（escapeHtml: true）
- 会員の個人情報（名前、メールアドレス）は適切に取り扱う
- テスト用インボックス設定により、本番環境以外でのテストが可能

## 備考

- ウェルカムメールはGhost Admin > Settings > Emails から設定可能
- 無料会員用（free）と有料会員用（paid）で別々のテンプレートを設定可能
- Lexical形式のリッチテキストエディタで本文を編集可能
- 件名と本文の両方で変数置換が可能
- `{first_name, "friend"}` のようにフォールバック値を指定可能

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | constants.js | `ghost/core/core/server/services/member-welcome-emails/constants.js` | MEMBER_WELCOME_EMAIL_SLUGS、MESSAGES定数 |
| 1-2 | AutomatedEmail model | `ghost/core/core/server/models/` | automated_emailsテーブルの構造 |

**読解のコツ**: slugは'member-welcome-email-free'と'member-welcome-email-paid'の2種類。statusが'active'でないと送信されない。

#### Step 2: サービス層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | service.js | `ghost/core/core/server/services/member-welcome-emails/service.js` | MemberWelcomeEmailService クラス全体 |

**主要処理フロー**:
- **32-50行目**: loadMemberWelcomeEmails() - DBからウェルカムメール設定を読み込み
- **52-96行目**: send() - メール送信処理
- **98-107行目**: isMemberWelcomeEmailActive() - 有効/無効チェック

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | member-welcome-email-renderer.js | `ghost/core/core/server/services/member-welcome-emails/member-welcome-email-renderer.js` | Lexical→HTML変換、変数置換 |

**主要処理フロー**:
- **36-49行目**: #buildReplacementDefinitions() - 変数定義
- **62-77行目**: #applyReplacements() - 変数置換処理
- **88-117行目**: render() - メインレンダリング処理

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

```
MemberCreatedEvent (Domain Event)
    │
    └─ Event Handler（イベント購読側）
           │
           ├─ MemberWelcomeEmailService.isMemberWelcomeEmailActive()
           │      └─ AutomatedEmail.findOne({slug})
           │
           └─ MemberWelcomeEmailService.send()
                  │
                  ├─ loadMemberWelcomeEmails() - 設定読み込み
                  │      └─ AutomatedEmail.findOne({slug})
                  │
                  ├─ MemberWelcomeEmailRenderer.render()
                  │      │
                  │      ├─ lexicalLib.render() - Lexical→HTML
                  │      │
                  │      ├─ #applyReplacements() - 変数置換
                  │      │
                  │      ├─ Handlebars.compile() - テンプレート展開
                  │      │
                  │      └─ juice() - CSSインライン化
                  │
                  └─ GhostMailer.send()
```

### データフロー図

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

MemberCreatedEvent ────▶ EventHandler
  - member                     │
  - memberStatus               ▼
                         ウェルカムメール有効確認
                               │
automated_emails ◀────── 設定読み込み
  - lexical                    │
  - subject                    ▼
  - status                Lexicalレンダリング
                               │
member情報 ────────────▶ 変数置換
  - name                       │
  - email                      ▼
                         CSSインライン化
settings ──────────────▶      │
  - title                      ▼
  - url                  メール送信 ─────────▶ 新規会員のメールボックス
  - accent_color
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| service.js | `ghost/core/core/server/services/member-welcome-emails/service.js` | サービス | メイン処理ロジック |
| member-welcome-email-renderer.js | `ghost/core/core/server/services/member-welcome-emails/member-welcome-email-renderer.js` | レンダラー | Lexical→HTML変換、変数置換 |
| constants.js | `ghost/core/core/server/services/member-welcome-emails/constants.js` | 定数 | slug名、エラーメッセージ |
| wrapper.hbs | `ghost/core/core/server/services/member-welcome-emails/email-templates/wrapper.hbs` | テンプレート | メールHTML外枠 |
| AutomatedEmail | `ghost/core/core/server/models/` | モデル | automated_emailsテーブルアクセス |
