# 機能設計書 59-レビュー投稿

## 概要

本ドキュメントは、商品へのレビュー・評価投稿機能の設計を記述する。

### 本機能の処理概要

本機能は、ログイン済みユーザーが購入した商品に対してレビューを投稿する機能を提供する。レビューには評価（星1-5）、タイトル、コメント、画像添付が可能であり、投稿されたレビューは管理者承認後に公開される。

**業務上の目的・背景**：商品レビューは、他のユーザーの購買意思決定に影響を与える重要な情報である。ユーザーからのフィードバックを収集することで、商品の改善や顧客満足度の向上に活用できる。また、レビュー投稿を促進することで、ECサイト全体の信頼性向上に貢献する。

**機能の利用シーン**：商品詳細画面からレビュー投稿ボタンをタップした時、注文完了後にレビューを依頼された時などにユーザーが利用する。

**主要な処理内容**：
1. 商品情報（画像、名前）の表示
2. 評価（星）の入力
3. タイトル・コメントの入力
4. 画像の添付（オプション）
5. 入力バリデーション
6. バックエンドAPIへのレビュー投稿

**関連システム・外部連携**：GraphQL APIを介してバックエンドサーバーにレビューデータを送信。画像はMultipartFileとしてアップロード。

**権限による制御**：ログイン済みユーザーのみがアクセス可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 27 | レビュー投稿画面 | 主画面 | 商品へのレビュー・評価投稿処理 |
| 7 | 商品詳細画面 | 遷移元画面 | レビュー投稿ボタンから遷移 |

## 機能種別

データ登録（CRUD - Create）/ ファイルアップロード

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| productId | int | Yes | 商品ID | 存在する商品ID |
| rating | int | Yes | 評価（星） | 1-5の整数 |
| title | String | Yes | レビュータイトル | 空文字不可 |
| comment | String | Yes | レビューコメント | 空文字不可 |
| name | String | No | レビュアー名 | - |
| attachments | List<MultipartFile> | No | 添付画像 | - |

### 入力データソース

- 画面遷移パラメータ: imageUrl, productId, productName
- 画面入力フォーム
- ImagePicker: 画像選択

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| success | bool | 処理成功/失敗 |
| message | String | 処理結果メッセージ |

### 出力先

- バックエンドデータベース（GraphQL API経由）
- 通知表示（成功/失敗メッセージ）
- 画面遷移（投稿成功後に前画面へ）

## 処理フロー

### 処理シーケンス

```
1. 画面初期化
   └─ AddReviewBlocの初期化、商品情報の表示
2. フォーム表示
   └─ 評価、タイトル、コメントの入力フィールドを表示
3. 画像選択（オプション）
   └─ ImagePickerで画像を選択
4. ユーザー入力
   └─ 評価の選択、タイトル・コメントの入力
5. 送信ボタンタップ
   └─ バリデーション実行
6. API呼び出し
   └─ AddReviewFetchEventを発行してAPIを呼び出し
7. レスポンス処理
   └─ 成功時は前画面へ遷移、失敗時はエラーメッセージを表示
```

### フローチャート

