# 画面設計書 28-ダウンロード商品一覧画面

## 概要

本ドキュメントは、Bagisto eコマースモバイルアプリにおけるダウンロード商品一覧画面の設計仕様を記述したものです。購入済みのデジタル商品（ダウンロード可能商品）の一覧表示とダウンロード機能を提供します。

### 本画面の処理概要

本画面は、ユーザーが購入したダウンロード可能商品の一覧を表示し、各商品のダウンロードを実行できる画面です。フィルタリング機能とページネーションによる無限スクロールに対応しています。

**業務上の目的・背景**：デジタル商品（電子書籍、ソフトウェア、音楽、動画等）を扱うeコマースにおいて、購入後のダウンロード管理は重要な機能です。ユーザーは購入したデジタルコンテンツをいつでもダウンロードでき、残りダウンロード回数や注文情報を確認できます。フィルタリング機能により、特定の商品やステータスで絞り込むことができます。

**画面へのアクセス方法**：アカウント情報画面またはダッシュボードから「ダウンロード商品」をタップしてアクセスします。

**主要な操作・処理内容**：
1. 画面初期化時にダウンロード商品一覧をAPIから取得して表示する
2. スクロールによるページネーションで追加商品を取得する
3. フィルターボタンでフィルタリング条件を設定する
4. 各商品のダウンロードボタンでファイルをダウンロードする
5. ダウンロード済み/可能数を確認する

**画面遷移**：アカウント情報画面またはダッシュボードから本画面に遷移します。フィルターボタンでフィルターボトムシートが表示されます。

**権限による表示制御**：ログイン済みユーザーのみアクセス可能です。各ダウンロードリンクは購入者にのみ表示されます。

## 関連機能

| 機能No | 機能名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 16 | ダウンロード可能商品 | 主機能 | 購入済みデジタル商品のダウンロード管理 |

## 画面種別

一覧

## URL/ルーティング

ルート名: `downloadableScreen`（routes定義による）

## 入出力項目

### フィルター入力項目

| 項目名 | 入出力 | 型 | 必須 | 説明 |
|--------|--------|------|------|------|
| status | 入力 | String | 任意 | ダウンロードステータスフィルター |
| title | 入力 | String | 任意 | 商品名フィルター |
| orderId | 入力 | String | 任意 | 注文IDフィルター |
| orderDateFrom | 入力 | String | 任意 | 注文日（開始）フィルター |
| orderDateTo | 入力 | String | 任意 | 注文日（終了）フィルター |

## 表示項目

### ダウンロード商品カード

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| 商品画像 | Image | ダウンロード商品のサムネイル |
| 商品名 | String | productName |
| 注文ID | String | orderId |
| 注文ステータス | String | order.status |
| 注文日時 | String | createdAt |
| 購入済みダウンロード数 | int | downloadBought |
| 使用済みダウンロード数 | int | downloadUsed |
| 残りダウンロード数 | int | available（計算値） |
| ダウンロードボタン | Widget | 残りダウンロードがある場合表示 |

### ページネーション情報

| 項目名 | データ型 | 説明 |
|--------|---------|------|
| total | int | 総件数 |
| currentPage | int | 現在のページ番号 |

## イベント仕様

### 1-画面初期化イベント

画面初期化時（initState）にDownloadableProductsBlocを通じてDownloadableProductsCustomerEventが発行されます。

処理フロー：
1. context.read<DownloadableProductsBloc>()でBlocインスタンスを取得
2. DownloadableProductsCustomerEvent(page, limit)を発行（初期page = 1, limit = 10）
3. scrollController.addListenerで無限スクロールを設定
4. DownloadableProductsRepository.getDownloadableProductCustomerDataが呼び出される

### 2-ページネーションイベント

スクロールが最下部に到達した際に追加商品を取得します。

処理フロー：
1. _setItemScrollListenerがスクロール位置を監視
2. maxScrollExtent到達かつhasMoreData()がtrueの場合
3. pageをインクリメント
4. DownloadableProductsCustomerEventを発行（フィルター条件も含む）
5. 取得したデータを既存リストに追加

### 3-フィルター適用イベント

フィルターボタン押下でフィルターボトムシートを表示し、条件を設定します。

処理フロー：
1. AppBarのフィルターアイコンをタップ
2. showModalBottomSheetでDownloadableOrderFiltersを表示
3. フィルター条件を設定して適用
4. page = 1にリセット
5. DownloadableProductsCustomerEventを発行（フィルター条件付き）
6. フィルタークリア時はappliedFilters = nullでリセット

### 4-ダウンロードイベント

ダウンロードボタン押下時の処理です。

