# 機能設計書 65-GDPRリクエスト取消

## 概要

本ドキュメントは、GDPRリクエスト取消機能の設計仕様を定義する。ユーザーが過去に作成したGDPRリクエスト（データ削除要求・更新要求）を取り消す機能を提供する。

### 本機能の処理概要

**業務上の目的・背景**：ユーザーがGDPRリクエストを誤って作成した場合や、状況が変わってリクエストの必要がなくなった場合に、未処理のリクエストを取り消す手段が必要である。本機能により、ユーザーは自身の判断でリクエストの取消を行うことができる。

**機能の利用シーン**：
- 誤ってデータ削除リクエストを作成してしまった場合
- リクエストを作成したが、対応を待たずに自分で問題を解決できた場合
- アカウントの継続利用を決めたため、削除リクエストを取り消したい場合

**主要な処理内容**：
1. GDPRリクエスト一覧でRevokeボタンをタップ
2. GraphQL mutationを呼び出してリクエストを取消
3. 取消成功後、リクエスト一覧を再取得して画面を更新
4. 取消済みリクエストはボタンがグレーアウトされ、再取消不可

**関連システム・外部連携**：
- Bagisto GraphQL API（revokeGdprRequest mutation）

**権限による制御**：ログイン済みユーザーのみ実行可能。自身のリクエストのみ取消可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 33 | GDPRリクエスト画面 | 主画面 | GDPRデータリクエストの取消処理 |

## 機能種別

データ更新処理（UPDATE操作）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| id | int | Yes | 取消対象のリクエストID | 正の整数、String.parseで変換 |

### 入力データソース

- GDPRリクエスト一覧のカード内Revokeボタンタップ時のリクエストID

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| status | bool | 取消成功/失敗 |
| message | String | 結果メッセージ |
| gdprRequest | Object | 取消されたリクエストの詳細（id、status等） |

### 出力先

- SnackBar表示で成功/失敗を通知
- 成功時はリクエスト一覧を再取得

## 処理フロー

### 処理シーケンス

```
1. Revokeボタンタップ
   └─ ステータスがREVOKED以外の場合のみ有効
2. RevokeGdprRequestイベントをBlocに発行
   └─ リクエストIDを引数として渡す
3. GdprBlocでイベント処理
   └─ GdprRepositoryのrevokeGdprRequestメソッドを呼び出し
4. APIクライアント呼び出し
   └─ GraphQL mutation実行（revokeGdprRequest）
5. レスポンス処理
   └─ BaseModelにデシリアライズ
6. 状態更新
   └─ RevokeGdprRequestState.success発行
7. SnackBar表示
   └─ 成功メッセージを表示
8. 一覧再取得
   └─ FetchGdprRequestsListを再発行
```

### フローチャート

