# バッチ設計書 9-product_export_generate_task

## 概要

本ドキュメントは、Shopwareシステムにおける商品エクスポート生成バッチ（product_export_generate_task）の設計仕様を定義するものです。

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

このバッチは、設定された商品エクスポートファイルの生成をスケジュールし、外部サービス（Google Shopping、比較サイト等）への商品フィード配信を自動化するためのタスクです。

**業務上の目的・背景**：ECサイトでは、Google Shopping、価格比較サイト、マーケットプレイス等の外部プラットフォームに商品情報を配信するために、定期的に商品フィード（CSV、XML等）を生成する必要があります。本バッチは、各販売チャネルに設定された商品エクスポート設定に基づき、定期的にエクスポートファイルの生成をトリガーします。これにより、商品情報の更新を自動的に外部サービスに反映させ、販売機会を最大化します。

**バッチの実行タイミング**：毎分実行（60秒間隔）。高頻度で実行され、各エクスポート設定の個別インターバルに基づいて実際の生成処理をディスパッチします。

**主要な処理内容**：
1. アクティブなStorefront販売チャネルIDを取得
2. 各販売チャネルの商品エクスポート設定を取得
3. 各エクスポート設定について実行条件をチェック（前回生成からの経過時間、実行中フラグ）
4. 条件を満たす場合、ProductExportPartialGenerationメッセージをディスパッチ
5. スタック状態（is_running=trueのまま長時間経過）のエクスポートをリセット

**前後の処理との関連**：本バッチはメッセージのディスパッチのみを行い、実際のエクスポートファイル生成は別のハンドラーで処理されます。

**影響範囲**：product_exportテーブルの参照とis_runningフラグの更新。メッセージキューへのメッセージ追加。

## バッチ種別

レポート生成 / データ連携

## 実行スケジュール

| 項目 | 内容 |
|-----|------|
| 実行頻度 | 毎分 |
| 実行時刻 | 毎分（60秒間隔） |
| 実行曜日 | 毎日 |
| 実行日 | - |
| トリガー | cron（ScheduledTask） |

## 実行条件

### 前提条件

| 条件 | 説明 |
|-----|------|
| スケジュールタスク登録 | ProductExportGenerateTaskがscheduled_taskテーブルに登録されていること |
| メッセージキュー稼働 | Symfony Messengerのワーカーが稼働していること |
| 商品エクスポート設定 | product_exportテーブルにgenerate_by_cronjob=1の設定が存在すること |

### 実行可否判定

- ScheduledTaskHandlerの基本機能によりタスクのステータスが"scheduled"の場合に実行
- shouldRescheduleOnFailure()がtrueを返すため、失敗時は自動的に再スケジュール
- 各エクスポート設定について以下の条件をチェック：
  - is_running=falseまたはスタック状態
  - generated_at=nullまたは前回生成から設定インターバル以上経過

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | デフォルト値 | 説明 |
|-------------|-----|-----|-------------|------|
| staleMinSeconds | int | No | 300 | スタック判定の最小秒数 |
| staleIntervalFactor | float | No | 2.0 | スタック判定のインターバル係数 |

### 入力データソース

| データソース | 形式 | 説明 |
|-------------|------|------|
| sales_channel | DB | 販売チャネル情報（Storefrontのみ対象） |
| product_export | DB | 商品エクスポート設定 |
| sales_channel_domain | DB | 販売チャネルドメイン情報 |

## 出力仕様

### 出力データ

| 出力先 | 形式 | 説明 |
|-------|------|------|
| product_export | DB | is_runningフラグの更新（スタックリセット時） |
| メッセージキュー | Message | ProductExportPartialGenerationメッセージ |

### 出力ファイル仕様

本バッチはファイル出力を直接行いません。ディスパッチされたメッセージの処理で別途エクスポートファイルが生成されます。

## 処理フロー

### 処理シーケンス

