# 機能設計書 77-アカウントデータエクスポート

## 概要

本ドキュメントは、Fizzyアプリケーションにおけるアカウント全体のデータエクスポート機能の設計を記述する。

### 本機能の処理概要

アカウントに含まれる全データ（ボード、カード、ユーザー、設定など）をZIPファイルとしてエクスポートする機能である。エクスポート処理は非同期で実行され、完了時にメールでダウンロードリンクが送信される。ダウンロードリンクは24時間有効。

**業務上の目的・背景**：データのバックアップ、他のFizzyインスタンスへの移行、監査目的でのデータ保存、サービス終了前のデータ取得など、様々な理由でアカウント全体のデータをエクスポートする必要がある。この機能により、ユーザーは自分のデータを完全にコントロールでき、データポータビリティを確保できる。

**機能の利用シーン**：
- 自己ホスト版からSaaS版（またはその逆）へ移行する場合
- 定期的なバックアップを取得したい場合
- アカウントキャンセル前にデータを保存したい場合
- 監査や法的要件のためにデータアーカイブが必要な場合

**主要な処理内容**：
1. アカウント設定画面からエクスポート開始をリクエスト
2. バックグラウンドジョブでZIPファイルを生成
3. 生成完了後、ダウンロードリンク付きのメールを送信
4. ユーザーがリンクをクリックしてファイルをダウンロード

**関連システム・外部連携**：
- Active Storage：ZIPファイルの保存
- メール送信システム：完了通知メール

**権限による制御**：管理者（admin）またはオーナー（owner）ロールを持つユーザーのみがエクスポート可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 53 | アカウント設定画面 | 主画面 | エクスポート開始ダイアログ |
| 54 | エクスポート状況画面 | 結果表示画面 | エクスポート完了後のダウンロード画面 |

## 機能種別

データ出力（バッチ処理）

## 入力仕様

### 入力パラメータ

#### create（エクスポート開始）
| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| なし | - | - | POSTリクエストのみ | - |

#### show（ダウンロード表示）
| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | UUID | Yes | エクスポートID | 完了済みかつ本人のエクスポート |

### 入力データソース

- セッション：Current.account（エクスポート対象）
- セッション：Current.user（実行者・権限確認用）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| ZIPファイル | File | アカウント全データを含むZIPアーカイブ |
| 完了通知メール | Email | ダウンロードリンク付きメール |

### 出力先

- Active Storage：ZIPファイル保存
- メール送信：エクスポート完了通知

## 処理フロー

### 処理シーケンス

```
1. 管理者/オーナーがエクスポート開始をリクエスト
   └─ 確認ダイアログで「Start export」を選択

2. エクスポートレコード作成
   └─ Current.account.exports.create!(user: Current.user)

3. バックグラウンドジョブをキュー
   └─ export.build_later → DataExportJob.perform_later

4. ジョブ実行
   └─ export.build
   └─ processing!でステータス更新
   └─ ZIPファイル生成
   └─ mark_completedでステータス更新
   └─ ExportMailer.completed.deliver_later

5. ユーザーがメールのリンクをクリック
   └─ showアクションでダウンロード画面表示
   └─ 自動的にダウンロード開始（auto-click）

6. ファイルダウンロード
   └─ rails_blob_pathでActive Storageから取得
```

### フローチャート

