api.so-manager-dev.com/app/Services/ShjSixService.php
unhi.go e1073e2577
All checks were successful
Deploy api / deploy (push) Successful in 24s
SH-6 SHJ-9 実装
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 20:16:47 +08:00

1186 lines
48 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Services;
use App\Models\Device;
use App\Models\HardwareCheckLog;
use App\Models\PrintJobLog;
use App\Models\OperatorQue;
use App\Models\Ope;
use App\Models\Manager;
use App\Models\Setting;
use App\Models\JurisdictionParking;
use App\Models\Park;
use App\Services\ShjMailSendService;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
/**
* SHJ-6 サーバ死活監視処理サービス
*
* サーバとデバイスの死活監視、ハードウェア状態監視、プリンタログ監視を実行
* 異常検出時のメール通知機能を含む統合モニタリングサービス
*/
class ShjSixService
{
/**
* ShjMailSendService
*
* @var ShjMailSendService
*/
protected $mailSendService;
/**
* ShjEightService
*
* @var ShjEightService
*/
protected $shjEightService;
/**
* 固定メールアドレスDB反映NG時用
*
* @var string
*/
const FIXED_EMAIL_ADDRESS = 'test@dev-so-manager.com';
/**
* 監視期間(分)
*
* @var int
*/
const MONITOR_PERIOD_MINUTES = 15;
/**
* システム監視警報用メールテンプレートID
*
* @var int
*/
const SYSTEM_ALERT_MAIL_TEMPLATE_ID = 202;
/**
* キュー種別定数
*/
const QUE_CLASS_SERVER_ERROR = 101; // サーバーエラー
const QUE_CLASS_PRINTER_ERROR = 102; // プリンタエラー
const QUE_CLASS_SCANNER_ERROR = 103; // スキャナエラー
const QUE_CLASS_PAPER_WARNING = 104; // プリンタ用紙残少警報
/**
* デバイス種別:サーバー
*/
const DEVICE_TYPE_SERVER = 'サーバー';
/**
* デバイス稼働状態:稼働中
*/
const DEVICE_WORK_ACTIVE = '1';
/**
* コンストラクタ
*
* @param ShjMailSendService $mailSendService
* @param ShjEightService $shjEightService
*/
public function __construct(
ShjMailSendService $mailSendService,
ShjEightService $shjEightService
) {
$this->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呼び出し
// 仕様書準拠:ステータスコメント = バッチコメント + 各件数
$statusComment = $accumulatedBatchComment
. ':メール正常終了件数' . $totalMailSuccessCount
. '、メール異常終了件数' . $totalMailErrorCount
. '、キュー登録正常終了件数' . $totalQueueSuccessCount
. '、キュー登録異常終了件数' . $totalQueueErrorCount;
$status = 'success'; // 仕様書:常に"success"で記録
$message = empty($warnings) ?
'SHJ-6 サーバ死活監視処理正常完了' :
'SHJ-6 サーバ死活監視処理完了(警告あり)';
// 仕様書準拠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;
// 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')
->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(),
'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 対象キュー種別ID101〜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 対象キュー種別ID101〜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死活監視アタッチエラー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死活監視アタッチエラー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()
];
}
}
}