# 機能設計書 84-プロジェクトメンバー管理

## 概要

本ドキュメントは、GitLabにおけるプロジェクトメンバー管理機能の設計を記述する。プロジェクトオーナーおよび管理者は、プロジェクトへのメンバー追加、権限変更、削除を行うことができる。

### 本機能の処理概要

**業務上の目的・背景**：ソフトウェア開発プロジェクトでは、開発者、レビュアー、プロジェクトマネージャーなど様々な役割のメンバーが協力して作業を行う。プロジェクトメンバー管理機能は、各メンバーに適切なアクセス権限を付与し、プロジェクトリソースへのアクセスを制御する。これにより、セキュリティを確保しながら効率的なチームコラボレーションを実現する。

**機能の利用シーン**：
- 新しい開発者をプロジェクトに追加する場合
- メンバーの権限レベルを変更する場合（例：DeveloperからMaintainerへ昇格）
- プロジェクトを離れるメンバーを削除する場合
- 招待中のメンバー一覧を確認する場合
- アクセスリクエストを承認・却下する場合

**主要な処理内容**：
1. プロジェクトメンバーの一覧表示（権限レベル、招待状態含む）
2. 新規メンバーの追加（ユーザーID/ユーザー名/メール招待）
3. メンバーの権限レベル変更
4. メンバーの削除
5. 招待メールの再送信
6. アクセスリクエストの表示・承認・却下

**関連システム・外部連携**：
- グループメンバー管理（グループメンバーシップの継承）
- 通知システム（メンバー追加・削除時の通知）
- 招待メールシステム
- システムフック（メンバーイベント通知）

**権限による制御**：
- Ownerはすべてのメンバー操作が可能
- Maintainerは自分以下の権限レベルのメンバー操作が可能
- プロジェクトのOwner権限付与はOwnerのみ可能

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 124 | メンバー一覧 | 主画面 | プロジェクトメンバーの一覧表示 |

## 機能種別

CRUD操作 / 権限管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| user_id | Integer/Array | 条件付き | 追加するユーザーID | user_idまたはusernameが必須 |
| username | String/Array | 条件付き | 追加するユーザー名 | user_idまたはusernameが必須 |
| access_level | Integer | Yes | 権限レベル（10-50） | 有効なアクセスレベルのみ |
| expires_at | Date | No | メンバーシップ有効期限 | 未来日付のみ |
| invite_source | String | No | 招待元の追跡用識別子 | - |

### 入力データソース

- 画面入力（Projects::ProjectMembersController経由）
- API経由

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | Integer | メンバーID |
| user_id | Integer | ユーザーID |
| access_level | Integer | 権限レベル |
| expires_at | Date | 有効期限 |
| created_at | DateTime | 追加日時 |
| invite_email | String | 招待メールアドレス（招待時） |
| invite_accepted_at | DateTime | 招待承諾日時 |
| requested_at | DateTime | アクセスリクエスト日時 |

### 出力先

- 画面表示（メンバー一覧）
- メール送信（招待、権限変更通知）

## 処理フロー

### 処理シーケンス

```
1. メンバー追加リクエスト受信
   └─ Projects::ProjectMembersController
2. 権限チェック
   └─ authorize_admin_project_member!
3. メンバー作成
   └─ Members::CreateService#execute
4. 後処理
   └─ 通知送信、イベント発行、システムフック実行
5. 結果返却
```

### フローチャート

