# 機能設計書 71-メールアドレス変更

## 概要

本ドキュメントは、Fizzyアプリケーションにおけるユーザーのメールアドレス変更機能の設計を記述する。

### 本機能の処理概要

ユーザーが自身のログイン用メールアドレスを変更するための機能である。セキュリティを確保するため、メールアドレス変更は即座に反映されず、新しいメールアドレスへ確認リンクを送信し、ユーザーがリンクをクリックすることで変更が完了する二段階認証方式を採用している。

**業務上の目的・背景**：ユーザーは様々な理由（転職、個人メールアドレスの変更、セキュリティ向上など）でメールアドレスを変更する必要がある。この機能により、ユーザーはアカウントを維持したまま連絡先メールアドレスを安全に更新できる。また、なりすましや不正なメールアドレス変更を防ぐため、新しいメールアドレスの所有者確認が必須となっている。

**機能の利用シーン**：
- ユーザーが会社のメールアドレスから個人メールアドレスに変更したい場合
- ドメイン変更に伴いメールアドレスを更新する必要がある場合
- セキュリティ上の理由でメールアドレスを変更したい場合

**主要な処理内容**：
1. ユーザーがプロフィール編集画面から新しいメールアドレスを入力
2. システムが既存ユーザーとの重複チェックを実行
3. 確認用トークンを生成し、新しいメールアドレスに確認メールを送信
4. ユーザーは30分以内にメール内のリンクをクリックして変更を確定

**関連システム・外部連携**：メール送信システム（ActionMailer）を使用して確認メールを送信する。

**権限による制御**：ユーザーは自分自身のメールアドレスのみ変更可能である。他のユーザーのメールアドレスは変更できない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 41 | メールアドレス追加画面 | 主画面 | 新しいメールアドレスを入力して送信 |
| 42 | メールアドレス追加完了画面 | 結果表示画面 | メールアドレス追加後の確認表示 |

## 機能種別

データ更新（メール認証連携）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| user_id | UUID | Yes | 対象ユーザーのID | Current.identityに紐づくユーザーであること |
| email_address | String | Yes | 新しいメールアドレス | 有効なメールアドレス形式 |

### 入力データソース

- 画面入力：新しいメールアドレス
- セッション：現在ログイン中のIdentity情報

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| 確認メール | Email | 新しいメールアドレスに送信される確認リンク付きメール |
| 完了画面 | HTML | メール送信完了を通知する画面 |

### 出力先

- メール送信：新しいメールアドレス宛
- 画面表示：メール送信完了画面（create.html.erb）

## 処理フロー

### 処理シーケンス

```
1. ユーザーがメールアドレス変更画面（new）にアクセス
   └─ @userをCurrent.identity.usersから取得

2. 新しいメールアドレスを入力してフォーム送信（create）
   └─ rate_limitにより1時間に5回までに制限

3. 重複チェック実行
   └─ 入力されたメールアドレスで既存Identityを検索
   └─ 同一アカウント内に同じメールアドレスのユーザーが存在するか確認

4. 重複がある場合
   └─ アラートメッセージを表示して入力画面にリダイレクト

5. 重複がない場合
   └─ 確認トークンを生成（SignedGlobalID、30分有効）
   └─ UserMailer.email_change_confirmationで確認メールを送信
   └─ 完了画面を表示
```

### フローチャート