処理フロー：
1. DownloadProductEventを発行（リンクID）
2. ShowLoaderStateでローダー表示
3. DownloadProductStateが成功の場合
4. DownloadBase64ProductEventを発行
5. Base64形式のファイルデータを取得
6. DownloadFile().saveBase64Stringでファイルを保存
7. 失敗時はエラーメッセージを表示

## データベース更新仕様

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

| 操作（イベント） | 対象テーブル | 操作種別 | 概要 |
|----------------|-------------|---------|------|
| 画面表示 | downloadable_link_purchases | SELECT | 購入済みダウンロードリンク一覧を取得 |
| ダウンロード実行 | downloadable_link_purchases | UPDATE | download_usedをインクリメント |

### テーブル別更新項目詳細

#### downloadable_link_purchases

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | * | customer_id = ログインユーザーID | 購入リンク一覧 |
| SELECT | * | フィルター条件（status, title, order_id, date範囲） | フィルター適用時 |
| UPDATE | download_used | +1 | ダウンロード実行時 |

## メッセージ仕様

| メッセージ種別 | メッセージキー | 表示内容 | 表示条件 |
|--------------|--------------|---------|---------|
| タイトル | StringConstants.downloadableProductsList | ダウンロード商品 | AppBarタイトル |
| 空データ | StringConstants.noProducts | 商品がありません | 商品0件時 |
| エラー | graphqlErrors | エラーメッセージ | ダウンロード失敗時 |

## 例外処理

| 例外状況 | 処理内容 | ユーザーへの表示 |
|---------|---------|----------------|
| 商品データなし | EmptyDataViewを表示 | 空のカートアイコンとメッセージを表示 |
| API通信エラー | DownloadableProductsCustomerDataState.failが発行される | エラー状態を維持 |
| ダウンロード失敗 | DownloadBase64ProductState.failが発行される | エラーメッセージを表示 |
| ダウンロード回数超過 | ダウンロードボタンを非表示 | 残り回数0を表示 |

## 備考

- ダウンロード可能数 = downloadBought - downloadUsed
- ダウンロードファイルはBase64エンコードされてAPIから返却される
- DownloadFile().saveBase64StringでBase64をデコードしてファイル保存
- ページサイズ（limit）は10固定
- フィルターはDownloadableFiltersInputモデルで管理
- SkeletonLoaderで初期ローディングを表示
- 無限スクロールで追加データ取得中はLoaderを表示

---

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

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

### 推奨読解順序

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

まず、ダウンロード商品のデータ構造を理解することが重要です。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | downloadable_product_model.dart | `lib/screens/downloadable_products/data_model/downloadable_product_model.dart` | DownloadableProductModel、DownloadableLinkPurchasesの構造 |
| 1-2 | download_product_model.dart | `lib/screens/downloadable_products/data_model/download_product_model.dart` | ダウンロードリンクモデル |
| 1-3 | download_product_Image_model.dart | `lib/screens/downloadable_products/data_model/download_product_Image_model.dart` | Base64ダウンロードデータモデル |

**読解のコツ**: DownloadableProductModelがList<DownloadableLinkPurchases>とPaginatorInfoを持ちます。各DownloadableLinkPurchasesがOrder、OrderItemを参照します。

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

処理の起点となるファイル・関数を特定します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | downloadable_products.dart | `lib/screens/downloadable_products/view/downloadable_products.dart` | DownLoadableScreenクラスがエントリーポイント |

**主要処理フロー**:
1. **38-44行目**: initStateでBloc取得、イベント発行、スクロール監視設定
2. **46-60行目**: _setItemScrollListenerでページネーション処理
3. **68-104行目**: buildメソッドでScaffold、AppBar、フィルターボタン構築
4. **107-134行目**: BlocConsumerで状態に応じた処理
5. **137-171行目**: _downloadableProductsItemsで状態に応じたUI返却
6. **173-206行目**: downloadableListで商品リスト構築

#### Step 3: ウィジェット層を理解する

画面を構成するサブウィジェットを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | download_product_item.dart | `lib/screens/downloadable_products/view/widgets/download_product_item.dart` | 商品カードウィジェット |
| 3-2 | download_button.dart | `lib/screens/downloadable_products/view/widgets/download_button.dart` | ダウンロードボタン |
| 3-3 | downloadable_order_filter.dart | `lib/screens/downloadable_products/view/widgets/downloadable_order_filter.dart` | フィルターボトムシート |

#### Step 4: Bloc層を理解する

状態管理と非同期処理の流れを確認します。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | downloadable_products_bloc.dart | `lib/screens/downloadable_products/bloc/downloadable_products_bloc.dart` | イベント処理とステート発行 |
| 4-2 | downloadable_products_events.dart | `lib/screens/downloadable_products/bloc/downloadable_products_events.dart` | 各イベントの定義 |
| 4-3 | downlaodable_products_state.dart | `lib/screens/downloadable_products/bloc/downlaodable_products_state.dart` | 各ステートの定義 |