```mermaid
flowchart TD
    A[メンバー追加リクエスト] --> B{管理権限あり?}
    B -->|No| C[アクセス拒否]
    B -->|Yes| D{Owner権限付与?}
    D -->|Yes| E{現在ユーザーはOwner?}
    E -->|No| C
    E -->|Yes| F[メンバー作成処理]
    D -->|No| F
    F --> G{招待制限内?}
    G -->|No| H[TooManyInvitesError]
    G -->|Yes| I[メンバーレコード作成]
    I --> J{メール招待?}
    J -->|Yes| K[招待メール送信]
    J -->|No| L[即時メンバー追加]
    K --> M[結果返却]
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-84-01 | Owner権限制限 | Owner権限の付与はOwnerのみ可能 | access_level=50時 |
| BR-84-02 | 招待数制限 | 一度に招待できるユーザー数はデフォルト100人まで | 常時 |
| BR-84-03 | グループ継承 | プロジェクトの親グループメンバーはプロジェクトメンバーとして扱われる | 常時 |
| BR-84-04 | 最低権限 | プロジェクトメンバーの権限は親グループの権限以上が必要 | 常時 |
| BR-84-05 | 有効期限 | expires_atは未来日付のみ設定可能 | expires_at指定時 |

### 計算ロジック

**アクセスレベル**：
- 10: Guest
- 15: Planner
- 20: Reporter
- 30: Developer
- 40: Maintainer
- 50: Owner

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 追加 | members | INSERT | 新規メンバーレコード作成 |
| 更新 | members | UPDATE | 権限レベル、有効期限更新 |
| 削除 | members | DELETE | メンバーレコード削除 |
| 削除後処理 | todos | DELETE | 関連Todoの削除 |
| 削除後処理 | notification_settings | DELETE | 通知設定の削除 |

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

#### members

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | source_id | プロジェクトID | |
| INSERT | source_type | 'Project' | |
| INSERT | user_id | 対象ユーザーID（既存ユーザー時） | |
| INSERT | invite_email | メールアドレス（招待時） | |
| INSERT | access_level | 指定された権限レベル | |
| INSERT | expires_at | 指定された有効期限 | |
| INSERT | invite_token | 招待トークン（招待時） | |
| UPDATE | access_level | 新しい権限レベル | 権限変更時 |
| DELETE | - | 対象レコード削除 | |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | 権限エラー | 管理権限なし | アクセス拒否画面表示 |
| - | BlankInvitesError | 招待対象未指定 | エラーメッセージ表示 |
| - | TooManyInvitesError | 招待数超過 | エラーメッセージ表示 |
| - | バリデーションエラー | 無効なメールアドレス等 | エラーメッセージ表示 |

### リトライ仕様

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

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

- メンバー追加：単一レコードINSERTのため明示的トランザクション不要
- メンバー削除：関連データ削除を含むが、各サービスで個別処理

## パフォーマンス要件

- メンバー一覧取得：500ms以内
- メンバー追加：2秒以内（メール送信含む）

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

- 権限に基づくアクセス制御の徹底
- 招待トークンの安全な生成・管理
- システムフックによる外部通知

## 備考

- MembershipActionsコンサーンで共通処理を実装
- グループメンバーとの継承関係に注意

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | member.rb | `app/models/member.rb` | メンバーモデルの基底クラス |
| 1-2 | project_member.rb | `app/models/project_member.rb` | プロジェクトメンバー固有の定義 |

**読解のコツ**: Memberモデルは多くのスコープとコールバックを持つ。特にactive, invite, requestスコープの違いを理解することが重要。

**主要処理フロー**:
- **7行目**: enum :level定義がないことに注意（Gitlab::Accessモジュール経由）
- **30-33行目**: アソシエーション定義
- **132-143行目**: activeスコープの定義
- **187-188行目**: invite/non_inviteスコープ
- **193-194行目**: request/non_requestスコープ
- **532-542行目**: invite?, request?, pending?メソッド

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | project_members_controller.rb | `app/controllers/projects/project_members_controller.rb` | コントローラーアクション |

**主要処理フロー**:
- **10行目**: authorize_admin_project_member!によるアクセス制御
- **15-27行目**: indexアクション
- **30行目**: membershipable aliasでMembershipActions共通処理を利用

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | create_service.rb | `app/services/members/create_service.rb` | メンバー作成ロジック |
| 3-2 | destroy_service.rb | `app/services/members/destroy_service.rb` | メンバー削除ロジック |

**主要処理フロー（CreateService）**:
- **10行目**: DEFAULT_INVITE_LIMIT = 100
- **23-43行目**: executeメソッド
- **98-104行目**: add_membersでCreatorService呼び出し

**主要処理フロー（DestroyService）**:
- **7-36行目**: executeメソッド（多数のオプション付き）
- **87-89行目**: destroy_memberで実際の削除
- **122-128行目**: delete_subresourcesでサブリソース削除

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

```
Projects::ProjectMembersController
    │
    ├─ #index
    │      ├─ MembersFinder#execute
    │      ├─ AccessRequestsFinder#execute
    │      └─ present_members
    │
    ├─ #create (via MembershipActions)
    │      └─ Members::CreateService#execute
    │             ├─ Members::Projects::CreatorService#add_members
    │             ├─ after_execute (通知等)
    │             └─ publish_event! (MembersAddedEvent)
    │
    └─ #destroy (via MembershipActions)
           └─ Members::DestroyService#execute
                  ├─ destroy_member
                  ├─ delete_subresources
                  ├─ enqueue_delete_todos
                  └─ publish_destroyed_event
```

### データフロー図

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

ユーザー選択         CreateService                    DB保存
(user_id/email,  ──▶ ├─権限チェック              ──▶  members
 access_level)        ├─CreatorService#add_members
                      └─後処理
                                                      ▼
                                                   招待メール送信
                                                   (invite時)
                                                      ▼
                                                   システムフック
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| member.rb | `app/models/member.rb` | モデル | メンバー基底クラス |
| project_member.rb | `app/models/project_member.rb` | モデル | プロジェクトメンバー |
| project_members_controller.rb | `app/controllers/projects/project_members_controller.rb` | コントローラー | Web UI用 |
| create_service.rb | `app/services/members/create_service.rb` | サービス | 作成ロジック |
| destroy_service.rb | `app/services/members/destroy_service.rb` | サービス | 削除ロジック |
| update_service.rb | `app/services/members/update_service.rb` | サービス | 更新ロジック |
| membership_actions.rb | `app/controllers/concerns/membership_actions.rb` | コンサーン | 共通アクション |
