# 機能設計書 53-メンバー分析

## 概要

本ドキュメントは、Ghostのメンバー分析機能に関する設計を記述します。この機能は、メンバーの増減、収益（MRR）、サブスクリプション状況などを分析し、メンバーシップビジネスの成長を可視化します。

### 本機能の処理概要

**業務上の目的・背景**：メンバーシップビジネスを運営するパブリッシャーにとって、会員数の推移、有料転換率、解約率、収益の成長は最も重要な経営指標です。本機能は、これらのKPIを可視化し、ビジネスの健全性をモニタリングするために必要です。

**機能の利用シーン**：日次・週次・月次でのメンバーシップビジネスの状況確認、マーケティングキャンペーン後のコンバージョン効果測定、解約傾向の分析、収益予測などの場面で活用されます。

**主要な処理内容**：
1. メンバー数履歴（無料/有料/コンプ別）の取得と推移グラフ生成
2. サブスクリプション履歴（Tier/Cadence別）の取得
3. MRR履歴の取得と通貨別集計
4. 獲得経路（リファラー）別の分析
5. 成長率・解約率の計算
6. メンバー属性に基づくセグメント分析

**関連システム・外部連携**：
- Stripe: サブスクリプション・決済データの取得
- データベース: メンバーイベント、サブスクリプションイベントの永続化

**権限による制御**：メンバー分析データへのアクセスは、`members` ドキュメントの `browse` 権限を持つユーザーに限定されます。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 8 | ホーム画面 | 参照画面 | メンバー数・収益のサマリー表示 |
| 24 | 成長分析画面 | 主画面 | メンバー増減・収益の成長分析 |

## 機能種別

データ取得 / 計算処理 / 集計処理 / 分析ダッシュボード表示

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| date_from | string | No | 集計開始日（YYYY-MM-DD形式） | ISO 8601日付形式 |
| timezone | string | No | タイムゾーン | IANA タイムゾーン形式 |

### 入力データソース

- データベース: `members`, `members_status_events`, `members_subscription_created_events`, `members_paid_subscription_events`, `members_created_events`, `members_stripe_customers_subscriptions`, `stripe_prices`, `products`

## 出力仕様

### 出力データ

#### メンバー数履歴レスポンス

| 項目名 | 型 | 説明 |
|--------|-----|------|
| date | string | 日付（YYYY-MM-DD形式） |
| paid | number | 有料メンバー数 |
| free | number | 無料メンバー数 |
| comped | number | コンプメンバー数 |
| paid_subscribed | number | 当日の有料登録数 |
| paid_canceled | number | 当日の有料解約数 |

#### サブスクリプション履歴レスポンス

| 項目名 | 型 | 説明 |
|--------|-----|------|
| date | string | 日付（YYYY-MM-DD形式） |
| tier | string | TierのID |
| cadence | string | 課金サイクル（month/year） |
| positive_delta | number | 増加数 |
| negative_delta | number | 減少数 |
| signups | number | 新規登録数 |
| cancellations | number | 解約数 |
| count | number | 累計数 |

### 出力先

- Admin API経由でフロントエンド（React Stats App）へJSON形式で返却

## 処理フロー

### 処理シーケンス

```
1. API リクエスト受信
   └─ memberCountHistory / subscriptions エンドポイント

2. パラメータ解析
   └─ date_from の解析、デフォルト91日前

3. ステータスイベント取得
   └─ members_status_events から日別デルタを集計

4. 現在の合計取得
   └─ members テーブルからステータス別合計を取得

5. 履歴データ生成
   └─ 逆算して各日の累計を計算

6. レスポンス返却
   └─ data配列とmeta.totalsを返却
```

### フローチャート

