# 機能設計書 72-デジタル商品ダウンロード

## 概要

本ドキュメントは、Shopwareストアフロントにおけるデジタル商品ダウンロード機能の設計を記載する。顧客が購入したデジタル商品（電子書籍、ソフトウェア、音楽ファイル等）をダウンロードできる機能である。

### 本機能の処理概要

デジタル商品ダウンロード機能は、顧客が注文したデジタル商品（ダウンロード可能なファイル）を取得するための機能である。物理的な配送が不要なデジタルコンテンツの販売において、購入後にファイルを提供する重要な役割を担う。

**業務上の目的・背景**：ECサイトでデジタル商品を販売する場合、商品購入後に顧客がファイルをダウンロードできる仕組みが必要となる。従来の物理商品とは異なり、即座に商品を提供でき、在庫管理や配送コストが不要なため、デジタルコンテンツ販売は多くのEC事業者にとって魅力的なビジネスモデルである。本機能はその根幹を支えるものである。

**機能の利用シーン**：
- 顧客が電子書籍を購入し、PDF/EPUBファイルをダウンロードする
- 顧客がソフトウェアを購入し、インストーラーをダウンロードする
- 顧客が音楽やビデオコンテンツを購入し、ファイルをダウンロードする
- 顧客が注文履歴からデジタル商品を再ダウンロードする

**主要な処理内容**：
1. ユーザー認証の確認（ログインまたはゲスト認証）
2. ダウンロードアクセス権限の確認
3. 注文と顧客の紐付け検証
4. メディアファイルの取得
5. ファイルダウンロードレスポンスの生成

**関連システム・外部連携**：
- メディアサービス（ファイルストレージ）
- ダウンロードレスポンス生成サービス
- 認証サービス（ログイン/ゲスト認証）

**権限による制御**：
- ログイン済み顧客：自身の注文に紐づくダウンロード商品のみアクセス可能
- ゲスト顧客：deepLinkCodeを使用した認証でアクセス可能
- アクセス許可フラグ（accessGranted）がtrueの場合のみダウンロード可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 21 | 注文履歴ページ | 主画面 | 注文詳細からデジタル商品のダウンロードリンクをクリック |
| 11 | 注文完了ページ | 参照画面 | 注文完了後のダウンロードリンク表示 |

## 機能種別

データ連携 / ファイル出力

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| orderId | string (UUID) | Yes | 注文ID | UUIDフォーマット |
| downloadId | string (UUID) | Yes | ダウンロードアイテムのID | UUIDフォーマット |
| deepLinkCode | string | No（ゲストの場合は必須） | 注文への直接アクセス用コード | 文字列 |

### 入力データソース

- URLパスパラメータ：orderId, downloadId
- クエリパラメータ：deepLinkCode（ゲスト認証時）
- セッション：ログイン顧客情報
- SalesChannelContext：顧客情報、販売チャネル情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| content | binary | ファイルのバイナリデータ |
| Content-Type | string | ファイルのMIMEタイプ |
| Content-Disposition | string | ファイル名とダウンロード指定 |
| Content-Length | integer | ファイルサイズ |

### 出力先

HTTPレスポンス（ファイルダウンロード）

## 処理フロー

### 処理シーケンス

```
1. リクエスト受信
   └─ DownloadController::downloadFile がリクエストを受け取る

2. ログイン確認
   ├─ 顧客がログインしている場合
   │   └─ 処理を継続
   └─ 顧客がログインしていない場合
       └─ 注文詳細ページへリダイレクト（deepLinkCode付き）

3. ダウンロードRoute呼び出し
   └─ DownloadRoute::load を呼び出し

4. ダウンロードアイテム検索
   ├─ downloadIdで検索
   ├─ orderIdとの紐付け確認
   ├─ 顧客IDとの紐付け確認
   └─ accessGrantedフラグの確認

5. メディアファイル取得
   └─ ダウンロードアイテムに紐づくメディアを取得

6. レスポンス生成
   └─ DownloadResponseGeneratorでファイルダウンロードレスポンスを生成
```

### フローチャート