```mermaid
flowchart TD
    A[開始: メールアドレス変更画面表示] --> B[新しいメールアドレス入力]
    B --> C{レート制限チェック}
    C -->|制限超過| D[429エラー]
    C -->|OK| E[メールアドレスで既存Identity検索]
    E --> F{同一アカウント内に<br/>既存ユーザーあり?}
    F -->|Yes| G[アラート表示<br/>入力画面にリダイレクト]
    F -->|No| H[確認トークン生成<br/>30分有効]
    H --> I[確認メール送信]
    I --> J[完了画面表示]
    J --> K[終了]
    D --> K
    G --> K
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-71-01 | 重複チェック | 同一アカウント内に同じメールアドレスのユーザーが存在する場合は変更不可 | メールアドレス送信時 |
| BR-71-02 | レート制限 | 1時間に5回までの変更リクエストに制限 | createアクション実行時 |
| BR-71-03 | トークン有効期限 | 確認トークンは30分間有効 | トークン生成時 |
| BR-71-04 | 本人限定 | ユーザーは自分のメールアドレスのみ変更可能 | 全操作時 |

### 計算ロジック

特になし

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 重複チェック | identities | SELECT | メールアドレスでIdentity検索 |
| 重複チェック | users | SELECT | 同一アカウント内の既存ユーザー確認 |

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

#### identities

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | email_address | 入力されたメールアドレスと一致 | Identity.find_by_email_address |

#### users

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | account_id | 対象ユーザーのアカウントIDと一致 | 重複チェック用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バリデーションエラー | 同一アカウント内にメールアドレス重複 | アラートメッセージ表示、入力画面にリダイレクト |
| 429 | レート制限超過 | 1時間に5回以上のリクエスト | エラーページ表示 |
| 403 | 権限エラー | 他ユーザーのメールアドレス変更試行 | Forbiddenエラー |

### リトライ仕様

レート制限に達した場合、1時間後に再試行可能

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

本機能ではデータベース更新を行わないため、トランザクション管理は不要。実際のメールアドレス更新は確認機能（No.72）で行われる。

## パフォーマンス要件

- レスポンス時間：3秒以内（メール送信含む）
- メール送信は非同期（deliver_later）で実行

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

- レート制限による不正リクエスト防止（1時間5回）
- SignedGlobalIDによる改ざん防止付きトークン生成
- トークン有効期限30分による時間制限
- 本人確認のため、変更前のメールアドレスもトークンに含める

## 備考

- メールアドレス変更の完了には、No.72「メールアドレス確認」機能が必要
- Identityは複数のアカウントに紐づく可能性があるため、アカウント単位での重複チェックが必要

---

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

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

### 推奨読解順序

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

メールアドレス変更に関連するデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | User | `app/models/user.rb` | Userモデルの基本構造とincludeされるconcernを確認 |
| 1-2 | EmailAddressChangeable | `app/models/user/email_address_changeable.rb` | メールアドレス変更機能の実装詳細 |
| 1-3 | Identity | `app/models/identity.rb` | メールアドレスを保持するIdentityモデル |

**読解のコツ**: UserモデルはEmailAddressChangeableモジュールをincludeしており、メールアドレス変更のロジックは同モジュールに集約されている。

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

処理の起点となるコントローラーを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | EmailAddressesController | `app/controllers/users/email_addresses_controller.rb` | new/createアクションの実装 |

**主要処理フロー**:
1. **3行目**: `rate_limit to: 5, within: 1.hour` - 1時間に5回のレート制限
2. **8-16行目**: createアクションで重複チェックと確認メール送信
3. **9行目**: `Identity.find_by_email_address` - 既存Identity検索
4. **11-13行目**: 同一アカウント内の重複チェック
5. **15行目**: `send_email_address_change_confirmation` - 確認メール送信

#### Step 3: メールアドレス変更ロジックを理解する

EmailAddressChangeableモジュールの詳細を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | EmailAddressChangeable | `app/models/user/email_address_changeable.rb` | トークン生成とメール送信のロジック |

**主要処理フロー**:
- **2-3行目**: 定数定義（TOKEN_PURPOSE、有効期限30分）
- **20-30行目**: `send_email_address_change_confirmation` - トークン生成とメール送信
- **41-48行目**: `generate_email_address_change_token` - SignedGlobalIDを使用したトークン生成

#### Step 4: ビューを理解する

画面表示の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | new.html.erb | `app/views/users/email_addresses/new.html.erb` | メールアドレス入力フォーム |
| 4-2 | create.html.erb | `app/views/users/email_addresses/create.html.erb` | 送信完了画面 |

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

```
Users::EmailAddressesController#create
    │
    ├─ Identity.find_by_email_address(new_email_address)
    │      └─ 既存Identityの検索
    │
    ├─ identity.users.exists?(account: @user.account)
    │      └─ 同一アカウント内の重複チェック
    │
    └─ @user.send_email_address_change_confirmation(new_email_address)
           │
           ├─ generate_email_address_change_token
           │      └─ to_sgid (SignedGlobalID生成)
           │
           └─ UserMailer.email_change_confirmation.deliver_later
                  └─ 確認メール非同期送信
```

### データフロー図

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

新しいメールアドレス ───▶ EmailAddressesController#create ───▶ 完了画面表示
                              │
                              ├──▶ Identity検索
                              │
                              ├──▶ 重複チェック
                              │
                              ├──▶ トークン生成
                              │
                              └──▶ UserMailer ───────────────▶ 確認メール送信
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| EmailAddressesController | `app/controllers/users/email_addresses_controller.rb` | コントローラー | メールアドレス変更リクエスト処理 |
| EmailAddressChangeable | `app/models/user/email_address_changeable.rb` | モデル（Concern） | メールアドレス変更ロジック |
| User | `app/models/user.rb` | モデル | ユーザー情報管理 |
| Identity | `app/models/identity.rb` | モデル | メールアドレス・認証情報管理 |
| new.html.erb | `app/views/users/email_addresses/new.html.erb` | ビュー | 入力フォーム画面 |
| create.html.erb | `app/views/users/email_addresses/create.html.erb` | ビュー | 送信完了画面 |
| routes.rb | `config/routes.rb` | 設定 | ルーティング定義（21-23行目） |
