# 通知設計書 9-スケジュールジョブ実行結果通知

## 概要

本ドキュメントは、RuoYiシステムにおけるスケジュールジョブ実行結果通知（ログ記録）機能の設計仕様を記述したものである。Quartzフレームワークで管理される定時ジョブの実行結果（成功/失敗、実行時間、例外情報等）をジョブログとして記録する機能について定義する。

### 本通知の処理概要

本通知は、システム内で定時実行されるバッチジョブ（Quartzジョブ）の実行結果を監査ログとしてデータベースに記録する機能を提供する。ジョブ名、ジョブグループ、呼び出し対象、開始時刻、終了時刻、実行時間、実行ステータス、例外情報などを記録し、ジョブの運用監視に活用される。

**業務上の目的・背景**：バッチ処理の運用管理およびトラブルシューティングのため、全ての定時ジョブの実行履歴を記録する必要がある。ジョブの実行状況監視、失敗ジョブの検知・復旧、パフォーマンス分析などの業務要件を満たすために本機能が実装されている。

**通知の送信タイミング**：Quartzジョブの実行が完了した時点（正常終了・例外発生両方）でログ記録が実行される。AbstractQuartzJob.after()メソッドでトリガーされる。

**通知の受信者**：システム管理者（ジョブログ閲覧権限を持つユーザー）。記録されたログは監視画面（/monitor/jobLog）から参照可能。

**通知内容の概要**：ジョブ名、ジョブグループ、呼び出し対象（メソッド）、開始時刻、終了時刻、実行時間、実行ステータス（成功/失敗）、例外情報（失敗時）が記録される。

**期待されるアクション**：管理者はジョブログを監視し、失敗ジョブの検知・対応、長時間実行ジョブの調査、ジョブスケジュールの最適化などを行う。

## 通知種別

ログ記録（データベース監査ログ）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（ジョブ実行スレッド内） |
| 優先度 | 高 |
| リトライ | 無 |

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

- ログは一律でsys_job_logテーブルに記録
- 全てのQuartzジョブ実行が対象

## 通知テンプレート

### ログ記録の場合

| 項目 | 内容 |
|-----|------|
| 記録先 | sys_job_logテーブル |
| 記録形式 | データベースレコード |

### 本文テンプレート

```
ジョブ名: {jobName}
ジョブグループ: {jobGroup}
呼び出し対象: {invokeTarget}
ログ情報: {jobMessage}
実行ステータス: {status}
例外情報: {exceptionInfo}
開始時刻: {startTime}
終了時刻: {endTime}
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| - | - | - | 添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| jobLogId | ジョブログID | 自動採番 | Yes |
| jobName | ジョブ名 | sysJob.getJobName() | Yes |
| jobGroup | ジョブグループ | sysJob.getJobGroup() | Yes |
| invokeTarget | 呼び出し対象 | sysJob.getInvokeTarget() | Yes |
| jobMessage | ログ情報 | ジョブ名 + 実行時間 | Yes |
| status | 実行ステータス | 0=成功, 1=失敗 | Yes |
| exceptionInfo | 例外情報 | 例外メッセージ | No |
| startTime | 開始時刻 | ThreadLocal | Yes |
| endTime | 終了時刻 | 現在日時 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ジョブ実行 | ジョブ完了 | 常時 | AbstractQuartzJob.after() |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| なし | 全てのジョブ実行が記録対象 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Quartzトリガー発火] --> B[AbstractQuartzJob.execute]
    B --> C[before: 開始時刻記録]
    C --> D[doExecute: ジョブ実行]
    D --> E{例外発生?}
    E -->|No| F[after: 正常終了処理]
    E -->|Yes| G[after: 例外処理]
    F --> H[SysJobLog構築]
    G --> H
    H --> I[実行時間計算]
    I --> J[ステータス設定]
    J --> K{例外あり?}
    K -->|Yes| L[例外情報設定]
    K -->|No| M[ISysJobLogService.addJobLog]
    L --> M
    M --> N[sys_job_logにINSERT]
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| sys_job | ジョブ定義の取得 | JobDataMapから取得 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| sys_job_log | INSERT | ジョブ実行ログ記録 |

#### sys_job_log テーブル

| 操作 | 項目（カラム名） | 更新値 | 備考 |
|-----|-----------------|-------|------|
| INSERT | job_log_id | 自動採番 | 主キー |
| INSERT | job_name | ジョブ名 | - |
| INSERT | job_group | ジョブグループ | - |
| INSERT | invoke_target | 呼び出し対象 | Bean.method形式 |
| INSERT | job_message | ログ情報 | ジョブ名 + 実行時間 |
| INSERT | status | ステータス | 0=成功,1=失敗 |
| INSERT | exception_info | 例外情報 | 最大2000文字 |
| INSERT | start_time | 開始時刻 | - |
| INSERT | end_time | 終了時刻 | - |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ジョブ実行例外 | ジョブ内でException発生 | 例外情報を記録、status=1 |
| DB接続エラー | データベース接続不可 | ログ出力のみ |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0回 |
| リトライ間隔 | - |
| リトライ対象エラー | - |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし。ジョブ実行に応じて常時記録。

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

- **例外情報の切り詰め**：例外メッセージは最大2000文字に制限
- **ジョブホワイトリスト**：実行可能なジョブはcom.ruoyi.quartz.taskパッケージに限定
- **危険な文字列の禁止**：java.net.URL, javax.naming.InitialContext等は禁止

## 備考

- AbstractQuartzJobを継承したQuartzJobExecution（並行実行可）とQuartzDisallowConcurrentExecution（並行実行不可）の2種類がある
- ThreadLocalで開始時刻を保持し、実行時間を計算
- ジョブ情報はJobDataMapからTASK_PROPERTIESキーで取得
- 実行時間はミリ秒単位で記録

---

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

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

### 推奨読解順序

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

ジョブログに使用されるエンティティクラスを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | SysJobLog.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java` | ジョブログエンティティの構造 |
| 1-2 | SysJob.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java` | ジョブ定義エンティティ |

**読解のコツ**: statusフィールド（0=成功、1=失敗）に注目。@Excelアノテーションでエクスポート時の変換を確認。

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

抽象ジョブクラスを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | AbstractQuartzJob.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java` | ジョブ実行テンプレート |

