mailSendService = $mailSendService; $this->shjEightService = $shjEightService; } /** * SHJ-6 サーバ死活監視処理メイン実行 * * 処理フロー: * 【処理1】サーバ死活監視(DBアクセス) * 【処理2】デバイス管理マスタを取得する * 【処理3】デバイス毎のハードウェア状態を取得する * 【処理4】プリンタ制御プログラムログを取得する * 【処理5】バッチ処理ログを作成する * ※ 異常時は共通A処理を実行 * * @return array 処理結果 */ public function executeServerMonitoring(): array { $batchLogId = null; $warnings = []; $errorDetails = []; $alertCount = 0; $totalMailSuccessCount = 0; $totalMailErrorCount = 0; $totalQueueSuccessCount = 0; // キュー登録正常終了件数(仕様書準拠) $totalQueueErrorCount = 0; // キュー登録異常終了件数(仕様書準拠) $accumulatedBatchComment = ''; // 累積バッチコメント try { // バッチ処理開始ログ(内部ログのみ、batch_log廃止) Log::info('SHJ-6 サーバ死活監視処理開始'); // 【処理1】サーバ死活監視(DBアクセス) $dbAccessResult = $this->checkDatabaseAccessWithSettings(); if (!$dbAccessResult['success']) { // DB接続NGの場合は共通A処理実行(キュー種別:101、DB登録可否:0) $commonAResult = $this->executeCommonProcessA( $batchLogId, 'DB接続エラー: ' . $dbAccessResult['message'], self::QUE_CLASS_SERVER_ERROR, null, // park_id null, // user_id null, // contract_id 0, // DB登録可否=0(登録不可) 'サーバ死活監視:DB接続異常検出' ); // メール送信統計を集計 $totalMailSuccessCount += $commonAResult['mail_success_count'] ?? 0; $totalMailErrorCount += $commonAResult['mail_error_count'] ?? 0; $accumulatedBatchComment = $commonAResult['updated_batch_comment'] ?? ''; return [ 'success' => false, 'message' => 'データベース接続エラーが発生しました', 'error_details' => [$dbAccessResult['message']], 'batch_log_id' => $batchLogId, 'mail_success_count' => $totalMailSuccessCount, 'mail_error_count' => $totalMailErrorCount ]; } // DB登録可否フラグを取得 $dbRegisterFlag = $dbAccessResult['db_register_flag']; // 【処理2】デバイス管理マスタを取得する $devices = $this->getServerDevices(); Log::info('サーバーデバイス取得完了', [ 'device_count' => count($devices) ]); // 【処理3】デバイス毎のハードウェア状態を取得する foreach ($devices as $device) { $hardwareResult = $this->checkDeviceHardwareStatus($device, $batchLogId, $dbRegisterFlag); if ($hardwareResult['has_alert']) { $alertCount++; $warnings[] = "デバイスID {$device->device_id}: ハードウェア異常検出"; // 仕様書準拠:メール送信統計とキュー登録統計を集計 $totalMailSuccessCount += $hardwareResult['mail_success_count'] ?? 0; $totalMailErrorCount += $hardwareResult['mail_error_count'] ?? 0; $totalQueueSuccessCount += $hardwareResult['queue_success_count'] ?? 0; $totalQueueErrorCount += $hardwareResult['queue_error_count'] ?? 0; // 仕様書準拠:バッチコメントを累積 if (!empty($hardwareResult['updated_batch_comment'])) { $accumulatedBatchComment .= ($accumulatedBatchComment ? ' | ' : '') . $hardwareResult['updated_batch_comment']; } } } // 【処理4】プリンタ制御プログラムログを取得する $printerResult = $this->checkPrinterErrorLogs($batchLogId, $dbRegisterFlag); if ($printerResult['error_count'] > 0) { $alertCount += $printerResult['error_count']; $warnings[] = "プリンタエラー {$printerResult['error_count']}件検出"; // 仕様書準拠:メール送信統計とキュー登録統計を集計 $totalMailSuccessCount += $printerResult['mail_success_count'] ?? 0; $totalMailErrorCount += $printerResult['mail_error_count'] ?? 0; $totalQueueSuccessCount += $printerResult['queue_success_count'] ?? 0; $totalQueueErrorCount += $printerResult['queue_error_count'] ?? 0; // 仕様書準拠:バッチコメントを累積 if (!empty($printerResult['accumulated_batch_comment'])) { $accumulatedBatchComment .= ($accumulatedBatchComment ? ' | ' : '') . $printerResult['accumulated_batch_comment']; } } // 【処理5】バッチ処理ログを作成する - SHJ-8呼び出し // 仕様書準拠:JOB5 は常に実行する(JOB4 の結果に関わらず) // 注:bat_job_log.device_id は int unsigned のため連結文字列は格納不可 // 先頭デバイスIDを使用。デバイスなしの場合は 0 // (SHJ-8 はエラー返却するが、式様書上 result≠0 でも「処理を終了する」のため問題なし) $deviceIds = $devices->pluck('device_id')->toArray(); $deviceId = !empty($deviceIds) ? (int)$deviceIds[0] : 0; // デバイスが0件の場合はバッチコメントに明記(JOB3がスキップされた理由を記録) if (empty($deviceIds)) { $accumulatedBatchComment .= ($accumulatedBatchComment ? '、' : '') . 'デバイステーブル空(JOB3スキップ)'; } // JOB4 監視対象ログ情報を整形(varchar(255)超過防止のため最大5件ID表示) $monitoredIds = $printerResult['monitored_log_ids'] ?? []; $monitoredCount = count($monitoredIds); if ($monitoredCount === 0) { $job4Summary = 'JOB4監視:0件'; } elseif ($monitoredCount <= 5) { $job4Summary = 'JOB4監視:' . $monitoredCount . '件[ID:' . implode(',', $monitoredIds) . ']'; } else { $shownIds = array_slice($monitoredIds, 0, 5); $job4Summary = 'JOB4監視:' . $monitoredCount . '件[ID:' . implode(',', $shownIds) . '...]'; } // ステータスコメント = バッチコメント + JOB4監視情報 + 各件数 $statusComment = $accumulatedBatchComment . '、' . $job4Summary . ':メール正常終了件数' . $totalMailSuccessCount . '、メール異常終了件数' . $totalMailErrorCount . '、キュー登録正常終了件数' . $totalQueueSuccessCount . '、キュー登録異常終了件数' . $totalQueueErrorCount; $status = 'success'; // 仕様書:常に"success"で記録 $message = empty($warnings) ? 'SHJ-6 サーバ死活監視処理正常完了' : 'SHJ-6 サーバ死活監視処理完了(警告あり)'; // SHJ-8 バッチ処理ログ作成 $shj8Result = $this->createShjBatchLog( $deviceId, $printerResult['process_name'] ?? null, // 仕様書準拠:JOB4のプロセス名のみ 'SHJ-6サーバ死活監視', // job_name 固定 'success', // 仕様書:常にsuccess $statusComment ); // 式様書:result=0 でも result≠0 でも「処理を終了する」 if (!$shj8Result['success']) { Log::warning('SHJ-8 バッチ処理ログ作成で異常', [ 'device_id' => $deviceId, 'error' => $shj8Result['error'] ?? '' ]); } Log::info('SHJ-6 サーバ死活監視処理完了', [ 'status_comment' => $statusComment, 'warnings' => $warnings, 'alert_count' => $alertCount, 'mail_success_count' => $totalMailSuccessCount, 'mail_error_count' => $totalMailErrorCount ]); // 監視サマリーを生成 $monitoringSummary = sprintf( '監視デバイス数: %d, アラート: %d件, メール成功: %d件', count($devices), $alertCount, $totalMailSuccessCount ); return [ 'success' => true, 'message' => 'SHJ-6 サーバ死活監視処理が完了しました', 'monitoring_summary' => $monitoringSummary, 'status_comment' => $statusComment, 'warnings' => $warnings, 'alert_count' => $alertCount, 'mail_success_count' => $totalMailSuccessCount, 'mail_error_count' => $totalMailErrorCount, 'batch_log_id' => $batchLogId ]; } catch (\Exception $e) { $errorMessage = 'SHJ-6 サーバ死活監視処理でエラーが発生: ' . $e->getMessage(); // 例外発生時も共通A処理実行(キュー種別:101、DB登録可否:0) $commonAResult = $this->executeCommonProcessA( null, // batch_log廃止のためnull $errorMessage, self::QUE_CLASS_SERVER_ERROR, null, null, null, 0, 'サーバ死活監視:システムエラー発生' ); $totalMailSuccessCount += $commonAResult['mail_success_count'] ?? 0; $totalMailErrorCount += $commonAResult['mail_error_count'] ?? 0; Log::error('SHJ-6 サーバ死活監視処理エラー', [ 'exception' => $e->getMessage(), 'trace' => $e->getTraceAsString() ]); return [ 'success' => false, 'message' => $errorMessage, 'monitoring_summary' => 'エラーにより監視中断', 'error_details' => [$e->getMessage()], 'mail_success_count' => $totalMailSuccessCount, 'mail_error_count' => $totalMailErrorCount ]; } } /** * 【処理1】サーバ死活監視(DBアクセス)- 設定マスタを使用 * * @return array アクセス結果 */ private function checkDatabaseAccessWithSettings(): array { try { // 設定マスタを取得してDB接続確認 $setting = Setting::getSettings(); if (!$setting) { return [ 'success' => false, 'message' => '設定マスタが取得できませんでした', 'db_register_flag' => 0 ]; } Log::info('データベース接続確認成功(設定マスタ取得)', [ 'setting_id' => $setting->setting_id ]); return [ 'success' => true, 'message' => 'データベース接続正常', 'db_register_flag' => 1 // DB登録可能 ]; } catch (\Exception $e) { Log::error('データベース接続エラー', [ 'error' => $e->getMessage() ]); return [ 'success' => false, 'message' => $e->getMessage(), 'db_register_flag' => 0 // DB登録不可 ]; } } /** * 【処理2】サーバーデバイスを取得する * * 条件: * - device_type = 'サーバー' * - device_work = '1' (稼働中) * - device_workstart <= 現在日付 * - device_replace IS NULL * * @return \Illuminate\Support\Collection デバイス情報 */ private function getServerDevices() { try { // 仕様書準拠SQL-2:デバイス管理マスタを取得 $devices = DB::table('device') ->select([ 'device_id', 'park_id', 'device_type', 'device_subject', 'device_workstart', ]) ->where('device_type', self::DEVICE_TYPE_SERVER) ->where('device_work', self::DEVICE_WORK_ACTIVE) ->where('device_workstart', '<=', now()) ->whereNull('device_replace') ->get(); Log::info('サーバーデバイス取得完了', [ 'device_count' => $devices->count() ]); return $devices; } catch (\Exception $e) { Log::error('サーバーデバイス取得エラー', [ 'error' => $e->getMessage() ]); throw $e; } } /** * 【処理3】デバイスのハードウェア状態をチェック * * 直近15分のハードウェアチェックログを確認し、異常時は共通A処理を実行 * * @param object $device デバイス情報 * @param int|null $batchLogId バッチログID * @param int $dbRegisterFlag DB登録可否 * @return array チェック結果(メール送信統計含む) */ private function checkDeviceHardwareStatus($device, ?int $batchLogId, int $dbRegisterFlag): array { try { // 直近15分のログを取得(最新1件) $fifteenMinutesAgo = Carbon::now()->subMinutes(self::MONITOR_PERIOD_MINUTES); $latestLog = DB::table('hardware_check_log') ->where('device_id', $device->device_id) ->where('created_at', '>=', $fifteenMinutesAgo) ->where('created_at', '<=', now()) ->orderBy('created_at', 'desc') ->first(); // 仕様書準拠:JOB3-STEP1 取得レコードが0件の場合のみアラート if (!$latestLog) { // ログが存在しない = 異常 $alertMessage = sprintf( 'ハードウェア監視異常: デバイスID=%d, デバイス名=%s, 理由=直近15分のログが存在しません', $device->device_id, $device->device_subject ?? 'N/A' ); $commonAResult = $this->executeCommonProcessA( $batchLogId, $alertMessage, self::QUE_CLASS_SERVER_ERROR, $device->park_id, null, // user_id null, // contract_id $dbRegisterFlag, 'ハードウェア監視:ログ未検出' ); return [ 'has_alert' => true, 'reason' => 'ログなし', 'mail_success_count' => $commonAResult['mail_success_count'] ?? 0, 'mail_error_count' => $commonAResult['mail_error_count'] ?? 0, 'queue_success_count' => $commonAResult['queue_success_count'] ?? 0, // 仕様書準拠 'queue_error_count' => $commonAResult['queue_error_count'] ?? 0, // 仕様書準拠 'updated_batch_comment' => $commonAResult['updated_batch_comment'] ?? '' ]; } // 仕様書準拠:レコードが存在する場合はステータス値に関わらずJOB4へ(アラートなし) return [ 'has_alert' => false, 'reason' => '正常', 'mail_success_count' => 0, 'mail_error_count' => 0 ]; } catch (\Exception $e) { Log::error('ハードウェア状態チェックエラー', [ 'device_id' => $device->device_id, 'error' => $e->getMessage() ]); return [ 'has_alert' => false, 'reason' => 'エラー', 'mail_success_count' => 0, 'mail_error_count' => 0 ]; } } /** * 【処理4】プリンタエラーログをチェック * * 直近15分のプリンタエラーログ(status >= 100)を確認し、 * エラーログごとに共通A処理を実行 * * 仕様書準拠:ステータス >= 100 を条件とする * * @param int|null $batchLogId バッチログID * @param int $dbRegisterFlag DB登録可否 * @return array チェック結果 */ private function checkPrinterErrorLogs(?int $batchLogId, int $dbRegisterFlag): array { try { // 直近15分のエラーログを取得 $fifteenMinutesAgo = Carbon::now()->subMinutes(self::MONITOR_PERIOD_MINUTES); // 仕様書: ステータス >= 100 // status は varchar型のため CAST で数値変換して比較 $errorLogs = DB::table('print_job_log') ->select([ 'job_log_id', 'park_id', 'user_id', 'contract_id', 'process_name', 'job_name', 'status', 'error_code', 'status_comment', ]) ->where('created_at', '>=', $fifteenMinutesAgo) ->where('created_at', '<=', now()) ->whereRaw('CAST(status AS SIGNED) >= 100') ->orderBy('created_at', 'desc') ->get(); Log::info('プリンタエラーログ取得完了', [ 'error_count' => $errorLogs->count(), 'period_minutes' => self::MONITOR_PERIOD_MINUTES ]); // メール送信統計とキュー登録統計(仕様書準拠) $mailSuccessCount = 0; $mailErrorCount = 0; $queueSuccessCount = 0; $queueErrorCount = 0; $accumulatedBatchComment = ''; // エラーログごとに共通A処理を実行 foreach ($errorLogs as $log) { // 仕様書準拠:statusの値に応じてキュー種別を判定 // - 200番台 → 102(プリンタエラー) // - 300番台 → 103(スキャナエラー) // - 400番台 → 104(プリンタ用紙残少警報) $statusValue = (int)$log->status; if ($statusValue >= 400 && $statusValue < 500) { $queClass = self::QUE_CLASS_PAPER_WARNING; // 104 $errorType = 'プリンタ用紙残少警報'; } elseif ($statusValue >= 300 && $statusValue < 400) { $queClass = self::QUE_CLASS_SCANNER_ERROR; // 103 $errorType = 'スキャナエラー'; } elseif ($statusValue >= 200 && $statusValue < 300) { $queClass = self::QUE_CLASS_PRINTER_ERROR; // 102 $errorType = 'プリンタエラー'; } else { // 仕様書未定義のステータス範囲 → 102(プリンタエラー)をデフォルト使用 // 式様書「対象レコード数分」に準拠し、スキップしない $queClass = self::QUE_CLASS_PRINTER_ERROR; // 102 $errorType = 'プリンタエラー'; Log::warning('JOB4: 仕様書未定義のステータス範囲(デフォルト102適用)', [ 'status' => $statusValue, 'process_name' => $log->process_name ?? null ]); } $alertMessage = sprintf( '%s: ステータス=%s, プロセス=%s, ジョブ=%s, エラーコード=%d, コメント=%s', $errorType, $log->status ?? 'N/A', $log->process_name ?? 'N/A', $log->job_name ?? 'N/A', $log->error_code ?? 0, $log->status_comment ?? 'N/A' ); $commonAResult = $this->executeCommonProcessA( $batchLogId, $alertMessage, $queClass, // ステータス値に応じて102/103/104 $log->park_id, $log->user_id, $log->contract_id, $dbRegisterFlag, sprintf('プリンタ制御:%s検出', $errorType) ); // 仕様書準拠:メール送信結果とキュー登録結果を集計 $mailSuccessCount += $commonAResult['mail_success_count'] ?? 0; $mailErrorCount += $commonAResult['mail_error_count'] ?? 0; $queueSuccessCount += $commonAResult['queue_success_count'] ?? 0; $queueErrorCount += $commonAResult['queue_error_count'] ?? 0; // 仕様書準拠:バッチコメントを累積 if (!empty($commonAResult['updated_batch_comment'])) { $accumulatedBatchComment .= ($accumulatedBatchComment ? ' | ' : '') . $commonAResult['updated_batch_comment']; } } // 仕様書準拠:処理4のプロセス名を返却(複数件を連結) $processNames = []; foreach ($errorLogs as $log) { if (!empty($log->process_name)) { $processNames[] = $log->process_name; } } // 仕様書準拠:複数の場合は後ろ連結(重複除去しない) $concatenatedProcessNames = !empty($processNames) ? implode(',', $processNames) : null; return [ 'success' => true, 'error_count' => $errorLogs->count(), 'monitored_log_ids' => $errorLogs->pluck('job_log_id')->toArray(), // JOB4監視対象ログID一覧 'mail_success_count' => $mailSuccessCount, 'mail_error_count' => $mailErrorCount, 'queue_success_count' => $queueSuccessCount, // 仕様書準拠 'queue_error_count' => $queueErrorCount, // 仕様書準拠 'accumulated_batch_comment' => $accumulatedBatchComment, 'process_name' => $concatenatedProcessNames // 処理4のプロセス名(複数件連結) ]; } catch (\Exception $e) { Log::error('プリンタエラーログチェックエラー', [ 'error' => $e->getMessage() ]); return [ 'success' => false, 'error_count' => 0 ]; } } /** * 共通A処理:監視結果を反映 * * 仕様書準拠: * - SHJ-7 メール送信結果を集計(正常終了件数/異常終了件数) * - 異常時はバッチコメントに SHJ-7 異常情報を追記 * - 更新されたバッチコメントでオペレータキュー登録 * * @param int|null $batchLogId バッチログID * @param string $alertMessage アラートメッセージ * @param int $queClass 対象キュー種別ID(101〜104) * @param int|null $parkId 駐輪場ID * @param int|null $userId 利用者ID * @param int|null $contractId 定期契約ID * @param int $dbRegisterFlag DB登録可否(0=不可、1=可) * @param string|null $batchComment バッチコメント * @return array 処理結果(メール送信統計含む) */ private function executeCommonProcessA( ?int $batchLogId, string $alertMessage, int $queClass, ?int $parkId, ?int $userId, ?int $contractId, int $dbRegisterFlag, ?string $batchComment ): array { // メール送信結果統計 $mailSuccessCount = 0; $mailErrorCount = 0; // キュー登録結果統計(仕様書準拠) $queueSuccessCount = 0; $queueErrorCount = 0; try { Log::info('共通A処理開始', [ 'batch_log_id' => $batchLogId, 'alert_message' => $alertMessage, 'que_class' => $queClass, 'park_id' => $parkId, 'db_register_flag' => $dbRegisterFlag ]); // 【共通判断1】DB反映可否判定 if ($dbRegisterFlag === 1) { // DB登録可能な場合 // 仕様書準拠:キューコメント = 内部変数.バッチコメント(そのまま使用) $updatedBatchComment = $batchComment ?? ''; // 【共通処理1】オペレータキューを登録する(仕様書順序準拠) // 注:SHJ-7異常情報はメール送信後に追記 $queueResult = $this->registerOperatorQueue( $alertMessage, $batchLogId, $queClass, $parkId, $userId, $contractId, $updatedBatchComment ); // 仕様書準拠:キュー登録正常終了件数/異常終了件数を更新 if ($queueResult['success']) { $queueSuccessCount++; } else { $queueErrorCount++; // 仕様書準拠(line 209):定期契約ID + 異常情報をバッチコメントに追記 $errorPrefix = ($contractId !== null) ? $contractId : ''; $updatedBatchComment .= $errorPrefix . ($queueResult['error'] ?? ''); } // 【共通処理2】メール送信対象オペレータを取得する $operators = $this->getMailTargetOperators($queClass, $parkId); // 【共通判断2】送信対象有無 if (!empty($operators)) { foreach ($operators as $operator) { $result = $this->sendAlertMail($operator['email'], $alertMessage, 'オペレータ'); // SHJ-7 メール送信結果を集計 if ($result['success']) { $mailSuccessCount++; } else { $mailErrorCount++; // 仕様書準拠(line 209):定期契約ID + SHJ-7.異常情報をバッチコメントに追記 $errorPrefix = ($contractId !== null) ? $contractId : ''; $updatedBatchComment .= $errorPrefix . ($result['error'] ?? ''); } } } // 【共通処理3】駐輪場管理者を取得する $parkManagers = $this->getParkManagers($parkId); // 【共通判断3】送信対象有無 if (!empty($parkManagers)) { foreach ($parkManagers as $manager) { $result = $this->sendAlertMail($manager['email'], $alertMessage, '駐輪場管理者'); // SHJ-7 メール送信結果を集計 if ($result['success']) { $mailSuccessCount++; } else { $mailErrorCount++; // 仕様書準拠(line 209):定期契約ID + SHJ-7.異常情報をバッチコメントに追記 $errorPrefix = ($contractId !== null) ? $contractId : ''; $updatedBatchComment .= $errorPrefix . ($result['error'] ?? ''); } } } } else { // DB反映NGの場合は固定メールアドレスに緊急メール送信 // 仕様書準拠:テンプレート不使用、件名固定、本文なし $result = $this->sendEmergencyMail( self::FIXED_EMAIL_ADDRESS, $alertMessage ); $updatedBatchComment = $batchComment ?? ''; if ($result['success']) { $mailSuccessCount++; } else { $mailErrorCount++; // 仕様書準拠:異常情報をバッチコメントに直接追記 $updatedBatchComment .= $result['error'] ?? ''; } } Log::info('共通A処理完了', [ 'batch_log_id' => $batchLogId, 'que_class' => $queClass, 'mail_success_count' => $mailSuccessCount, 'mail_error_count' => $mailErrorCount, 'queue_success_count' => $queueSuccessCount, 'queue_error_count' => $queueErrorCount, 'updated_batch_comment' => $updatedBatchComment ?? $batchComment ]); return [ 'success' => true, 'mail_success_count' => $mailSuccessCount, 'mail_error_count' => $mailErrorCount, 'queue_success_count' => $queueSuccessCount, // 仕様書準拠 'queue_error_count' => $queueErrorCount, // 仕様書準拠 'updated_batch_comment' => $updatedBatchComment ?? $batchComment ]; } catch (\Exception $e) { Log::error('共通A処理エラー', [ 'batch_log_id' => $batchLogId, 'que_class' => $queClass, 'error' => $e->getMessage() ]); $updatedBatchComment = ($batchComment ?? '') . ' | 共通A処理エラー: ' . $e->getMessage(); return [ 'success' => false, 'mail_success_count' => $mailSuccessCount, 'mail_error_count' => $mailErrorCount, 'queue_success_count' => $queueSuccessCount, 'queue_error_count' => $queueErrorCount, 'updated_batch_comment' => $updatedBatchComment, 'error' => $e->getMessage() ]; } } /** * オペレータキューを登録 * * 仕様書準拠: * - キューコメント = 内部変数.バッチコメント * - キューステータスID = 1(キュー発生) * - 更新オペレータID = 9999999(機器ID固定値) * * @param string $alertMessage アラートメッセージ * @param int|null $batchLogId バッチログID * @param int $queClass 対象キュー種別ID(101〜104) * @param int|null $parkId 駐輪場ID * @param int|null $userId 利用者ID * @param int|null $contractId 定期契約ID * @param string|null $batchComment バッチコメント * @return array 登録結果 ['success' => bool, 'error' => string|null, 'que_id' => int|null] */ private function registerOperatorQueue( string $alertMessage, ?int $batchLogId, int $queClass, ?int $parkId, ?int $userId, ?int $contractId, ?string $batchComment ): array { try { // 仕様書準拠(todo/SHJ-6/SHJ-6.txt:170-182): // - キューコメント = 内部変数.バッチコメント // - キューステータスコメント = "" (空) // - 業務指示コメント = "" (空) $operatorQue = OperatorQue::create([ 'que_class' => $queClass, 'user_id' => $userId, 'contract_id' => $contractId, 'park_id' => $parkId, 'que_comment' => $batchComment ?? '', // 仕様書:内部変数.バッチコメント(SHJ-7異常情報は後で追記) 'que_status' => 1, // キュー発生 'que_status_comment' => '', // 仕様書:空文字列 'work_instructions' => '', // 仕様書:空文字列 'operator_id' => 9999999, // 仕様書準拠:固定値 'created_at' => now(), 'updated_at' => now() ]); Log::info('オペレータキュー登録完了', [ 'batch_log_id' => $batchLogId, 'que_class' => $queClass, 'que_id' => $operatorQue->que_id, // 主キーはque_id 'park_id' => $parkId, 'user_id' => $userId, 'contract_id' => $contractId ]); return [ 'success' => true, 'error' => null, 'que_id' => $operatorQue->que_id // 仕様書準拠:主キーque_idを返却 ]; } catch (\Exception $e) { Log::error('オペレータキュー登録エラー', [ 'batch_log_id' => $batchLogId, 'que_class' => $queClass, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage(), 'que_id' => null ]; } } /** * メール送信対象オペレータを取得 * * 管轄駐輪場マスタをJOINして、指定駐輪場を管轄しているオペレータのみ取得 * * キュー種別IDに応じた送信フラグをチェック: * - 101(サーバーエラー) → ope_sendalart_que10 * - 102(プリンタエラー) → ope_sendalart_que11 * - 103(スキャナエラー) → ope_sendalart_que12 * - 104(プリンタ用紙残少) → ope_sendalart_que13 * * @param int $queClass 対象キュー種別ID * @param int|null $parkId 駐輪場ID * @return array オペレータ一覧 */ private function getMailTargetOperators(int $queClass, ?int $parkId): array { try { // 仕様書準拠:駐輪場IDがnullの場合はJOIN条件が成立しないため空返却 if ($parkId === null) { Log::info('park_idがnullのため、メール送信対象オペレータなし', [ 'que_class' => $queClass ]); return []; } // キュー種別IDに対応する送信フラグカラム名を決定 $alertFlagColumn = $this->getOperatorAlertFlagColumn($queClass); if (empty($alertFlagColumn)) { Log::warning('不正なキュー種別ID', ['que_class' => $queClass]); return []; } // 仕様書準拠SQL-6:管轄駐輪場マスタをJOINしてオペレータを取得 $query = DB::table('ope as T1') ->join('jurisdiction_parking as T2', 'T1.ope_id', '=', 'T2.ope_id') // 仕様書SQL-6: ope_login_id と記載があるが、実DBカラム名は login_id ->select(['T1.ope_id', 'T1.login_id', 'T1.ope_name', 'T1.ope_mail']) ->where('T2.park_id', $parkId) ->where('T1.' . $alertFlagColumn, 1) // 該当アラート送信フラグが有効 ->where('T1.ope_quit_flag', 0) // 退職していない ->whereNotNull('T1.ope_mail') ->where('T1.ope_mail', '!=', ''); $operators = $query->get() ->map(function ($ope) { return [ 'ope_id' => $ope->ope_id, 'name' => $ope->ope_name, 'email' => $ope->ope_mail ]; }) ->toArray(); Log::info('メール送信対象オペレータ取得完了', [ 'que_class' => $queClass, 'park_id' => $parkId, 'alert_flag_column' => $alertFlagColumn, 'operator_count' => count($operators) ]); return $operators; } catch (\Exception $e) { Log::error('メール送信対象オペレータ取得エラー', [ 'que_class' => $queClass, 'park_id' => $parkId, 'error' => $e->getMessage() ]); return []; } } /** * キュー種別IDに対応するオペレータ送信フラグカラム名を取得 * * @param int $queClass キュー種別ID * @return string|null カラム名 */ private function getOperatorAlertFlagColumn(int $queClass): ?string { $mapping = [ 101 => 'ope_sendalart_que10', // サーバーエラー 102 => 'ope_sendalart_que11', // プリンタエラー 103 => 'ope_sendalart_que12', // スキャナエラー 104 => 'ope_sendalart_que13', // プリンタ用紙残少警報 ]; return $mapping[$queClass] ?? null; } /** * 駐輪場管理者を取得 * * 駐輪場管理者マスタから指定駐輪場の管理者を取得: * - 退職フラグ = 0(在職中) * - メールアドレスが設定されている * - 所属駐輪場ID = 指定駐輪場ID * * @param int|null $parkId 駐輪場ID * @return array 駐輪場管理者一覧 */ private function getParkManagers(?int $parkId): array { try { // 仕様書準拠:駐輪場IDがnullの場合は該当なしのため空返却 if ($parkId === null) { Log::info('park_idがnullのため、駐輪場管理者なし'); return []; } $query = Manager::active()->hasEmail() ->where('manager_parkid', $parkId); $managers = $query->select(['manager_id', 'manager_name', 'manager_mail', 'manager_parkid']) ->get() ->map(function ($manager) { return [ 'manager_id' => $manager->manager_id, 'name' => $manager->manager_name, 'email' => $manager->manager_mail, 'park_id' => $manager->manager_parkid ]; }) ->toArray(); Log::info('駐輪場管理者取得完了', [ 'park_id' => $parkId, 'manager_count' => count($managers) ]); return $managers; } catch (\Exception $e) { Log::error('駐輪場管理者取得エラー', [ 'park_id' => $parkId, 'error' => $e->getMessage() ]); return []; } } /** * アラートメールを送信(SHJ-7) * * 仕様書準拠: * - SHJ-7 メール送信サービスを呼び出し * - 戻り値(成功/失敗、異常情報)を返却 * * @param string $email メールアドレス * @param string $alertMessage アラートメッセージ * @param string $recipientType 受信者タイプ * @return array 送信結果 ['success' => bool, 'error' => string|null] */ private function sendAlertMail(string $email, string $alertMessage, string $recipientType): array { try { // SHJ-7 メール送信機能を使用(メールテンプレートID=202を使用) $result = $this->mailSendService->executeMailSend( $email, '', // 予備メールアドレスは空 self::SYSTEM_ALERT_MAIL_TEMPLATE_ID ); // SHJ-7仕様準拠: result === 0 が正常、それ以外は異常 if (($result['result'] ?? 1) === 0) { Log::info('アラートメール送信成功', [ 'email' => $email, 'recipient_type' => $recipientType, 'alert_message' => $alertMessage ]); return [ 'success' => true, 'error' => null ]; } else { // 仕様準拠: error_info を使用 $errorInfo = $result['error_info'] ?? 'メール送信失敗'; Log::error('アラートメール送信失敗', [ 'email' => $email, 'recipient_type' => $recipientType, 'error_info' => $errorInfo ]); return [ 'success' => false, 'error' => $errorInfo ]; } } catch (\Exception $e) { Log::error('アラートメール送信エラー', [ 'email' => $email, 'recipient_type' => $recipientType, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * DB反映不可時の緊急メール送信 * * 仕様書準拠: * - テンプレート不使用 * - 件名: 「So-Manager:死活監視DBアタッチエラー(SHJ-6)」固定 * - 本文: なし(空) * - 固定アドレスに直接送信 * * @param string $email 送信先メールアドレス * @param string $alertMessage アラートメッセージ(ログ用) * @return array 送信結果 ['success' => bool, 'error' => string|null] */ private function sendEmergencyMail(string $email, string $alertMessage): array { try { // 仕様書準拠:件名固定、本文なし $subject = 'So-Manager:死活監視DBアタッチエラー(SHJ-6)'; $body = ''; // 本文なし // Laravelの Mail facade を使用して直接送信 \Illuminate\Support\Facades\Mail::raw($body, function ($message) use ($email, $subject) { $message->to($email) ->subject($subject); }); Log::info('緊急メール送信成功', [ 'email' => $email, 'subject' => $subject, 'alert_message' => $alertMessage ]); return [ 'success' => true, 'error' => null ]; } catch (\Exception $e) { Log::error('緊急メール送信エラー', [ 'email' => $email, 'alert_message' => $alertMessage, 'error' => $e->getMessage() ]); return [ 'success' => false, 'error' => $e->getMessage() ]; } } /** * 【処理5】SHJ-8 バッチ処理ログ作成 * * 仕様書に基づくSHJ-8共通処理呼び出し * * @param int $deviceId デバイスID * @param string|null $processName プロセス名(JOB4から取得) * @param string $jobName ジョブ名 * @param string $status ステータス * @param string $statusComment ステータスコメント * @return array 処理結果 ['success' => bool, 'error' => string|null] */ private function createShjBatchLog( int $deviceId, ?string $processName, string $jobName, string $status, string $statusComment ): array { try { $createdDate = now()->format('Y/m/d'); $updatedDate = now()->format('Y/m/d'); Log::info('SHJ-8 バッチ処理ログ作成', [ 'device_id' => $deviceId, 'process_name' => $processName, 'job_name' => $jobName, 'status' => $status, 'status_comment' => $statusComment ]); // SHJ-8サービスを呼び出し $shj8Result = $this->shjEightService->execute( $deviceId, $processName, $jobName, $status, $statusComment, $createdDate, $updatedDate ); // 仕様書準拠:SHJ-8の処理結果を判定(result=0が正常) if (($shj8Result['result'] ?? 1) === 0) { Log::info('SHJ-8 バッチ処理ログ作成完了(正常)', [ 'device_id' => $deviceId, 'process_name' => $processName ]); return [ 'success' => true, 'error' => null ]; } else { $errorMessage = $shj8Result['error_message'] ?? 'SHJ-8 処理結果異常'; Log::warning('SHJ-8 バッチ処理ログ作成異常', [ 'device_id' => $deviceId, 'result' => $shj8Result['result'] ?? null, 'error_message' => $errorMessage ]); return [ 'success' => false, 'error' => $errorMessage ]; } } catch (\Exception $e) { Log::error('SHJ-8 バッチ処理ログ作成エラー', [ 'error' => $e->getMessage(), 'device_id' => $deviceId ]); // 仕様書準拠:SHJ-8でエラーが発生してもメイン処理は継続 // エラー情報を返却 return [ 'success' => false, 'error' => $e->getMessage() ]; } } }