```
1. バッチ開始
   └─ ScheduledTaskHandlerによりrun()メソッドが呼び出される
2. Storefront販売チャネル取得
   └─ type_id=STOREFRONTかつactive=1の販売チャネルIDを取得
3. 各販売チャネルのループ
   └─ 各チャネルの商品エクスポート設定を取得
4. 各エクスポート設定のチェック
   └─ shouldBeRun()で実行条件を判定
5. 実行条件チェック
   └─ is_running=true かつ スタック状態 → フラグリセット
   └─ generated_at=null または インターバル経過 → 実行対象
6. メッセージディスパッチ
   └─ ProductExportPartialGenerationを送信
7. バッチ終了
   └─ 正常終了
```

### フローチャート

```mermaid
flowchart TD
    A[バッチ開始] --> B[Storefront販売チャネル取得]
    B --> C{チャネルあり?}
    C -->|No| H[バッチ終了]
    C -->|Yes| D[エクスポート設定取得]
    D --> E{設定あり?}
    E -->|No| B
    E -->|Yes| F[実行条件チェック]
    F --> G{実行対象?}
    G -->|No| D
    G -->|Yes| I[メッセージディスパッチ]
    I --> D
    B --> H
```

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

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

| 処理 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 販売チャネル取得 | sales_channel | SELECT | Storefront販売チャネルIDの取得 |
| エクスポート設定取得 | product_export | SELECT | エクスポート設定の取得 |
| ドメイン参照 | sales_channel_domain | JOIN | 販売チャネルドメインの確認 |
| スタックリセット | product_export | UPDATE | is_runningフラグのリセット |

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

#### sales_channel

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id | type_id=STOREFRONT AND active=1 | 対象チャネルID取得 |

#### product_export

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | id, generated_at, interval, is_running, updated_at, created_at | generate_by_cronjob=1 AND sales_channel.active=1 | エクスポート設定取得 |
| UPDATE | is_running | 0 | スタック状態のリセット |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | データベース接続エラー | DB接続失敗時 | タスク再スケジュール |
| - | メッセージディスパッチエラー | キュー送信失敗時 | タスク再スケジュール |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | ScheduledTaskHandler標準 |
| リトライ間隔 | ScheduledTaskHandler標準 |
| リトライ対象エラー | 一般的な例外 |

### 障害時対応

shouldRescheduleOnFailure()がtrueのため、失敗時は自動的に次回実行がスケジュールされます。エクスポート生成がスタックした場合は、staleMinSecondsとstaleIntervalFactorに基づいて自動的にリセットされます。

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

| 項目 | 内容 |
|-----|------|
| トランザクション範囲 | 各UPDATE文単位 |
| コミットタイミング | UPDATE文実行後に即時コミット |
| ロールバック条件 | UPDATE文失敗時 |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定処理件数 | 数件〜数十件/実行（エクスポート設定数による） |
| 目標処理時間 | 30秒以内 |
| メモリ使用量上限 | 標準PHPメモリ制限内 |

## 排他制御

- is_runningフラグにより同一エクスポート設定の同時実行を防止
- スタック状態の検出とリセットにより、異常終了時のロック解除を自動化
- スタック判定：max(staleMinSeconds, staleIntervalFactor * interval) 以上経過

## ログ出力

| ログ種別 | 出力タイミング | 出力内容 |
|---------|--------------|---------|
| 開始ログ | バッチ開始時 | タスク開始（ScheduledTaskHandler標準） |
| 終了ログ | バッチ終了時 | タスク完了（ScheduledTaskHandler標準） |
| エラーログ | エラー発生時 | 例外情報（ScheduledTaskHandler標準） |

## 監視・アラート

| 監視項目 | 閾値 | アラート先 |
|---------|-----|----------|
| 処理時間 | 5分 | システム管理者 |
| 連続失敗回数 | 3回 | システム管理者 |
| スタックリセット発生 | 1回 | システム管理者（警告） |

## 備考

- Defaults::SALES_CHANNEL_TYPE_STOREFRONTでStorefront販売チャネルを特定
- generate_by_cronjob=1のエクスポート設定のみが対象
- ProductExportPartialGenerationメッセージをMessageBusInterface経由でディスパッチ
- スタック判定ロジック：
  - lastActivity = updated_at ?? created_at
  - threshold = max(staleMinSeconds, ceil(staleIntervalFactor * interval))
  - isStale = (now - lastActivity) >= threshold
- 毎分実行だが、実際のエクスポート生成は各設定のintervalに依存