**主要処理フロー**:
1. **30行目**: ThreadLocal定義 - `private static ThreadLocal<Date> threadLocal`
2. **32-51行目**: execute()メソッド - ジョブ実行テンプレート
3. **36行目**: JobDataMapからジョブ情報取得
4. **39行目**: before()呼び出し
5. **42行目**: doExecute()呼び出し（サブクラスで実装）
6. **44行目**: 正常終了時after()呼び出し
7. **49行目**: 例外発生時after()呼び出し
8. **59-62行目**: before() - 開始時刻記録
9. **70-96行目**: after() - ログ記録処理
10. **75-82行目**: SysJobLog構築
11. **83-88行目**: 例外発生時の処理
12. **95行目**: **ログ記録** - `SpringUtils.getBean(ISysJobLogService.class).addJobLog(sysJobLog)`

#### Step 3: 具体的なジョブ実装を理解する

ジョブ実装クラスを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | QuartzJobExecution.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java` | 並行実行可能ジョブ |
| 3-2 | QuartzDisallowConcurrentExecution.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java` | 並行実行不可ジョブ |

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

```
Quartz Scheduler
    │
    └─ Trigger発火
           │
           └─ AbstractQuartzJob.execute() (テンプレートメソッド)
                  │
                  ├─ before()
                  │      └─ threadLocal.set(開始時刻)
                  │
                  ├─ doExecute() (サブクラス実装)
                  │      │
                  │      └─ QuartzJobExecution / QuartzDisallowConcurrentExecution
                  │             │
                  │             └─ JobInvokeUtil.invokeMethod() (実際のタスク実行)
                  │
                  └─ after()
                         │
                         ├─ SysJobLog構築
                         │
                         ├─ 実行時間計算
                         │
                         └─ ISysJobLogService.addJobLog()
                                │
                                └─ sys_job_log INSERT
```

### データフロー図

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

Cronトリガー ───▶ AbstractQuartzJob ───▶ タスク実行結果
    │                    │
    │                    ▼
    │               before() (開始時刻記録)
    │                    │
    │                    ▼
    │               doExecute() (タスク実行)
    │                    │
    │                    ▼
    │               after() (ログ記録)
    │                    │
    │                    ▼
    └─────────────▶ sys_job_log テーブル
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| AbstractQuartzJob.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/AbstractQuartzJob.java` | ソース | 抽象ジョブクラス |
| QuartzJobExecution.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzJobExecution.java` | ソース | 並行実行可能ジョブ |
| QuartzDisallowConcurrentExecution.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/QuartzDisallowConcurrentExecution.java` | ソース | 並行実行不可ジョブ |
| SysJobLog.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJobLog.java` | ソース | ジョブログエンティティ |
| SysJob.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/domain/SysJob.java` | ソース | ジョブ定義エンティティ |
| ISysJobLogService.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/ISysJobLogService.java` | ソース | ジョブログサービス |
| SysJobLogServiceImpl.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/service/impl/SysJobLogServiceImpl.java` | ソース | ジョブログサービス実装 |
| ScheduleConstants.java | `ruoyi-common/src/main/java/com/ruoyi/common/constant/ScheduleConstants.java` | ソース | スケジュール定数 |
| JobInvokeUtil.java | `ruoyi-quartz/src/main/java/com/ruoyi/quartz/util/JobInvokeUtil.java` | ソース | ジョブ実行ユーティリティ |
