# 機能設計書 86-招待機能

## 概要

本ドキュメントは、GitLabにおけるメンバー招待機能の設計を記述する。プロジェクトやグループの管理者は、メールアドレスを指定してまだGitLabアカウントを持たないユーザーを招待できる。招待されたユーザーはメールに記載されたリンクからアカウントを作成し、メンバーとして参加する。

### 本機能の処理概要

**業務上の目的・背景**：チーム外の協力者や新規参加者を迎える際、相手がGitLabアカウントを持っていない場合がある。招待機能により、メールアドレスのみでメンバーシップを事前に設定し、相手がアカウント作成時に自動的にメンバーとして登録される仕組みを提供する。これにより、スムーズなオンボーディングが可能となる。

**機能の利用シーン**：
- 外部協力者をプロジェクトに招待する場合
- 新入社員がアカウント作成前にチームに追加する場合
- 顧客やパートナーを限定的なアクセス権で招待する場合
- 招待の再送信が必要な場合
- 招待の確認・辞退を行う場合

**主要な処理内容**：
1. メールアドレスによる招待作成
2. 招待メールの送信
3. 招待リンクからのアカウント作成・既存アカウントへの紐付け
4. 招待の承諾処理
5. 招待の辞退処理
6. 招待メールの再送信
7. 招待リマインダーの送信

**関連システム・外部連携**：
- メール送信システム（招待メール）
- ユーザー登録システム（アカウント作成フロー）
- 通知システム

**権限による制御**：
- 招待はメンバー管理権限を持つユーザーのみ実行可能
- 招待の承諾・辞退は招待されたユーザーのみ可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 305 | 招待確認 | 主画面 | 招待承認処理 |
| 306 | 招待辞退 | 主画面 | 招待辞退処理 |

## 機能種別

CRUD操作 / メール送信 / 認証連携

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| email | String | Yes | 招待先メールアドレス（カンマ区切りで複数可） | 有効なメール形式 |
| access_level | Integer | Yes | 権限レベル | 有効なアクセスレベル |
| expires_at | Date | No | メンバーシップ有効期限 | 未来日付のみ |
| invite_source | String | No | 招待元の追跡用識別子 | - |

### 入力データソース

- 画面入力
- API経由

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | メンバーID |
| invite_email | String | 招待メールアドレス |
| invite_token | String | 招待トークン（ハッシュ化） |
| invite_accepted_at | DateTime | 招待承諾日時 |
| created_by_id | Integer | 招待者のユーザーID |

### 出力先

- 画面表示（招待メンバー一覧）
- メール送信（招待メール）

## 処理フロー

### 処理シーケンス

```
1. 招待リクエスト受信
   └─ Members::InviteService#execute
2. メールアドレスバリデーション
   └─ 形式チェック、重複チェック
3. メンバーレコード作成（invite状態）
   └─ invite_token生成、invite_email設定
4. 招待メール送信
   └─ Members::InviteMailer#initial_email
5. 招待承諾
   └─ Member#accept_invite!
6. 招待辞退
   └─ Member#decline_invite!
```

### フローチャート

