# 機能設計書 52-通知既読化

## 概要

本ドキュメントは、InvenTreeモバイルアプリにおける通知既読化機能の設計を記述する。

### 本機能の処理概要

個々の通知メッセージを既読状態に変更し、通知一覧から非表示にする機能を提供する。

**業務上の目的・背景**：在庫管理業務において、確認済みの通知を既読にすることで、未対応の通知と区別できる。これにより、ユーザーは未読通知のみに集中でき、重要な情報の見落としを防止し、効率的な業務遂行が可能となる。通知の既読化は、ユーザーが通知内容を確認したことを明示的に記録する手段として機能する。

**機能の利用シーン**：通知一覧画面で個々の通知アイテムの既読ボタン（ブックマークアイコン）をタップした際に、その通知を既読状態に変更する。既読化後、通知一覧が自動的にリフレッシュされ、該当通知が一覧から消える。

**主要な処理内容**：
1. ユーザーが通知アイテムの既読ボタンをタップする
2. 連打防止のためisDismissingフラグを設定する
3. サーバーAPIで通知を既読状態に更新する
4. 通知一覧をリフレッシュして最新状態を表示する

**関連システム・外部連携**：InvenTree REST API（notifications/{pk}/エンドポイント）との通信を行う。APIバージョンにより使用するエンドポイントが異なる。

**権限による制御**：認証済みユーザーのみがアクセス可能。ユーザーは自身の通知のみ既読化できる。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | 通知画面 | 主機能 | 通知の既読ボタン押下時に既読化処理を実行 |

## 機能種別

CRUD操作（Update）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| notification.pk | int | Yes | 既読化する通知の一意識別子 | 正の整数 |

### 入力データソース

ユーザー操作（既読ボタンタップ）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| - | - | 既読化完了後は通知一覧がリフレッシュされる |

### 出力先

- サーバーAPI（通知ステータス更新）
- 画面表示（通知一覧のリフレッシュ）

## 処理フロー

### 処理シーケンス

```
1. 既読ボタンタップ検知
   └─ onPressed コールバック実行
2. 連打防止チェック
   └─ isDismissingがtrueならば処理スキップ
3. isDismissingフラグ設定
   └─ setState で true に設定
4. API呼び出し
   └─ notification.dismiss() 実行
   └─ APIバージョンにより処理分岐
      ├─ v82以降: update(values: {"read": "true"})
      └─ v82未満: api.post("${url}read/")
5. 一覧リフレッシュ
   └─ refresh(context) で通知一覧を再取得
6. isDismissingフラグ解除
   └─ setState で false に設定
```

### フローチャート