**主要処理フロー**:
- **25-40行目** (downloadable_products_bloc.dart): DownloadableProductsCustomerEventの処理
- **41-53行目**: DownloadProductEventの処理
- **54-68行目**: DownloadBase64ProductEventの処理

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

```
DownLoadableScreen (downloadable_products.dart)
    │
    ├─ initState()
    │      ├─ DownloadableProductsBloc.add(DownloadableProductsCustomerEvent)
    │      └─ scrollController.addListener(_setItemScrollListener)
    │
    ├─ build()
    │      ├─ AppBar
    │      │      └─ フィルターボタン → showModalBottomSheet
    │      │             └─ DownloadableOrderFilters
    │      │
    │      └─ _downloadableProductsList() → BlocConsumer
    │             └─ _downloadableProductsItems()
    │                    └─ downloadableList()
    │                           └─ ListView.builder
    │                                  └─ DownloadProductItem
    │                                         └─ ダウンロードボタン
    │
    ├─ _setItemScrollListener()
    │      └─ DownloadableProductsBloc.add(DownloadableProductsCustomerEvent)
    │
    └─ DownloadableProductsBloc (downloadable_products_bloc.dart)
           │
           ├─ DownloadableProductsCustomerEvent
           │      └─ DownloadableProductsRepository.getDownloadableProductCustomerData()
           │
           ├─ DownloadProductEvent
           │      └─ DownloadableProductsRepository.downloadProductLink()
           │
           └─ DownloadBase64ProductEvent
                  └─ DownloadableProductsRepository.dataBase64ProductModel()
                         └─ DownloadFile().saveBase64String()
```

### データフロー図

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

page=1, limit=10        ───▶ DownloadableProductsCustomerEvent ───▶ 商品一覧表示
                                    │
                                    ▼
                         DownloadableProductsRepository
                         .getDownloadableProductCustomerData()
                                    │
                                    ▼
                         DownloadableProductModel             ───▶ ListView.builder
                          ├─ downloadableLinkPurchases[]           │
                          │    ├─ productName                      ▼
                          │    ├─ downloadBought             DownloadProductItem
                          │    ├─ downloadUsed
                          │    └─ order
                          └─ paginatorInfo

フィルター条件           ───▶ showModalBottomSheet             ───▶ フィルター適用
                                    │                               │
                                    ▼                               ▼
                         DownloadableOrderFilters            page=1でリセット
                                    │                               │
                                    ▼                               ▼
                         DownloadableFiltersInput            DownloadableProductsCustomerEvent

ダウンロードボタン       ───▶ DownloadProductEvent             ───▶ ダウンロード実行
                                    │
                                    ▼
                         DownloadableProductsRepository
                         .downloadProductLink()
                                    │
                                    ▼
                         Download                            ───▶ DownloadBase64ProductEvent
                                                                   │
                                                                   ▼
                                                            Base64データ取得
                                                                   │
                                                                   ▼
                                                            DownloadFile().saveBase64String()
                                                                   │
                                                                   ▼
                                                            ファイル保存
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| downloadable_products.dart | `lib/screens/downloadable_products/view/downloadable_products.dart` | ソース | 画面本体のStatefulWidget |
| download_product_item.dart | `lib/screens/downloadable_products/view/widgets/download_product_item.dart` | ソース | 商品カードウィジェット |
| download_button.dart | `lib/screens/downloadable_products/view/widgets/download_button.dart` | ソース | ダウンロードボタン |
| downloadable_order_filter.dart | `lib/screens/downloadable_products/view/widgets/downloadable_order_filter.dart` | ソース | フィルターボトムシート |
| downloadable_products_bloc.dart | `lib/screens/downloadable_products/bloc/downloadable_products_bloc.dart` | ソース | 状態管理Bloc |
| downloadable_products_events.dart | `lib/screens/downloadable_products/bloc/downloadable_products_events.dart` | ソース | Blocイベント定義 |
| downlaodable_products_state.dart | `lib/screens/downloadable_products/bloc/downlaodable_products_state.dart` | ソース | Blocステート定義 |
| dowbloadable_products_repository.dart | `lib/screens/downloadable_products/bloc/dowbloadable_products_repository.dart` | ソース | API通信リポジトリ |
| downloadable_product_model.dart | `lib/screens/downloadable_products/data_model/downloadable_product_model.dart` | ソース | 商品一覧モデル |
| download_product_Image_model.dart | `lib/screens/downloadable_products/data_model/download_product_Image_model.dart` | ソース | Base64ダウンロードモデル |
| order_filters.dart | `lib/screens/downloadable_products/utils/order_filters.dart` | ソース | フィルター入力モデル |
| index.dart | `lib/screens/downloadable_products/utils/index.dart` | ソース | 共通インポートファイル |