```mermaid
flowchart TD
    A[API リクエスト] --> B[パラメータ解析]
    B --> C[現在の合計取得]
    C --> D[イベントデルタ取得]
    D --> E{startDate指定?}
    E -->|Yes| F[Complete Range生成]
    E -->|No| G[Sparse Range生成]
    F --> H[データ整形]
    G --> H
    H --> I[レスポンス返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-53-001 | デフォルト集計期間 | 日付指定がない場合、過去91日間を対象 | startDateが未指定 |
| BR-53-002 | 日付補完 | 指定期間内の欠損日はフォワードフィルで補完 | startDate指定時 |
| BR-53-003 | 将来日付除外 | 将来の日付のデータは結果から除外 | 全ての集計 |
| BR-53-004 | 負値防止 | 累計がマイナスになる場合は0に補正 | 累計計算時 |

### 計算ロジック

**メンバーデルタ計算**:
```sql
paid_subscribed = SUM(CASE WHEN to_status='paid' THEN 1 ELSE 0 END)
paid_canceled = SUM(CASE WHEN from_status='paid' THEN 1 ELSE 0 END)
free_delta = SUM(CASE WHEN to_status='free' THEN 1 WHEN from_status='free' THEN -1 ELSE 0 END)
comped_delta = SUM(CASE WHEN to_status='comped' THEN 1 WHEN from_status='comped' THEN -1 ELSE 0 END)
```

**サブスクリプションデルタ計算**:
```sql
positive_delta = SUM(CASE WHEN type IN ('created','reactivated','active') AND mrr_delta != 0 THEN 1 ... END)
negative_delta = SUM(CASE WHEN type IN ('canceled','expired','inactive') AND mrr_delta != 0 THEN 1 ... END)
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| メンバー数取得 | members | SELECT | ステータス別カウント |
| ステータスイベント取得 | members_status_events | SELECT | 日別デルタ集計 |
| サブスクリプション履歴取得 | members_paid_subscription_events | SELECT | Tier/Cadence別デルタ |
| サブスクリプション数取得 | members_stripe_customers_subscriptions | SELECT | Tier/Cadence別カウント |

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

#### members_status_events

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | created_at, from_status, to_status | created_at >= startDate | GROUP BY DATE(created_at) |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | PermissionError | 権限不足 | 適切な権限を持つユーザーでの操作を要求 |
| 500 | InternalError | DB接続エラー | ログ出力後、空の結果を返却 |

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

全て読み取り専用（SELECT）操作のため、トランザクション管理は不要です。

## パフォーマンス要件

- メンバー数履歴取得: 1秒以内
- サブスクリプション履歴取得: 2秒以内

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

- 個人を特定できるメンバー情報は集計データに含めない
- 認証済みスタッフユーザーのみアクセス可能

## 備考

- サブスクリプション統計はStripe連携が有効な場合のみ意味を持つ

---

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | members-stats-service.js | `ghost/core/core/server/services/stats/members-stats-service.js` | MemberStatusDelta, CountHistory型定義（266-299行目） |
| 1-2 | subscription-stats-service.js | `ghost/core/core/server/services/stats/subscription-stats-service.js` | SubscriptionHistoryEntry型定義（169-178行目） |

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | stats.js | `ghost/core/core/server/api/endpoints/stats.js` | memberCountHistory, subscriptions API |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | members-stats-service.js | `ghost/core/core/server/services/stats/members-stats-service.js` | getCount, fetchAllStatusDeltas, getCountHistory |
| 3-2 | subscription-stats-service.js | `ghost/core/core/server/services/stats/subscription-stats-service.js` | getSubscriptionHistory, fetchAllSubscriptionDeltas |

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

```
Admin API Request
    │
    └─ stats.js (API Controller)
           │
           ├─ memberCountHistory
           │      └─ MembersStatsService.getCountHistory()
           │             ├─ getCount()
           │             ├─ fetchAllStatusDeltas()
           │             ├─ _generateCompleteRange()
           │             └─ _generateSparseRange()
           │
           └─ subscriptions
                  └─ SubscriptionStatsService.getSubscriptionHistory()
                         ├─ fetchAllSubscriptionDeltas()
                         └─ fetchSubscriptionCounts()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| stats.js | `ghost/core/core/server/api/endpoints/stats.js` | ソース | API エンドポイント |
| members-stats-service.js | `ghost/core/core/server/services/stats/members-stats-service.js` | ソース | メンバー統計サービス |
| subscription-stats-service.js | `ghost/core/core/server/services/stats/subscription-stats-service.js` | ソース | サブスクリプション統計サービス |
| use-growth-stats.ts | `apps/stats/src/hooks/use-growth-stats.ts` | ソース | 成長統計フック |
| growth.tsx | `apps/stats/src/views/Stats/Growth/growth.tsx` | ソース | 成長分析画面 |