```mermaid
flowchart TD
    A[既読ボタンタップ] --> B{isDismissing?}
    B -->|Yes| C[処理スキップ]
    B -->|No| D[isDismissing = true]
    D --> E{mounted?}
    E -->|No| F[処理終了]
    E -->|Yes| G[notification.dismiss]
    G --> H{API v82以上?}
    H -->|Yes| I[update API呼び出し]
    H -->|No| J[POST read/ 呼び出し]
    I --> K[refresh実行]
    J --> K
    K --> L{mounted?}
    L -->|Yes| M[isDismissing = false]
    L -->|No| F
    M --> N[一覧更新完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-52-01 | 連打防止 | 既読処理中は追加の既読操作を無効化 | isDismissing中 |
| BR-52-02 | 自動リフレッシュ | 既読化完了後に通知一覧を自動更新 | 既読処理成功時 |
| BR-52-03 | APIバージョン分岐 | v82以降はPATCH、未満はPOSTを使用 | API呼び出し時 |

### 計算ロジック

該当なし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 既読化 | notifications | UPDATE | 通知のread属性をtrueに更新 |

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

#### notifications（リモートAPI）

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| UPDATE | read | true | 指定PKの通知を既読化 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 接続エラー | サーバー未接続時 | リフレッシュ後に状態を表示 |
| 404 | NotFound | 通知が既に削除済み | 一覧をリフレッシュして最新状態を反映 |
| 401 | 認証エラー | トークン無効時 | 再ログインを促す |

### リトライ仕様

エラー発生時はリフレッシュ操作で再試行可能

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

単一レコードの更新のためトランザクション制御はサーバー側で管理

## パフォーマンス要件

- 既読化処理は1秒以内に完了
- 一覧リフレッシュは2秒以内

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

- 認証トークンによるアクセス制御
- ユーザー単位の通知分離（他ユーザーの通知は操作不可）
- HTTPS通信による通信暗号化

## 備考

API バージョン82以降では、update() APIを使用するモダンな実装に移行している。従来のPOST `read/` エンドポイントは古いAPIバージョン向けの互換性のために維持されている。

---

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

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

### 推奨読解順序

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

既読化処理で使用するモデルの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | notification.dart | `lib/inventree/notification.dart` | dismiss()メソッドの実装、APIバージョン分岐 |

**読解のコツ**: dismiss()メソッドがAPIバージョンによって異なる処理を行う点に注目。

**主要処理フロー**:
- **40-47行目**: dismiss()メソッドの実装
- **41行目**: APIバージョン82以上のチェック
- **43行目**: モダンAPI - update()でread属性を更新
- **45行目**: 旧API - POST ${url}read/を呼び出し

#### Step 2: UIからの呼び出しを理解する

既読ボタンの処理とウィジェットの状態管理を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | notifications.dart | `lib/widget/notifications.dart` | dismissNotification()メソッド、ボタンUI |

**主要処理フロー**:
- **20行目**: isDismissingフラグの定義
- **41-62行目**: dismissNotification()メソッド
- **45-51行目**: マウント状態確認とフラグ設定
- **53行目**: notification.dismiss()呼び出し
- **55-60行目**: リフレッシュとフラグ解除
- **87-93行目**: 既読ボタンのonPressed定義

#### Step 3: 基底クラスのupdate処理を理解する

InvenTreeModelのupdate()メソッドの実装を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | model.dart | `lib/inventree/model.dart` | update()メソッドの実装 |

**主要処理フロー**:
- **619-641行目**: update()メソッドの定義
- **623-628行目**: PKの検証とURL構築
- **634-637行目**: api.patch()呼び出し

#### Step 4: API通信処理を理解する

PATCH/POSTリクエストの実装を確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | api.dart | `lib/api.dart` | patch()およびpost()メソッド |

**主要処理フロー**:
- **858-882行目**: patch()メソッドの実装
- **1120-1137行目**: post()メソッドの実装

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

```
NotificationWidget (notifications.dart)
    │
    └─ _NotificationState
           │
           ├─ getTiles() - UI構築
           │      └─ IconButton (既読ボタン)
           │             └─ onPressed
           │
           └─ dismissNotification()
                  │
                  ├─ setState(isDismissing = true)
                  │
                  ├─ notification.dismiss() (notification.dart)
                  │      │
                  │      └─ (API v82以上)
                  │             └─ update() (model.dart)
                  │                    └─ api.patch() (api.dart)
                  │      │
                  │      └─ (API v82未満)
                  │             └─ api.post() (api.dart)
                  │
                  ├─ refresh()
                  │
                  └─ setState(isDismissing = false)
```

### データフロー図

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

既読ボタンタップ  ───▶  dismissNotification()      ───▶  通知ステータス更新
                          │
                          ├─ isDismissing = true
                          │
                          ▼
                   notification.dismiss()
                          │
                          ├─ (v82+) PATCH /notifications/{pk}/
                          │         body: {"read": "true"}
                          │
                          └─ (v82-) POST /notifications/{pk}/read/
                          │
                          ▼
サーバー更新完了  ◀─── APIレスポンス             ───▶  リスト更新
                          │
                          ├─ refresh()
                          │
                          └─ isDismissing = false
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| notifications.dart | `lib/widget/notifications.dart` | ソース | 通知一覧表示・既読化UI |
| notification.dart | `lib/inventree/notification.dart` | ソース | 通知モデル・dismiss()実装 |
| model.dart | `lib/inventree/model.dart` | ソース | 基底モデル・update()実装 |
| api.dart | `lib/api.dart` | ソース | API通信（patch/post） |
| refreshable_state.dart | `lib/widget/refreshable_state.dart` | ソース | リフレッシュ処理基底クラス |
