# 帳票設計書 1-会員一覧CSV

## 概要

会員（Members）データをCSV形式でエクスポートする帳票設計書。Ghost CMSの会員管理機能において、登録されている全会員または条件に合致する会員の情報を一括でCSVファイルとして出力する機能を定義する。

### 本帳票の処理概要

本帳票は、Ghost CMSに登録されている会員情報をCSV形式でエクスポートする機能を提供する。会員データはストリーミング方式で処理され、大量データにも対応できる設計となっている。

**業務上の目的・背景**：サイト運営者が会員情報を外部システムへ移行したり、マーケティング分析のためにデータを抽出したり、バックアップ目的でデータを保存する際に利用される。Ghost以外のメールマーケティングツールやCRMシステムとの連携においても重要な役割を果たす。

**帳票の利用シーン**：管理画面のMembersセクションから「Export」ボタンをクリックして利用する。フィルター条件を設定して特定の会員のみをエクスポートすることも可能。例えば、有料会員のみ、特定のラベルが付与された会員のみ、といった条件付きエクスポートができる。

**主要な出力内容**：
1. 会員基本情報（ID、メールアドレス、名前、メモ）
2. 会員ステータス情報（無料/有料/コンプの区分）
3. ニュースレター購読状況
4. Stripe連携情報（Stripe顧客ID）
5. 作成日時
6. ラベル情報（複数可）
7. Tier（会員プラン）情報

**帳票の出力タイミング**：管理者がMembersセクションで「Export」操作を実行した時点でリアルタイムに生成される。フィルターや検索条件が設定されている場合は、その条件に合致する会員のみがエクスポート対象となる。

**帳票の利用者**：サイト管理者、運営スタッフ、マーケティング担当者。Admin API経由でのエクスポートも可能なため、外部システムからの自動取得も想定される。

## 帳票種別

一覧表（CSVエクスポート）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | Members一覧画面 | /ghost/#/members | Exportボタン |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | CSV |
| 用紙サイズ | N/A |
| 向き | N/A |
| ファイル名 | members.{YYYY-MM-DD}.csv |
| 出力方法 | ダウンロード |
| 文字コード | UTF-8 |

### CSV固有設定

| 項目 | 内容 |
|-----|------|
| 区切り文字 | カンマ（,） |
| 引用符 | ダブルクォート（"） |
| ヘッダー行 | 有 |

## 帳票レイアウト

### レイアウト概要

CSVファイルは1行目がヘッダー行、2行目以降がデータ行の標準的なCSV形式。

```
┌─────────────────────────────────────┐
│          ヘッダー行（カラム名）        │
├─────────────────────────────────────┤
│          データ行1                   │
│          データ行2                   │
│          ...                        │
│          データ行N                   │
└─────────────────────────────────────┘
```

### ヘッダー部（CSVカラム）

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | id | 会員ID | members.id | UUID形式 |
| 2 | email | メールアドレス | members.email | 文字列 |
| 3 | name | 会員名 | members.name | 文字列 |
| 4 | note | メモ | members.note | 文字列 |
| 5 | subscribed_to_emails | ニュースレター購読状況 | members_newsletters結合 | true/false |
| 6 | complimentary_plan | コンプリメンタリープラン | members.status | true/false |
| 7 | stripe_customer_id | Stripe顧客ID | members_stripe_customers.customer_id | 文字列 |
| 8 | created_at | 作成日時 | members.created_at | ISO8601形式 |
| 9 | labels | ラベル | members_labels + labels | カンマ区切り |
| 10 | tiers | Tier名 | members_products + products | カンマ区切り |

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| filter | NQL形式のフィルター条件 | No |
| search | 検索キーワード（名前、メール） | No |
| limit | 取得件数制限 | No（デフォルト: all） |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | 指定なし（デフォルト） | - |

### 改ページ条件

N/A（CSVファイルのため改ページ不要）

## データベース参照仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| members | 会員基本情報 | 主テーブル |
| members_products | 会員とTierの関連 | member_id = members.id |
| products | Tier（製品）情報 | id = members_products.product_id |
| members_labels | 会員とラベルの関連 | member_id = members.id |
| labels | ラベル情報 | id = members_labels.label_id |
| members_stripe_customers | Stripe顧客情報 | member_id = members.id |
| members_newsletters | ニュースレター購読情報 | member_id = members.id |

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

#### members

