# 通知設計書 66-new_achievement_email

## 概要

本ドキュメントは、GitLabにおける「新しいアチーブメントが授与されたことを通知する」メール通知機能の設計書である。

### 本通知の処理概要

本通知は、ユーザーにアチーブメント（達成バッジ）が授与された際に、通知メールを送信する機能である。アチーブメントはグループ管理者がユーザーの貢献や功績を称えるために授与するバッジである。

**業務上の目的・背景**：アチーブメント機能は、オープンソースコミュニティや企業内でのユーザーの貢献を認め、モチベーションを高めることを目的としている。ユーザーがアチーブメントを受け取ったことを通知することで、その功績を認識し、プロフィールページで確認できることを伝える。

**通知の送信タイミング**：グループの管理者またはオーナーがユーザーにアチーブメントを授与した直後に送信される。Achievements::AwardServiceの処理が成功した際にトリガーされる。

**通知の受信者**：アチーブメントを授与されたユーザー本人に送信される。ユーザーはアクティブな状態（`user.active?`）である必要がある。

**通知内容の概要**：授与されたアチーブメントの名前、授与したグループ（namespace）の名前とリンク、ユーザープロフィールページへのリンクが含まれる。

**期待されるアクション**：ユーザーは通知を確認し、アチーブメントを受け取ったことを認識する。プロフィールページでアチーブメントを確認し、必要に応じて公開設定を調整することが期待される。

## 通知種別

メール

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（deliver_later） |
| 優先度 | 中 |
| リトライ | Sidekiqのデフォルトリトライ設定に従う |

### 送信先決定ロジック

1. Achievements::AwardServiceが成功後、NotificationService.new_achievement_emailメソッドを呼び出す
2. ユーザーがアクティブな状態かチェック（`user.active?`）
3. ユーザーの通知用メールアドレス（`notification_email_or_default`）に送信

## 通知テンプレート

### メール通知の場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | Gitlab.config.gitlab.email_from |
| 送信元名称 | Gitlab.config.gitlab.email_display_name |
| 件名 | "[namespace_full_path] awarded you the [achievement_name] achievement" |
| 形式 | HTML/テキスト（マルチパート） |

### 本文テンプレート

```
[namespace_full_path] awarded you the [achievement_name] achievement!

View your achievements on your profile.
[プロフィールページURL]
```

### 添付ファイル

なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| @user | 通知対象ユーザー | User | Yes |
| @achievement | 授与されたアチーブメント | Achievements::Achievement | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| API/画面操作 | アチーブメント授与完了 | ユーザーがアクティブ | Achievements::AwardService#execute成功時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| `!user.active?` | ユーザーが非アクティブ状態 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[アチーブメント授与リクエスト] --> B[Achievements::AwardService#execute]
    B --> C{権限チェック}
    C -->|許可| D[UserAchievement.create]
    C -->|拒否| E[エラー返却]
    D --> F{作成成功?}
    F -->|Yes| G[NotificationService.new_achievement_email]
    F -->|No| H[エラー返却]
    G --> I{ユーザーがアクティブ?}
    I -->|Yes| J[new_achievement_email]
    I -->|No| K[処理終了]
    J --> L[deliver_later]
    L --> M[Sidekiqキューに追加]
    M --> N[非同期でメール送信]