```mermaid
flowchart TD
    A[招待リクエスト] --> B{メール形式有効?}
    B -->|No| C[バリデーションエラー]
    B -->|Yes| D{既存メンバー?}
    D -->|Yes| E[エラー: 既に招待済み]
    D -->|No| F[メンバーレコード作成]
    F --> G[招待トークン生成]
    G --> H[招待メール送信]
    H --> I[招待完了]

    J[招待リンククリック] --> K{アカウントあり?}
    K -->|Yes| L[既存アカウントに紐付け]
    K -->|No| M[新規アカウント作成]
    L --> N[accept_invite!]
    M --> N
    N --> O[メンバー有効化]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-86-01 | メール一意性 | 同一ソースへの重複招待は不可 | 招待作成時 |
| BR-86-02 | トークン有効性 | 招待トークンは一度使用すると無効化 | 承諾時 |
| BR-86-03 | 招待者権限 | 招待者は自分以下の権限レベルでのみ招待可能 | 招待作成時 |
| BR-86-04 | リマインダー | 未承諾の招待にはリマインダーメールを送信可能 | 一定期間後 |

### 計算ロジック

**招待トークン生成**：Devise.token_generatorによるセキュアなトークン生成。

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 招待作成 | members | INSERT | user_id=null, invite_email設定 |
| 招待承諾 | members | UPDATE | user_id設定、invite_token削除 |
| 招待辞退 | members | DELETE | レコード削除 |

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

#### members

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | invite_email | 招待先メールアドレス | |
| INSERT | invite_token | ハッシュ化されたトークン | |
| INSERT | user_id | null | 承諾前はnull |
| UPDATE | user_id | 承諾ユーザーID | 承諾時 |
| UPDATE | invite_token | null | 承諾時にクリア |
| UPDATE | invite_accepted_at | 現在日時 | 承諾時 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | バリデーションエラー | 無効なメール形式 | エラーメッセージ表示 |
| - | 重複エラー | 既存の招待 | エラーメッセージ表示 |
| - | トークン無効 | 期限切れまたは使用済み | 再招待を促すメッセージ |

### リトライ仕様

招待メール送信失敗時は、resend_invite機能で再送可能。

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

招待承諾時はユーザー作成とメンバー更新をトランザクションで管理。

## パフォーマンス要件

- 招待処理：2秒以内（メール送信含む）

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

- 招待トークンはハッシュ化して保存
- メール送信時のみ平文トークンを使用
- 管理者はサインアップ制限に基づいて招待を制限可能

## 備考

- InviteServiceはCreateServiceを継承
- 招待メールのカスタマイズはテンプレートで対応

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | member.rb | `app/models/member.rb` | 招待関連メソッド（invite?, accept_invite!等） |

**主要処理フロー**:
- **362行目**: generate_invite_tokenコールバック
- **364行目**: after_create :send_invite, if: :invite?
- **532-534行目**: invite?メソッド
- **559-574行目**: accept_invite!メソッド
- **576-584行目**: decline_invite!メソッド
- **586-590行目**: generate_invite_tokenメソッド
- **596-602行目**: resend_inviteメソッド

#### Step 2: サービス層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | invite_service.rb | `app/services/members/invite_service.rb` | 招待サービス |
| 2-2 | accept_invite_service.rb | `app/services/members/accept_invite_service.rb` | 招待承諾サービス |

**主要処理フロー（InviteService）**:
- **4行目**: CreateServiceを継承
- **10-11行目**: parsed_emailsでメールアドレスをパース
- **29-36行目**: validate_invitable!でメール検証
- **39-46行目**: invalid_emailsで無効メールを除外

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

```
Members::InviteService#execute
    │
    ├─ parsed_emails (メールアドレスパース)
    ├─ validate_invitable! (バリデーション)
    ├─ add_members (CreateService継承)
    │      └─ Member.create(invite_email: ...)
    └─ send_invite (after_createコールバック)
           └─ Members::InviteMailer#initial_email

Member#accept_invite!(new_user)
    │
    ├─ user設定
    ├─ invite_token = nil
    ├─ invite_accepted_at設定
    ├─ save
    └─ after_accept_invite
           └─ Members::InviteAcceptedMailer
```

### データフロー図

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

メールアドレス       InviteService                    DB保存
(email,          ──▶ ├─メール検証               ──▶  members
 access_level)        ├─メンバー作成(invite)           (invite状態)
                      └─トークン生成
                                                      ▼
                                                   招待メール送信
                                                      ▼
ユーザー操作
(承諾/辞退)       ──▶  accept_invite!/decline_invite!
                                                      ▼
                                                   メンバー有効化/削除
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| member.rb | `app/models/member.rb` | モデル | 招待関連メソッド |
| invite_service.rb | `app/services/members/invite_service.rb` | サービス | 招待作成 |
| accept_invite_service.rb | `app/services/members/accept_invite_service.rb` | サービス | 招待承諾 |
| invite_mailer.rb | `app/mailers/members/invite_mailer.rb` | メーラー | 招待メール |
| invite_accepted_mailer.rb | `app/mailers/members/invite_accepted_mailer.rb` | メーラー | 承諾通知 |
| invite_declined_mailer.rb | `app/mailers/members/invite_declined_mailer.rb` | メーラー | 辞退通知 |
