# 通知設計書 2-ダウンロード進捗通知

## 概要

本ドキュメントは、Bagisto ECモバイルアプリにおけるダウンロード可能商品のダウンロード進捗状況および完了を通知する機能の設計書である。

### 本通知の処理概要

本通知は、ダウンロード可能商品（デジタルコンテンツ）のダウンロード進捗状況をリアルタイムで通知バーに表示し、ダウンロード完了時にユーザーに通知する機能を提供する。進捗バー付きの通知を表示し、完了後はタップでファイルを開くことが可能。

**業務上の目的・背景**：ECサイトで販売されるデジタル商品（電子書籍、音楽、ソフトウェアなど）の購入後、ユーザーはダウンロードを実行する。ダウンロード中の進捗状況を可視化することで、ユーザーは処理の進行状況を把握でき、完了タイミングを逃さず確認できる。これにより、ユーザー体験の向上と、ダウンロード失敗時の早期発見が可能となる。

**通知の送信タイミング**：ダウンロード開始時に通知が生成され、ダウンロードの進捗に応じてリアルタイムで更新される。ダウンロード完了時に最終的な完了通知が表示される。

**通知の受信者**：ダウンロード可能商品を購入し、ダウンロードを開始したユーザー本人のデバイス。

**通知内容の概要**：ファイル名、ダウンロード状況（「開始」または「完了」）、進捗バー（完了までの割合を視覚的に表示）。

**期待されるアクション**：ダウンロード完了後、通知をタップすることでダウンロードしたファイルを直接開くことができる。これにより、ファイルマネージャーを経由せずにコンテンツへ即座にアクセス可能。

## 通知種別

ローカル通知（システム通知バー / 進捗バー付き通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（ダウンロード進捗に連動） |
| 優先度 | 高（Priority.high） |
| リトライ | なし（進捗更新のため再送不要） |

### 送信先決定ロジック

1. ダウンロードを開始したユーザーのデバイスに対してのみ通知
2. ローカル通知のため、端末内で完結
3. 通知IDは固定値「1」を使用（同一ダウンロードの更新用）

## 通知テンプレート

### プッシュ通知の場合

| 項目 | 内容 |
|-----|------|
| 通知ID | 1（固定） |
| チャンネルID | progress channel |
| チャンネル名 | Bagisto Notification |
| バッジ表示 | 無効（channelShowBadge: false） |

### 本文テンプレート