```

## データベース参照・更新仕様

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| users | 送信先ユーザー情報取得 | notification_email_or_default |
| achievements | アチーブメント情報取得 | 名前、namespace |
| namespaces | アチーブメント所属グループ情報 | full_path |
| user_achievements | ユーザーとアチーブメントの関連 | 授与記録 |

### テーブル別参照項目詳細

#### users

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | ユーザー識別 | - |
| email | 通知先アドレス | notification_email_or_default |
| state | アクティブ状態確認 | active? |
| preferred_language | メール言語設定 | - |

#### achievements

| 参照項目（カラム名） | 用途 | 取得条件 |
|-------------------|------|---------|
| id | アチーブメント識別 | - |
| name | アチーブメント名 | メール本文・件名 |
| namespace_id | 所属グループ | - |

### 更新テーブル一覧

なし（メール送信のみ。アチーブメント授与自体はAwardServiceで処理）

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 送信失敗 | SMTPサーバー接続エラー | Sidekiqリトライ |
| 宛先不正 | メールアドレス形式エラー | ログ記録、スキップ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | Sidekiqデフォルト（25回） |
| リトライ間隔 | 指数バックオフ |
| リトライ対象エラー | SMTP接続エラー等の一時的エラー |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | なし |
| 1日あたり上限 | なし |

### 配信時間帯

制限なし

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

- ユーザーがアクティブ状態でない場合は送信しない
- アチーブメント名やグループ名はユーザー入力値のため、HTMLエスケープが適用される
- メール本文にはパスワードなどの機密情報は含まれない

## 備考

- アチーブメント機能はグループ単位で管理される
- アチーブメントの授与には適切な権限（can?(:award_achievement, achievement)）が必要
- NotificationService経由でメーラーを直接呼び出し、即座にdeliver_laterを呼び出す特殊なパターン

---

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

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

### 推奨読解順序

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

通知で使用されるデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Achievement | `app/models/achievements/achievement.rb` | アチーブメントモデルの構造 |
| 1-2 | UserAchievement | `app/models/achievements/user_achievement.rb` | ユーザーとアチーブメントの関連 |
| 1-3 | User | `app/models/user.rb` | ユーザーモデルとactive?メソッド |

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

処理の起点となるサービスを確認。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Achievements::AwardService | `app/services/achievements/award_service.rb` | アチーブメント授与のエントリーポイント |

**主要処理フロー**:
1. **行13-29**: `execute`メソッドでアチーブメント授与処理
2. **行14**: アチーブメント取得
3. **行15**: 権限チェック `allowed?`
4. **行17**: 受取人取得
5. **行19-22**: UserAchievement作成
6. **行25**: `NotificationService.new.new_achievement_email(recipient, achievement).deliver_later`で通知送信

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | NotificationService | `app/services/notification_service.rb` | 通知ロジック（行773-775） |

**主要処理フロー**:
- **行773-775**: `new_achievement_email`メソッドでメーラーを呼び出し
- **行774**: `mailer.new_achievement_email(user, achievement)`を返す（deliver_laterは呼び出し元で実行）

#### Step 4: メーラー層を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Emails::Profile | `app/mailers/emails/profile.rb` | メール送信ロジック（行302-316） |
| 4-2 | new_achievement_email.html.haml | `app/views/notify/new_achievement_email.html.haml` | HTMLテンプレート |
| 4-3 | new_achievement_email.text.erb | `app/views/notify/new_achievement_email.text.erb` | テキストテンプレート |

**主要処理フロー**:
- **行302-316**: `new_achievement_email`メソッドでメール生成
- **行303**: `user.active?`チェック
- **行310-313**: 件名にnamespace_full_pathとachievement_nameを含める

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

```
GraphQL Mutation / API Controller
    │
    └─ Achievements::AwardService#execute (行13)
           │
           ├─ Achievement.find(achievement_id) (行14)
           │
           ├─ allowed?(achievement) (行15, 33-35)
           │      └─ current_user.can?(:award_achievement, achievement)
           │
           ├─ User.find(recipient_id) (行17)
           │
           ├─ UserAchievement.create (行19-22)
           │
           └─ NotificationService.new.new_achievement_email
                  .deliver_later (行25)
                  │
                  └─ NotificationService#new_achievement_email (行773)
                         │
                         └─ Notify#new_achievement_email (行302)
                                │
                                ├─ user.active? チェック (行303)
                                │
                                └─ email_with_layout
                                       └─ mail_with_locale
```

### データフロー図

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

管理者操作                    GraphQL/API
(アチーブメント授与)          (Mutation/Controller)
    │                              │
    ▼                              ▼
achievement_id ───────────▶ Achievements::AwardService
recipient_id                       │
                                   ▼
                           UserAchievement.create
                           (データベース保存)
                                   │
                                   ▼
                           NotificationService
                           .new_achievement_email
                           .deliver_later
                                   │
                                   ▼
                           Notify#new_achievement
                           _email
                                   │
                                   ▼
                           Sidekiq ───────────────────▶ メール送信
                                                        (受取人へ)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Achievements::AwardService | `app/services/achievements/award_service.rb` | サービス | アチーブメント授与ロジック |
| NotificationService | `app/services/notification_service.rb` | サービス | 通知ロジックの中核 |
| Emails::Profile | `app/mailers/emails/profile.rb` | メーラー | プロファイル関連メール定義 |
| Notify | `app/mailers/notify.rb` | メーラー | メインメーラークラス |
| new_achievement_email.html.haml | `app/views/notify/new_achievement_email.html.haml` | テンプレート | HTML形式のメールテンプレート |
| new_achievement_email.text.erb | `app/views/notify/new_achievement_email.text.erb` | テンプレート | テキスト形式のメールテンプレート |
| Achievement | `app/models/achievements/achievement.rb` | モデル | アチーブメントモデル |
| UserAchievement | `app/models/achievements/user_achievement.rb` | モデル | ユーザーアチーブメント関連モデル |