```mermaid
flowchart TD
    A[画面初期化] --> B[商品情報表示]
    B --> C[フォーム表示]
    C --> D{画像追加?}
    D -->|Yes| E[ImagePicker起動]
    E --> F[画像選択]
    F --> C
    D -->|No| G[評価・コメント入力]
    G --> H[送信ボタンタップ]
    H --> I{バリデーション}
    I -->|失敗| J[エラー表示]
    J --> G
    I -->|成功| K{評価あり?}
    K -->|No| L[評価警告表示]
    L --> G
    K -->|Yes| M[処理中ダイアログ表示]
    M --> N[AddReviewFetchEvent発行]
    N --> O[API呼び出し]
    O --> P{レスポンス}
    P -->|成功| Q[成功通知]
    Q --> R[前画面へ遷移]
    P -->|失敗| S[エラー通知]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-59-01 | 評価必須 | 評価（星）は必須で、1以上の値が必要 | 送信時 |
| BR-59-02 | タイトル必須 | タイトルは必須入力 | 送信時 |
| BR-59-03 | コメント必須 | コメントは必須入力 | 送信時 |
| BR-59-04 | 画像オプション | 画像添付は任意 | 常時 |
| BR-59-05 | 処理中表示 | API呼び出し中は処理中ダイアログを表示 | 送信時 |
| BR-59-06 | 遅延遷移 | 投稿成功後、3秒後に前画面へ遷移 | 成功時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| レビュー投稿 | product_reviews | INSERT | 新規レビューを登録 |

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

#### product_reviews

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | customer_id | ログインユーザーID | 自動設定 |
| INSERT | product_id | フォーム入力値 | - |
| INSERT | rating | フォーム入力値 | 1-5 |
| INSERT | title | フォーム入力値 | - |
| INSERT | comment | フォーム入力値 | - |
| INSERT | name | フォーム入力値 | オプション |
| INSERT | status | pending/approved | 管理者設定に依存 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バリデーションエラー | 必須項目未入力 | フィールドにエラーメッセージ表示 |
| - | 評価未設定エラー | 評価が0 | 警告通知を表示 |
| - | API通信エラー | ネットワーク障害 | エラー通知を表示 |
| - | サーバーエラー | バックエンド処理失敗 | エラー通知を表示 |

### リトライ仕様

エラー発生時は画面に留まり、ユーザーが再度送信ボタンを押すことで再試行可能。

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

API呼び出しはバックエンド側でトランザクション管理される。画像アップロードとレビューデータ登録は同一APIで処理。

## パフォーマンス要件

- レビュー投稿API呼び出し: 5秒以内（画像添付時）
- 画像選択・プレビュー: 即時

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

- ログイン済みユーザーのみアクセス可能
- 認証トークン付きでAPIリクエスト送信
- 画像はMultipartFileでセキュアにアップロード

## 備考

- RatingBar.builderで星評価UIを提供
- 画像は複数枚添付可能
- 投稿後のレビューは管理者承認制の場合がある

---

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

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

### 推奨読解順序

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

レビュー投稿で使用するイベントとレスポンスを把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | add_review_event.dart | `lib/screens/add_review/bloc/add_review_event.dart` | AddReviewFetchEvent, ImagePickerEventの構造 |
| 1-2 | add_review_state.dart | `lib/screens/add_review/bloc/add_review_state.dart` | AddReviewFetchState, ImagePickerStateの構造 |
| 1-3 | add_review_model.dart | `lib/screens/add_review/data_model/add_review_model.dart` | AddReviewModelの構造 |

**読解のコツ**: AddReviewFetchEventはproductId, rating, title, comment, attachmentsを保持。ImagePickerEventは画像選択/削除の操作を管理。

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

レビュー投稿画面のメインウィジェットを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | add_review.dart | `lib/screens/add_review/view/add_review.dart` | 画面構築とBloc連携ロジック |

**主要処理フロー**:
1. **16-23行目**: コンストラクタでimageUrl, productId, productNameを受け取り
2. **42-44行目**: initStateでAddReviewBlocを取得
3. **62-84行目**: BlocConsumerでリスナーとビルダーを設定
4. **161-176行目**: RatingBar.builderで星評価UIを構築
5. **272-336行目**: _onPressSubmitButtonでバリデーションとAPI呼び出し

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

状態管理のロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | add_review_bloc.dart | `lib/screens/add_review/bloc/add_review_bloc.dart` | イベント処理とステート発行 |

**主要処理フロー**:
- **22-36行目**: AddReviewFetchEventの処理
- **37-45行目**: ImagePickerEventの処理（画像追加/削除）

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

API呼び出しのロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | add_review_repository.dart | `lib/screens/add_review/bloc/add_review_repository.dart` | API呼び出し実装 |

**主要処理フロー**:
- **22-35行目**: callAddReviewApiメソッドでApiClient().addReview()を呼び出し

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

```
AddReview (add_review.dart)
    │
    ├─ AddReviewBloc (add_review_bloc.dart)
    │      │
    │      └─ AddReviewRepositoryImp (add_review_repository.dart)
    │             │
    │             └─ ApiClient.addReview()
    │                    │
    │                    └─ GraphQL API (with MultipartFile)
    │
    ├─ RatingBar.builder
    │      └─ 星評価UI
    │
    ├─ TextFormField
    │      └─ タイトル/コメント入力
    │
    └─ AddImageView (add_image_view.dart)
           │
           └─ ImagePicker
                  └─ ImagePickerEvent発行
```

### データフロー図

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

imageUrl, productId ───▶ 画面初期化 ───▶ 商品情報表示
productName

RatingBar ───▶ rating変数更新

TextFormField ───▶ titleController, commentController

ImagePicker ───▶ ImagePickerEvent ───▶ AddReviewBloc ───▶ ImagePickerState
                                              │
                                              ▼
                                    pickedImages更新
                                              │
                                              ▼
送信ボタン ───▶ _onPressSubmitButton ───▶ バリデーション
                                              │
                                              ▼ (OK)
AddReviewFetchEvent ───▶ AddReviewBloc ───▶ AddReviewFetchState
(productId, rating,           │
title, comment,               ▼
attachments)         AddReviewRepositoryImp
                              │
                              ▼
                     ApiClient.addReview()
                              │
                              ▼
                       GraphQL API
                              │
                              ▼
                     AddReviewModel ───▶ 成功/失敗通知 ───▶ 前画面遷移
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| add_review.dart | `lib/screens/add_review/view/add_review.dart` | ソース | メイン画面 |
| add_review_bloc.dart | `lib/screens/add_review/bloc/add_review_bloc.dart` | ソース | 状態管理 |
| add_review_repository.dart | `lib/screens/add_review/bloc/add_review_repository.dart` | ソース | API呼び出し |
| add_review_event.dart | `lib/screens/add_review/bloc/add_review_event.dart` | ソース | イベント定義 |
| add_review_state.dart | `lib/screens/add_review/bloc/add_review_state.dart` | ソース | ステート定義 |
| add_review_model.dart | `lib/screens/add_review/data_model/add_review_model.dart` | ソース | レスポンスモデル |
| add_image_view.dart | `lib/screens/add_review/view/widget/add_image_view.dart` | ソース | 画像追加ウィジェット |