| 参照項目（カラム名） | 帳票項目との対応 | 取得条件 | 備考 |
|-------------------|----------------|---------|------|
| id | id | - | UUID形式 |
| email | email | - | - |
| name | name | - | NULL許容 |
| note | note | - | NULL許容 |
| status | complimentary_plan | - | 'comped'の場合true |
| created_at | created_at | - | ISO8601形式に変換 |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| subscribed | members_newslettersに該当レコードが存在するか | - | 真偽値 |
| comped | members.status === 'comped' | - | 真偽値 |
| complimentary_plan | members.status === 'complimentary' | - | 真偽値 |
| tiers | members_productsからproduct_idを取得し、products.nameに変換 | - | 配列 |
| labels | members_labelsからlabel_idを取得し、labels.nameに変換 | - | 配列 |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[エクスポート要求] --> B[パラメータ検証]
    B --> C{フィルター有?}
    C -->|Yes| D[会員IDフィルタリング]
    C -->|No| E[全会員取得]
    D --> F[products/labels事前取得]
    E --> F
    F --> G[バッチングストリーム作成]
    G --> H[1000件ずつバッチ処理]
    H --> I[関連データ取得]
    I --> J[データ加工・変換]
    J --> K[CSVレスポンス生成]
    K --> L[ファイル返却]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| 認証エラー | 認証なしでアクセス | 401 Unauthorized | ログイン必要 |
| 権限エラー | browse権限がない | 403 Forbidden | 権限付与必要 |
| データなし | 該当会員なし | 空のCSV | - |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 数万〜数十万件 |
| 目標出力時間 | ストリーミングのため応答開始は即時 |
| 同時出力数上限 | 特に制限なし |

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

- 管理者認証が必要（authAdminApi）
- members.browse権限の確認
- 個人情報（メールアドレス等）を含むため、ダウンロードログの記録を推奨
- HTTPSでの通信が必須

## 備考

- ストリーミング処理により、大量データでもメモリ効率的に処理
- バッチサイズは1000件で固定
- 関連データ（tiers、labels、stripe_customer_id、subscribed）は各バッチごとに並列取得

---

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

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

### 推奨読解順序

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

まず、エクスポートされるデータの構造と、関連するDBテーブルの関係を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | query.js | `ghost/core/core/server/services/members/exporter/query.js` | エクスポートデータの構造、関連テーブルの結合ロジック |

**読解のコツ**: `processMembersData`関数で各会員レコードにtiers、labels、subscribed、stripe_customer_idがどのように付与されるかを確認する。

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

処理の起点となるAPIエンドポイントを特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | members.js | `ghost/core/core/server/api/endpoints/members.js` | exportCSVアクションの定義（370-400行目） |
| 2-2 | routes.js | `ghost/core/core/server/web/api/endpoints/admin/routes.js` | /members/uploadルートの定義（134行目） |

**主要処理フロー**:
1. **370-386行目**: exportCSVアクションのオプション定義（limit, filter, search）
2. **387-390行目**: レスポンスフォーマット（plain、stream）の指定
3. **395-399行目**: membersService.export呼び出し

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

エクスポート処理の本体ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | service.js | `ghost/core/core/server/services/members/service.js` | export関数のエクスポート（203行目） |
| 3-2 | query.js | `ghost/core/core/server/services/members/exporter/query.js` | ストリーミングエクスポートの実装 |

**主要処理フロー**:
- **50-68行目**: フィルター条件がある場合の会員ID取得
- **72-80行目**: 全productsと全labelsの事前取得
- **85-141行目**: バッチ処理用のTransformストリーム
- **168-180行目**: 会員データのストリーミング取得とパイプライン接続

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

```
GET /ghost/api/admin/members/upload
    │
    ├─ api/endpoints/members.js#exportCSV
    │      │
    │      └─ services/members/service.js#export
    │             │
    │             └─ services/members/exporter/query.js#createExportStream
    │                    │
    │                    ├─ knex('products').select() [事前取得]
    │                    ├─ knex('labels').select() [事前取得]
    │                    │
    │                    └─ knex('members').stream()
    │                           │
    │                           ├─ batchingTransform (1000件ずつ)
    │                           │
    │                           └─ processMembersTransform
    │                                  │
    │                                  ├─ knex('members_products')
    │                                  ├─ knex('members_labels')
    │                                  ├─ knex('members_stripe_customers')
    │                                  └─ knex('members_newsletters')
```

### データフロー図

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

リクエスト ───▶ エンドポイント ───▶ サービス層 ───▶ クエリ層
(filter/search)    (members.js)      (service.js)    (query.js)
                                                         │
                                                         ▼
                                              ┌─────────────────┐
                                              │ ストリーミング   │
                                              │ 処理パイプライン │
                                              └─────────────────┘
                                                         │
                                                         ▼
                                              ┌─────────────────┐
                                              │ CSVレスポンス    │
                                              │ (members.YYYY   │
                                              │  -MM-DD.csv)    │
                                              └─────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| members.js | `ghost/core/core/server/api/endpoints/members.js` | ソース | APIエンドポイント定義 |
| service.js | `ghost/core/core/server/services/members/service.js` | ソース | サービス層（export関数公開） |
| query.js | `ghost/core/core/server/services/members/exporter/query.js` | ソース | エクスポートクエリロジック |
| routes.js | `ghost/core/core/server/web/api/endpoints/admin/routes.js` | ソース | ルーティング定義 |