```mermaid
flowchart TD
    A[リクエスト受信] --> B{顧客ログイン済み?}
    B -->|No| C[注文詳細ページへリダイレクト]
    B -->|Yes| D[DownloadRoute::load 呼び出し]
    D --> E[ダウンロードアイテム検索]
    E --> F{アイテム存在?}
    F -->|No| G[エラー: ファイル未発見]
    F -->|Yes| H{顧客ID一致?}
    H -->|No| G
    H -->|Yes| I{accessGranted = true?}
    I -->|No| G
    I -->|Yes| J{メディア存在?}
    J -->|No| G
    J -->|Yes| K[DownloadResponseGenerator]
    K --> L[ファイルダウンロード]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-72-01 | アクセス権限 | 顧客は自身の注文に紐づくダウンロードアイテムのみアクセス可能 | 全てのダウンロードリクエスト |
| BR-72-02 | アクセス許可 | accessGrantedフラグがtrueの場合のみダウンロード可能 | ダウンロード実行時 |
| BR-72-03 | ゲストアクセス | ゲスト顧客もdeepLinkCodeを使用してダウンロード可能 | ゲスト注文の場合 |
| BR-72-04 | 注文紐付け | ダウンロードは指定された注文IDと紐付いていること | ダウンロード実行時 |

### 計算ロジック

特になし（ファイル提供のみ）

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| ダウンロードアイテム検索 | order_line_item_download | SELECT | downloadIdで検索、権限確認 |
| メディア取得 | media | SELECT | ダウンロードファイルの取得 |

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

#### order_line_item_download

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, media_id, access_granted | WHERE id = :downloadId AND order_line_item.order.id = :orderId AND order_line_item.order.order_customer.customer_id = :customerId AND access_granted = true | 複合条件で検索 |

#### media

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, file_name, file_extension, mime_type | order_line_item_downloadと結合 | ファイル情報取得 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| CUSTOMER_NOT_LOGGED_IN | 認証エラー | ログインしていない状態でアクセス | 注文詳細ページへリダイレクト |
| DOWNLOAD_FILE_NOT_FOUND | ビジネスエラー | ダウンロードアイテムが存在しないまたはアクセス権限がない | 404エラーを返却 |
| MISSING_REQUEST_PARAMETER | バリデーションエラー | orderIdまたはdownloadIdが不足 | 400エラーを返却 |

### リトライ仕様

- ファイル読み込みエラー：システムエラー時は再リクエストが必要
- ネットワークエラー：クライアント側で再ダウンロードを実行

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

- 読み取り専用の処理のためトランザクション不要
- データ更新は発生しない

## パフォーマンス要件

- ファイルダウンロード開始：2秒以内
- 大容量ファイル（1GB以上）：ストリーミングダウンロード対応

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

- **認証**：ログインユーザーまたはゲスト認証が必須
- **認可**：自身の注文に紐づくダウンロードアイテムのみアクセス可能
- **アクセス制御**：accessGrantedフラグによる二重チェック
- **ファイルパス**：直接パス指定によるファイルアクセスを防止（メディアID経由）

## 備考

- デジタル商品の購入フローでは、決済完了後にaccessGrantedフラグがtrueに設定される
- ダウンロード回数の制限が必要な場合は、別途カスタマイズが必要
- 大容量ファイルの配信にはCDN連携を推奨

---

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

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

### 推奨読解順序

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

まず、ダウンロードアイテムのエンティティ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | OrderLineItemDownloadEntity | `src/Core/Checkout/Order/Aggregate/OrderLineItemDownload/OrderLineItemDownloadEntity.php` | ダウンロードアイテムの構造（media_id, access_granted） |
| 1-2 | MediaEntity | `src/Core/Content/Media/MediaEntity.php` | メディアファイルの構造 |

**読解のコツ**: OrderLineItemDownloadEntityはOrderLineItemに紐づくダウンロード可能なファイルを表す。MediaEntityは実際のファイル情報を保持する。

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

処理の起点となるコントローラーを特定。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DownloadController.php | `src/Storefront/Controller/DownloadController.php` | ストアフロント側のエントリーポイント |

**主要処理フロー**:
1. **29行目**: ルート定義（`/account/order/download/{orderId}/{downloadId}`）
2. **30-42行目**: `downloadFile`メソッド
3. **32-38行目**: ログイン確認とリダイレクト処理
4. **41行目**: `downloadRoute->load`の呼び出し

#### Step 3: Store-APIルートを理解する

実際のダウンロードロジックはStore-APIルートに実装されている。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | AbstractDownloadRoute.php | `src/Core/Checkout/Customer/SalesChannel/AbstractDownloadRoute.php` | 抽象クラス定義 |
| 3-2 | DownloadRoute.php | `src/Core/Checkout/Customer/SalesChannel/DownloadRoute.php` | 実装クラス |

**主要処理フロー**:
- **43-51行目**: ルート定義（Store-API）
- **52-87行目**: `load`メソッド
- **54-56行目**: パラメータ取得
- **58-69行目**: ログイン確認とパラメータバリデーション
- **71-77行目**: Criteriaによるダウンロードアイテム検索
- **79-82行目**: 存在確認とエラーハンドリング
- **86行目**: DownloadResponseGeneratorの呼び出し

#### Step 4: ダウンロードレスポンス生成を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | DownloadResponseGenerator.php | `src/Core/Content/Media/File/DownloadResponseGenerator.php` | ファイルダウンロードレスポンス生成 |

**読解のコツ**: DownloadResponseGeneratorはメディアファイルからHTTPレスポンスを生成する共通コンポーネント。Content-Disposition、Content-Type等のヘッダー設定を担当。

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

```
DownloadController::downloadFile (Storefront)
    │
    ├─ ログイン確認
    │   └─ 未ログイン時: 注文詳細ページへリダイレクト
    │
    └─ DownloadRoute::load (Store-API)
           │
           ├─ パラメータバリデーション
           │   └─ orderId, downloadId の存在確認
           │
           ├─ downloadRepository::search
           │   └─ Criteria:
           │       - id = downloadId
           │       - orderLineItem.order.id = orderId
           │       - orderLineItem.order.orderCustomer.customerId = customerId
           │       - accessGranted = true
           │
           └─ DownloadResponseGenerator::getResponse
                  └─ ファイルダウンロードレスポンス生成
