# バッチ設計書 7-ExploreFeedContentController

## 概要

本ドキュメントは、Wikipedia iOSアプリにおけるExplore Feedコンテンツコントローラー（ExploreFeedContentController）のバッチ設計について記述します。

### 本バッチの処理概要

ExploreFeedContentControllerは、Explore Feed（ホーム画面）に表示するコンテンツソースの更新を管理するコントローラーです。BackgroundFetcherプロトコルに準拠し、バックグラウンドでのコンテンツ更新も行います。

**業務上の目的・背景**：Wikipedia iOSアプリのExplore Feedには、Featured Article（今日の記事）、News（ニュース）、TopRead（人気記事）、OnThisDay（今日の出来事）、Random（ランダム記事）、Location（近くの記事）など多様なコンテンツが表示されます。これらのコンテンツを適切に更新し、ユーザーに最新の情報を提供する必要があります。

**バッチの実行タイミング**：フォアグラウンドでのユーザー操作時、バックグラウンドフェッチ時（3時間間隔）、アプリ起動時に実行されます。

**主要な処理内容**：
1. 複数のコンテンツソース（WMFFeedContentSource、WMFRandomContentSource等）の並列更新
2. 60秒タイムアウトでの処理制御
3. ユーザー設定（言語、表示設定）に基づくコンテンツフィルタリング
4. CoreDataへのコンテンツ保存
5. Widgetの更新トリガー

**前後の処理との関連**：BackgroundAppRefreshTaskから呼び出されます。DatabaseHousekeeperTaskで古いコンテンツが削除されます。

**影響範囲**：WMFContentGroup、UserDefaults（feedRefreshDate）、iOS Widget、ユーザーの言語設定に影響します。

## バッチ種別

コンテンツ更新 / フィード管理

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | イベント駆動 / バックグラウンドフェッチ |
| 実行時刻 | 随時 |
| 実行曜日 | 毎日 |
| 実行日 | 毎日 |
| トリガー | ユーザー操作 / BackgroundAppRefreshTask |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| dataStoreが設定済み | MWKDataStoreが初期化されていること |
| コンテンツソースが初期化済み | contentSourcesが生成されていること |
| preferredSiteURLsが設定済み | ユーザーの言語設定が存在すること |

### 実行可否判定

updateFeedSourcesUserInitiated:completion:が呼び出されると処理が開始されます。NSOperationQueueで管理され、maxConcurrentOperationCount=1で排他制御されます。

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| dataStore | MWKDataStore | Yes | - | データストア参照 |
| wasUserInitiated | BOOL | No | NO | ユーザー起点の更新かどうか |
| date | NSDate | No | nil | 特定日付のコンテンツ取得 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| Wikipedia API | REST API | Feedコンテンツ |
| preferredSiteURLs | 配列 | ユーザーの優先言語 |
| exploreFeedPreferences | NSDictionary | 表示設定 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| CoreData (WMFContentGroup) | DB | 更新されたFeedコンテンツ |
| UserDefaults (wmf_FeedRefreshDate) | UserDefaults | 最終更新日時 |
| WMFWidgetController | iOS Widget | Widget更新トリガー |

### 出力ファイル仕様

ファイル出力はありません。

## 処理フロー

### 処理シーケンス

```
1. updateFeedSourcesUserInitiated:completion:呼び出し
   └─ WMFAsyncBlockOperationを作成
2. operationQueueに追加
   └─ 排他制御（maxConcurrentOperationCount=1）
3. feedImportContextを取得
   └─ バックグラウンドCoreDataコンテキスト
4. 各コンテンツソースの並列実行
   └─ WMFTaskGroupで管理
5. 各ソースのloadNewContentInManagedObjectContext呼び出し
   └─ WMFOptionalNewContentSource/WMFDateBasedContentSource分岐
6. タイムアウト待機（60秒）
   └─ waitInBackgroundWithTimeout
7. 結果保存
   └─ applyExploreFeedPreferencesToAllObjects
   └─ moc.save
8. 完了処理
   └─ wmf_setFeedRefreshDate
   └─ reloadAllWidgetsIfNecessary
   └─ completion callback
```

