# 機能設計書 54-リンクトラッキング

## 概要

本ドキュメントは、Ghostのリンクトラッキング機能に関する設計を記述します。この機能は、メール内リンクのクリックを追跡し、ニュースレターのエンゲージメントを測定するための機能です。

### 本機能の処理概要

**業務上の目的・背景**：ニュースレター配信において、読者がどのリンクをクリックしたかを把握することは、コンテンツの効果測定やCTAの最適化に不可欠です。本機能により、パブリッシャーは読者の興味・関心を数値で把握できます。

**機能の利用シーン**：ニュースレター配信後のクリック率分析、特定リンクのパフォーマンス確認、A/Bテストでのリンク比較、トップリンクの確認などの場面で活用されます。

**主要な処理内容**：
1. メール送信時にURLをリダイレクトURLに変換
2. クリック時のリダイレクト処理とイベント記録
3. 投稿別のリンククリック集計
4. メンバー別のクリック追跡
5. リダイレクトURLの編集・更新

**関連システム・外部連携**：
- メール配信システム: リンク変換の適用
- Domain Events: クリックイベントの発行・購読

**権限による制御**：リンクトラッキングデータへのアクセスは、`posts` ドキュメントの `browse` 権限を持つユーザーに限定されます。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 28 | 投稿Webトラフィック画面 | 参照画面 | リンククリックの追跡 |
| 30 | 投稿ニュースレター分析画面 | 主画面 | トップリンクの表示 |

## 機能種別

イベント処理 / データ記録 / 集計処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| url | URL | Yes | トラッキング対象URL | 有効なURL形式 |
| post | object | Yes | 投稿オブジェクト | 有効な投稿ID |
| memberUuid | string | Yes | メンバーのUUID | UUID形式 |

### 入力データソース

- メール配信処理: トラッキング対象URL
- リダイレクトリクエスト: クリックイベント
- データベース: `redirects`, `members_click_events`, `posts`

## 出力仕様

### 出力データ

#### リンク統計レスポンス

| 項目名 | 型 | 説明 |
|--------|-----|------|
| link_id | string | リンクID |
| post_id | string | 投稿ID |
| from | URL | リダイレクトURL |
| to | URL | 元のURL |
| click_count | number | クリック数 |

### 出力先

- データベース: `redirects`, `post_links`, `members_click_events`

## 処理フロー

### 処理シーケンス

```
1. リンク変換（メール送信時）
   └─ addTrackingToUrl() でURLをリダイレクトURLに変換

2. リダイレクト作成
   └─ addRedirectToUrl() でリダイレクトエントリを作成
   └─ PostLinkを保存して投稿との関連付け

3. クリック処理（リダイレクト時）
   └─ RedirectEvent発行
   └─ subscribe()でイベント購読
   └─ LinkClickを保存

4. 集計処理
   └─ getLinks() でリンク一覧取得
   └─ クリック数集計
```

### フローチャート

```mermaid
flowchart TD
    A[メール送信] --> B[URL抽出]
    B --> C[addTrackingToUrl]
    C --> D[リダイレクトURL生成]
    D --> E[PostLink保存]
    E --> F[メール送信]

    G[読者がクリック] --> H[リダイレクトURL]
    H --> I[RedirectEvent発行]
    I --> J{メンバーUUID?}
    J -->|Yes| K[LinkClick保存]
    J -->|No| L[スキップ]
    K --> M[元URLにリダイレクト]
    L --> M
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-54-001 | UUID必須 | メンバーUUIDがない場合はクリック記録しない | クリック処理時 |
| BR-54-002 | 帰属情報追加 | サイト内URLにはattribution_type/idを追加 | サイト内リンク |
| BR-54-003 | ref保持 | 元URLのrefパラメータを新URLに引き継ぐ | リンク更新時 |

### 計算ロジック

**クリック数集計**:
```sql
SELECT COUNT(DISTINCT member_id) as click_count
FROM members_click_events
WHERE redirect_id = :redirect_id
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| リダイレクト作成 | redirects | INSERT | リダイレクトエントリ作成 |
| 投稿リンク作成 | post_links | INSERT | 投稿との関連付け |
| クリック記録 | members_click_events | INSERT | クリックイベント記録 |
| リンク更新 | redirects | UPDATE | リダイレクト先変更 |

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

#### members_click_events

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | member_uuid, link_id | RedirectEventから取得 | UUIDがある場合のみ |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 400 | BadRequestError | 無効なフィルター形式 | 正しいフィルター形式を通知 |
| 400 | IncorrectUsageError | 未対応のbulkAction | 対応アクションを通知 |

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

リンク更新時はトランザクション内で実行し、複数リンクの一括更新の整合性を保証します。

## パフォーマンス要件

- クリック処理: 50ms以内
- リンク一覧取得: 1秒以内

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

- メンバーUUIDのハッシュ化検討
- リダイレクトURLの推測困難性確保

## 備考

- リンクトラッキングはニュースレター送信時のみ適用

---

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | click-event.js | `ghost/core/core/server/services/link-tracking/click-event.js` | LinkClickクラス定義 |
| 1-2 | post-link.js | `ghost/core/core/server/services/link-tracking/post-link.js` | PostLinkクラス定義 |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | link-click-tracking-service.js | `ghost/core/core/server/services/link-tracking/link-click-tracking-service.js` | メインサービス |

**主要処理フロー**:
- **191-206行目**: `addRedirectToUrl()` - リダイレクト作成
- **215-219行目**: `addTrackingToUrl()` - トラッキングURL生成
- **221-234行目**: `subscribe()` - クリックイベント購読

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

```
Email Sending
    │
    └─ LinkClickTrackingService
           │
           ├─ addTrackingToUrl(url, post, memberUuid)
           │      └─ addRedirectToUrl(url, post)
           │             ├─ linkRedirectService.getSlugUrl()
           │             ├─ linkRedirectService.addRedirect()
           │             └─ postLinkRepository.save()
           │
           └─ subscribe() [RedirectEvent]
                  └─ linkClickRepository.save(LinkClick)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| link-click-tracking-service.js | `ghost/core/core/server/services/link-tracking/link-click-tracking-service.js` | ソース | メインサービス |
| click-event.js | `ghost/core/core/server/services/link-tracking/click-event.js` | ソース | クリックイベントモデル |
| post-link.js | `ghost/core/core/server/services/link-tracking/post-link.js` | ソース | 投稿リンクモデル |
| post-link-repository.js | `ghost/core/core/server/services/link-tracking/post-link-repository.js` | ソース | リポジトリ |
| link-click-repository.js | `ghost/core/core/server/services/link-tracking/link-click-repository.js` | ソース | リポジトリ |