```

### データフロー図

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

orderId ────────────┐
                    │
downloadId ─────────┼──▶ DownloadController   ───▶ DownloadRoute
                    │         │                         │
SalesChannelContext─┘         │                         ▼
                              │                  downloadRepository
                              │                         │
                              │                         ├──▶ DB: order_line_item_download
                              │                         │      (SELECT with filters)
                              │                         │
                              │                         ├──▶ DB: media
                              │                         │      (JOIN)
                              ▼                         │
                         Response ◀─────────────────────┘
                    (File binary + headers)        DownloadResponseGenerator
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DownloadController.php | `src/Storefront/Controller/DownloadController.php` | コントローラー | ストアフロントのエントリーポイント |
| AbstractDownloadRoute.php | `src/Core/Checkout/Customer/SalesChannel/AbstractDownloadRoute.php` | 抽象クラス | ルートの抽象定義 |
| DownloadRoute.php | `src/Core/Checkout/Customer/SalesChannel/DownloadRoute.php` | ルート | Store-APIルート実装 |
| DownloadResponseGenerator.php | `src/Core/Content/Media/File/DownloadResponseGenerator.php` | サービス | ダウンロードレスポンス生成 |
| OrderLineItemDownloadEntity.php | `src/Core/Checkout/Order/Aggregate/OrderLineItemDownload/OrderLineItemDownloadEntity.php` | エンティティ | ダウンロードアイテムDB構造 |
| OrderLineItemDownloadCollection.php | `src/Core/Checkout/Order/Aggregate/OrderLineItemDownload/OrderLineItemDownloadCollection.php` | コレクション | ダウンロードアイテムコレクション |
| MediaEntity.php | `src/Core/Content/Media/MediaEntity.php` | エンティティ | メディアファイルDB構造 |
| CustomerException.php | `src/Core/Checkout/Customer/CustomerException.php` | 例外 | 顧客関連例外 |