### フローチャート

```mermaid
flowchart TD
    A[updateFeedSources呼び出し] --> B[WMFAsyncBlockOperation作成]
    B --> C[operationQueueに追加]
    C --> D[feedImportContext取得]
    D --> E[WMFTaskGroup作成]
    E --> F[各ContentSourceをループ]
    F --> G{WMFOptionalNewContentSource?}
    G -->|Yes| H[loadContentForDate]
    G -->|No| I{WMFDateBasedContentSource?}
    I -->|Yes| J[loadContentForDate]
    I -->|No| K[loadNewContentInManagedObjectContext]
    H --> L[group.leave]
    J --> L
    K --> L
    L --> M{全ソース完了?}
    M -->|No| F
    M -->|Yes| N[60秒タイムアウト待機]
    N --> O[applyExploreFeedPreferences]
    O --> P[moc.save]
    P --> Q[wmf_setFeedRefreshDate]
    Q --> R[reloadAllWidgetsIfNecessary]
    R --> S[completion callback]
    S --> T[op.finish]
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| Feed更新 | WMFContentGroup | INSERT/UPDATE | コンテンツグループの作成・更新 |
| 設定適用 | WMFContentGroup | UPDATE | isVisible、dailySortPriority更新 |
| Feed更新 | WMFArticle | INSERT/UPDATE | 記事プレビュー情報 |

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

#### WMFContentGroup

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | midnightUTCDate | 日付のUTC午前0時 | グループ日付 |
| INSERT/UPDATE | date | 現在日時 | 作成/更新日時 |
| UPDATE | isVisible | true/false | 設定に基づく |
| UPDATE | dailySortPriority | 言語順位に基づく | ソート優先度 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | タイムアウト | 60秒以内に完了しない | 部分的な結果で完了 |
| - | CoreData保存エラー | DB書き込み失敗 | DDLogErrorでログ出力 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | 次回フェッチ時 |
| リトライ対象エラー | - |

### 障害時対応

タイムアウト発生時も部分的な結果を保存します。保存エラーはDDLogErrorでログ出力されますが、処理は継続します。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | 全コンテンツソース更新 |
| コミットタイミング | 全ソース完了後 |
| ロールバック条件 | CoreData保存エラー時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | コンテンツソース数 x 言語数（10-50グループ） |
| 目標処理時間 | 60秒（フォアグラウンド）/ 30秒（バックグラウンド） |
| メモリ使用量上限 | システム制限に従う |

## 排他制御

- operationQueueでmaxConcurrentOperationCount=1で排他
- busyプロパティでKVO通知
- WMFExploreFeedContentControllerBusyStateDidChange通知

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| エラーログ | 保存エラー時 | "Error saving: \(saveError)" |
| エラーログ | バックグラウンド保存エラー時 | "Error saving background source update: \(saveError)" |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 保存エラー | - | DDLogError経由 |

## 備考

- WMFFeedRefreshTimeoutInterval = 60秒（フォアグラウンド）
- WMFFeedRefreshBackgroundTimeout = 30秒（バックグラウンド）
- WMFExploreFeedMaximumNumberOfDays = 30日
- コンテンツソース種類：
  - WMFRelatedPagesContentSource（関連記事）
  - WMFContinueReadingContentSource（続きを読む）
  - WMFSuggestedEditsContentSource（編集提案）
  - WMFFeedContentSource（Featured/News/TopRead/PictureOfTheDay）
  - WMFNearbyContentSource（近くの記事）
  - WMFRandomContentSource（ランダム記事）
  - WMFAnnouncementsContentSource（アナウンス）
  - WMFOnThisDayContentSource（今日の出来事）
- BackgroundFetcherプロトコル準拠でperformBackgroundFetch実装
- 言語バリアント移行機能（migrateExploreFeedSettingsToLanguageVariants）対応