```mermaid
flowchart TD
    A[Revokeボタンタップ] --> B{ステータス確認}
    B -->|REVOKED| C[処理スキップ]
    B -->|その他| D[RevokeGdprRequest発行]
    D --> E[GdprLoadingState]
    E --> F[GdprBloc処理]
    F --> G[Repository.revokeGdprRequest]
    G --> H[ApiClient.revokeGdprRequest]
    H --> I{API成功?}
    I -->|Yes| J[RevokeGdprRequestState.success]
    I -->|No| K[RevokeGdprRequestState.fail]
    J --> L[成功SnackBar表示]
    L --> M[一覧再取得]
    K --> N[エラーSnackBar表示]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-65-01 | 認証必須 | ログイン済みユーザーのみ実行可能 | 常時 |
| BR-65-02 | 自己リクエストのみ | ユーザーは自身のリクエストのみ取消可能 | 常時 |
| BR-65-03 | 取消済み不可 | ステータスがREVOKEDのリクエストは再取消不可 | ボタンタップ時 |
| BR-65-04 | ボタングレーアウト | REVOKEDステータスの場合はボタンをグレーアウト | 表示時 |
| BR-65-05 | 成功時一覧更新 | 取消成功後は一覧を自動的に再取得 | API成功時 |

### 計算ロジック

該当なし

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

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

本機能はGraphQL APIを通じてバックエンドのデータを更新する。クライアント側でのデータベース操作は行わない。

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| リクエスト取消 | gdpr_requests（バックエンド） | UPDATE | リクエストのステータスをREVOKEDに更新 |

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

#### gdpr_requests（バックエンド）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | status | 'REVOKED' | 取消ステータス |
| UPDATE | revoked_at | 現在日時 | 取消日時を記録 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | API通信エラー | ネットワーク接続失敗 | RevokeGdprRequestState.fail発行、SnackBar表示 |
| - | 権限エラー | 他ユーザーのリクエストを取消しようとした | エラーメッセージ表示 |
| - | 状態エラー | 既に処理済み/取消済みのリクエスト | 「Failed to revoke request」SnackBar表示 |

### リトライ仕様

自動リトライは実装されていない。ユーザーは再度ボタンを押して取消を試みる必要がある。

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

バックエンド側で管理。クライアント側ではAPIリクエストを1回発行するのみ。

## パフォーマンス要件

- リクエスト取消: 3秒以内にレスポンス

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

- 認証必須（ログインユーザーのみ）
- 自身のリクエストのみ取消可能（バックエンドで検証）
- GDPRコンプライアンス対応機能

## 備考

- ステータスがREVOKEDの場合、ボタンテキストは「Revoked」に変わりグレー表示
- 取消後のリクエストは一覧に残るが、再度取消はできない

---

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

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

### 推奨読解順序

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

処理の起点となる画面ファイルのボタン処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | gdpr_screen.dart | `lib/screens/gdpr/view/gdpr_screen.dart` | Revokeボタンの条件分岐とイベント発行 |

**主要処理フロー**:
1. **240-283行目**: Revokeボタンの定義
2. **243-249行目**: ステータスがREVOKEDの場合は処理をスキップ（return）
3. **250-251行目**: RevokeGdprRequestイベントを発行
4. **253-260行目**: ボタンの色をステータスに応じて変更
5. **265-281行目**: ボタンテキストをステータスに応じて変更

#### Step 2: イベント・状態管理を理解する

BLoCパターンによる状態管理の仕組みを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | gdpr_event.dart | `lib/screens/gdpr/bloc/gdpr_event.dart` | RevokeGdprRequestの定義 |
| 2-2 | gdpr_state.dart | `lib/screens/gdpr/bloc/gdpr_state.dart` | RevokeGdprRequestStateの定義 |
| 2-3 | gdpr_bloc.dart | `lib/screens/gdpr/bloc/gdpr_bloc.dart` | RevokeGdprRequestイベントの処理 |

**主要処理フロー**:
- **gdpr_event.dart 25-31行目**: RevokeGdprRequestでidを保持
- **gdpr_state.dart 72-90行目**: RevokeGdprRequestStateでmessage、status、errorを保持
- **gdpr_bloc.dart 53-62行目**: RevokeGdprRequestイベントを処理、repositoryのrevokeGdprRequestを呼び出し

#### Step 3: リポジトリ層を理解する

API呼び出しの抽象化層を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | gdpr_repository.dart | `lib/screens/gdpr/bloc/gdpr_repository.dart` | revokeGdprRequestメソッドの定義と実装 |

**主要処理フロー**:
- **10行目**: revokeGdprRequestメソッド定義（id引数）
- **41-51行目**: GdprRequestRepoでApiClient().revokeGdprRequestを呼び出し

#### Step 4: 成功時の処理を理解する

取消成功後のUI更新処理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | gdpr_screen.dart | `lib/screens/gdpr/view/gdpr_screen.dart` | BlocConsumer listenerでの成功時処理 |

**主要処理フロー**:
- **103-116行目**: RevokeGdprRequestState成功時の処理
- **104-107行目**: 成功時SnackBar表示
- **108行目**: FetchGdprRequestsListを再発行して一覧更新

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

```
GdprScreen (gdpr_screen.dart)
    │
    ├─ ElevatedButton("Revoke" / "Revoked")
    │      │
    │      ├─ ステータス確認（REVOKEDならreturn）
    │      └─ GdprBloc.add(RevokeGdprRequest)
    │
    └─ BlocConsumer listener
           │
           └─ RevokeGdprRequestState.success
                  ├─ SnackBar表示
                  └─ FetchGdprRequestsList再発行

GdprBloc (gdpr_bloc.dart)
    │
    └─ mapEventToState
           │
           GdprRepository.revokeGdprRequest
               │
               ApiClient.revokeGdprRequest
                   │
                   GraphQL Mutation: revokeGdprRequest
```

### データフロー図

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

リクエストID ───────▶ ステータス確認 ───────▶ RevokeGdprRequest発行
(req['id'])                │                         │
                          │                         ▼
                          │               GdprBloc
                          │                         │
                          │                         ▼
                          │               GdprRepository.revokeGdprRequest
                          │                         │
                          │                         ▼
                          │               ApiClient.revokeGdprRequest
                          │                         │
                          │                         ▼
                          │               GraphQL API
                          │                         │
                          │                         ▼
                          │               BaseModel ───▶ RevokeGdprRequestState
                          │                                     │
                          │                                     ▼
                          └──────────────────────────▶ SnackBar表示 & 一覧更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| gdpr_screen.dart | `lib/screens/gdpr/view/gdpr_screen.dart` | ソース | Revokeボタン配置、成功時処理 |
| gdpr_bloc.dart | `lib/screens/gdpr/bloc/gdpr_bloc.dart` | ソース | 状態管理BLoC |
| gdpr_event.dart | `lib/screens/gdpr/bloc/gdpr_event.dart` | ソース | イベント定義 |
| gdpr_state.dart | `lib/screens/gdpr/bloc/gdpr_state.dart` | ソース | 状態定義 |
| gdpr_repository.dart | `lib/screens/gdpr/bloc/gdpr_repository.dart` | ソース | API呼び出しリポジトリ |
| api_client.dart | `lib/services/api_client.dart` | ソース | GraphQL APIクライアント |
| mutation_query.dart | `lib/services/mutation_query.dart` | ソース | GraphQLクエリ定義 |