```
タイトル: {ファイル名}
本文:
  - 進行中: "開始" (StringConstants.started)
  - 完了時: "完了" (StringConstants.completed)
進捗バー:
  - 表示条件: progress < total
  - 最大値: {total}
  - 現在値: {progress}
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| なし | - | - | 添付ファイルなし（代わりにファイルパスをペイロードに格納） |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| total | ファイル総サイズ | ダウンロード処理から取得 | Yes |
| progress | 現在のダウンロード済みサイズ | ダウンロード処理から取得 | Yes |
| name | ファイル名/商品名 | ダウンロード処理から取得 | Yes |
| path | ファイル保存パス | ダウンロード処理から取得 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| 画面操作 | ダウンロードボタン押下 | ダウンロード開始時 | 進捗バー付き通知を初期表示 |
| 自動更新 | 進捗更新 | progress値が変化した時 | 進捗バーを更新表示 |
| 自動更新 | ダウンロード完了 | progress == total | 完了メッセージに変更、進捗バー非表示 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 通知権限なし | ユーザーが通知権限を拒否している場合 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[ダウンロード開始] --> B[createDownloadNotification呼び出し]
    B --> C{progress < total?}
    C -->|Yes| D[進捗バー表示]
    C -->|No| E[進捗バー非表示]
    D --> F[本文: started]
    E --> G[本文: completed]
    F --> H[NotificationDetails生成]
    G --> H
    H --> I[ペイロード生成]
    I --> J[flutterLocalNotificationsPlugin.show]
    J --> K{ダウンロード継続?}
    K -->|Yes| L[進捗更新]
    L --> B
    K -->|No| M[完了]
    M --> N[通知タップ待機]
    N --> O{タップ検出}
    O -->|Yes| P[openFile実行]
    O -->|No| Q[終了]
    P --> Q
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| なし | - | ローカル処理のためDB参照なし |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| ローカルファイルシステム | WRITE | ダウンロードファイルの保存 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ファイルオープン失敗 | ファイルパスが無効 | PlatformExceptionをキャッチしてログ出力 |
| プラットフォームエラー | MethodChannel呼び出し失敗 | デバッグログに出力 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし（進捗更新頻度に依存） |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし（ユーザー操作に連動）

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

- ダウンロードファイルのパスは端末内で管理
- MethodChannelによるネイティブコード呼び出しは適切にエラーハンドリング
- ファイルアクセス権限はOSレベルで制御

## 備考

- 通知ID「1」は固定値であるため、複数ダウンロードの同時進行では最新のダウンロードのみが表示される
- `onlyAlertOnce: true` により、更新時に音声・振動は1回のみ
- Androidのみで進捗バー表示が有効（iOSでは標準の通知として表示）
- ファイルを開く機能はネイティブMethodChannelを使用（`defaultChannelName`）

---

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

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

### 推奨読解順序

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

ダウンロード進捗通知のパラメータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | push_notifications_manager.dart | `lib/utils/push_notifications_manager.dart` | createDownloadNotification メソッドの引数構造 |

**読解のコツ**: `flutter_local_notifications` パッケージの `AndroidNotificationDetails` の `showProgress` プロパティが進捗バー表示の鍵となる。

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

ダウンロード処理からの通知呼び出しを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | download_button.dart | `lib/screens/downloadable_products/view/widgets/download_button.dart` | ダウンロード処理と通知呼び出しの連携 |

**主要処理フロー**:
1. ダウンロードボタン押下
2. ファイルダウンロード処理開始
3. 進捗更新のたびに `createDownloadNotification()` を呼び出し
4. 完了時に最終通知表示

#### Step 3: 通知生成処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | push_notifications_manager.dart | `lib/utils/push_notifications_manager.dart` | createDownloadNotification() メソッドの実装詳細 |

**主要処理フロー**:
- **行152-176**: `createDownloadNotification()` メソッド
- **行154**: `Future.delayed` でUIスレッドをブロックしない
- **行155-163**: `AndroidNotificationDetails` の設定
  - **行157**: `channelShowBadge: false` - バッジ非表示
  - **行160**: `onlyAlertOnce: true` - 更新時の通知音を抑制
  - **行161**: `showProgress: progress < total` - 進捗バー表示条件
  - **行162-163**: 進捗値の設定
- **行167-174**: 通知表示とペイロード設定

#### Step 4: ファイルオープン処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | push_notifications_manager.dart | `lib/utils/push_notifications_manager.dart` | openFile() メソッドとMethodChannel連携 |

**主要処理フロー**:
- **行178-189**: `openFile()` メソッド
- **行179**: MethodChannel の定義（`defaultChannelName`）
- **行181-184**: プラットフォーム別のファイルビューア呼び出し
- **行186-188**: エラーハンドリング

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

```
DownloadButton（ダウンロード画面）
    |
    +-- ダウンロード処理開始
            |
            +-- 進捗更新ループ
                    |
                    +-- PushNotificationsManager.instance.createDownloadNotification()
                            |
                            +-- AndroidNotificationDetails生成
                            |       |
                            |       +-- showProgress設定
                            |       +-- maxProgress/progress設定
                            |
                            +-- flutterLocalNotificationsPlugin.show()
                                    |
                                    +-- 通知タップ検出
                                            |
                                            +-- onDidReceiveNotificationResponse
                                                    |
                                                    +-- openFile()
                                                            |
                                                            +-- MethodChannel.invokeMethod('fileviewer')
```

### データフロー図

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

ダウンロード開始 ──────▶ total/progress取得 ──────▶ 進捗計算
                                                        |
                                                        ▼
                                                showProgress判定
                                                        |
                               ┌────────────────────────┼────────────────────────┐
                               ▼                        ▼                        ▼
                        進捗 < 100%               進捗 = 100%              エラー発生
                               |                        |                        |
                               ▼                        ▼                        ▼
                        進捗バー表示              完了メッセージ            エラーログ
                        "started"                 "completed"
                               |                        |
                               ▼                        ▼
                        通知バー更新 ◀──────── ファイルパスをペイロードに格納
                                                        |
                                                        ▼
                                                通知タップ → openFile()
                                                        |
                                                        ▼
                                                ファイルビューア起動
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| push_notifications_manager.dart | `lib/utils/push_notifications_manager.dart` | ソース | ダウンロード進捗通知の生成・表示 |
| download_button.dart | `lib/screens/downloadable_products/view/widgets/download_button.dart` | ソース | ダウンロードUI・処理のトリガー |
| string_constants.dart | `lib/utils/string_constants.dart` | 設定 | started/completed 文字列定数 |
| server_configuration.dart | `lib/utils/server_configuration.dart` | 設定 | defaultChannelName定数 |