```mermaid
flowchart TD
    A[開始: エクスポート開始] --> B{管理者/オーナー?}
    B -->|No| C[403 Forbidden]
    B -->|Yes| D{制限チェック<br/>24h以内に10件未満?}
    D -->|No| E[429 Too Many Requests]
    D -->|Yes| F[エクスポートレコード作成]
    F --> G[DataExportJob<br/>キューイング]
    G --> H[アカウント設定に<br/>リダイレクト]
    H --> I[バックグラウンド処理]
    I --> J[ZIPファイル生成]
    J --> K{成功?}
    K -->|No| L[status: failed]
    K -->|Yes| M[status: completed]
    M --> N[完了メール送信]
    N --> O[ユーザーがリンククリック]
    O --> P[show画面表示]
    P --> Q[ファイルダウンロード]
    Q --> R[終了]
    C --> R
    E --> R
    L --> R
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-77-01 | 権限制限 | 管理者またはオーナーのみエクスポート可能 | createアクション時 |
| BR-77-02 | レート制限 | 24時間以内に10件までのエクスポートに制限 | createアクション時 |
| BR-77-03 | ファイル有効期限 | ダウンロードリンクは24時間有効 | showアクション時 |
| BR-77-04 | 本人限定 | エクスポートを作成したユーザーのみダウンロード可能 | showアクション時 |
| BR-77-05 | 完了後のみ | 完了ステータスのエクスポートのみダウンロード可能 | showアクション時 |

### 計算ロジック

- ファイル名: `fizzy-account-{account_id}-export-{export_id}.zip`
- 期限切れ判定: `completed_at < 24.hours.ago`

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| エクスポート作成 | exports | INSERT | エクスポートレコード作成 |
| ステータス更新 | exports | UPDATE | status: pending → processing → completed/failed |
| 件数チェック | exports | SELECT | 24時間以内のエクスポート件数確認 |
| エクスポート取得 | exports | SELECT | showアクションで完了済みエクスポート取得 |

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

#### exports

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | account_id | Current.account.id | - |
| INSERT | user_id | Current.user.id | - |
| INSERT | type | "Account::Export" | STI |
| INSERT | status | "pending" | デフォルト値 |
| UPDATE | status | "processing" | ジョブ開始時 |
| UPDATE | status | "completed" | 完了時 |
| UPDATE | completed_at | Time.current | 完了時 |
| UPDATE | status | "failed" | 失敗時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 403 | 権限エラー | 非管理者/非オーナーによる実行 | Forbiddenエラー |
| 429 | レート制限超過 | 24時間以内に10件以上 | Too Many Requests |
| - | ジョブ失敗 | ZIP生成中のエラー | status: failedに更新、例外を再raise |

### リトライ仕様

バックグラウンドジョブはSolid Queueのデフォルトリトライポリシーに従う。

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

エクスポートレコード作成は単一INSERT。ZIP生成処理はファイル操作のためトランザクション外で実行。ステータス更新は個別に実行。

## パフォーマンス要件

- エクスポート開始レスポンス：1秒以内（非同期処理のため）
- ZIP生成時間：データ量に依存（バックグラウンド実行）
- ダウンロードリンクの有効期限：24時間

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

- ensure_admin_or_ownerによる権限チェック
- accessible_to?メソッドによる本人確認
- レート制限による不正リクエスト防止
- Active Storageの署名付きURLによるダウンロードリンク保護

## 備考

- エクスポートデータはAccount::DataTransfer::Manifestを使用して生成される
- インポート機能（No.78）と対になる機能
- 24時間経過した完了済みエクスポートはExport.cleanupで定期的に削除される

---

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

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

### 推奨読解順序

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

エクスポートのデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Export | `app/models/export.rb` | 基底クラス（STI）の実装 |
| 1-2 | Account::Export | `app/models/account/export.rb` | アカウントエクスポート固有の実装 |

**読解のコツ**:
- Export（基底）:
  - 7行目: enum :statusでpending/processing/completed/failedを定義
  - 9-10行目: scope :currentとscope :expiredで期限管理
  - 16-33行目: buildメソッドでZIP生成処理
- Account::Export:
  - 4行目: filenameメソッドでファイル名定義
  - 7-10行目: populate_zipでデータ収集

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

処理の起点となるコントローラーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | ExportsController | `app/controllers/account/exports_controller.rb` | create/showアクションの実装 |

**主要処理フロー**:
1. **2行目**: before_action :ensure_admin_or_owner - 権限チェック
2. **3行目**: before_action :ensure_export_limit_not_exceeded - レート制限
3. **6行目**: CURRENT_EXPORT_LIMIT = 10 - 制限値定義
4. **11-13行目**: createアクション - レコード作成とジョブキューイング
5. **12行目**: exports.create!(user: Current.user).build_later
6. **17-18行目**: ensure_admin_or_ownerの実装
7. **20-22行目**: ensure_export_limit_not_exceededの実装
8. **24-26行目**: set_export - completed.find_by(id:, user:)

#### Step 3: バックグラウンドジョブを理解する

ZIP生成処理の詳細を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Export#build | `app/models/export.rb` | buildメソッドの実装 |

**主要処理フロー**:
- 20行目: processing!でステータス更新
- 22-24行目: with_contextでコンテキスト設定
- 24行目: ZipFile.create_forでZIP作成
- 27行目: mark_completedでステータス更新
- 28行目: ExportMailer.completed.deliver_later

#### Step 4: ビューを理解する

画面表示の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | _export.html.erb | `app/views/account/settings/_export.html.erb` | エクスポート開始ダイアログ |
| 4-2 | show.html.erb | `app/views/account/exports/show.html.erb` | ダウンロード画面 |

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

```
Account::ExportsController#create
    │
    ├─ ensure_admin_or_owner (before_action)
    │      └─ Current.user.admin? || Current.user.owner?
    │
    ├─ ensure_export_limit_not_exceeded (before_action)
    │      └─ exports.current.count >= CURRENT_EXPORT_LIMIT?
    │
    └─ Current.account.exports.create!(user: Current.user).build_later
           │
           ├─ Account::Export INSERT
           │
           └─ DataExportJob.perform_later(self)
                  │
                  └─ export.build
                         │
                         ├─ processing!
                         │
                         ├─ ZipFile.create_for(file, filename:)
                         │      │
                         │      └─ populate_zip(zip)
                         │             │
                         │             └─ Account::DataTransfer::Manifest
                         │                    └─ each_record_set.export(to: zip)
                         │
                         ├─ mark_completed
                         │
                         └─ ExportMailer.completed.deliver_later
```

### データフロー図

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

POSTリクエスト ───▶ Account::ExportsController#create ───┬─▶ リダイレクト
                          │                               │   (account_settings)
                          │                               │
                          └─▶ exports INSERT              └─▶ 通知（開始）

バックグラウンド ───▶ DataExportJob ─────────────────────▶ ZIP生成
                          │                                   │
                          └─▶ Export#build                    └─▶ Active Storage

完了後 ─────────────▶ ExportMailer ───────────────────────▶ 完了メール

メールのリンク ────▶ #show ───────────────────────────────▶ ダウンロード画面
                          │                                   │
                          └─▶ rails_blob_path ────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| ExportsController | `app/controllers/account/exports_controller.rb` | コントローラー | エクスポート操作のエントリーポイント |
| Export | `app/models/export.rb` | モデル（基底） | エクスポート共通処理 |
| Account::Export | `app/models/account/export.rb` | モデル | アカウントエクスポート固有処理 |
| DataExportJob | `app/jobs/data_export_job.rb` | ジョブ | バックグラウンド処理 |
| _export.html.erb | `app/views/account/settings/_export.html.erb` | ビュー（パーシャル） | エクスポート開始ダイアログ |
| show.html.erb | `app/views/account/exports/show.html.erb` | ビュー | ダウンロード画面 |
| ExportMailer | `app/mailers/export_mailer.rb` | メーラー | 完了通知メール |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（9行目） |
