so-manager-dev.com/app/Services/ShjSixService.php
2025-09-19 19:01:21 +09:00

711 lines
24 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\Batch\BatchLog;
use App\Models\OperatorQue;
use App\Models\User;
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
{
/**
* Device モデル
*
* @var Device
*/
protected $deviceModel;
/**
* HardwareCheckLog モデル
*
* @var HardwareCheckLog
*/
protected $hardwareCheckLogModel;
/**
* PrintJobLog モデル
*
* @var PrintJobLog
*/
protected $printJobLogModel;
/**
* BatchLog モデル
*
* @var BatchLog
*/
protected $batchLogModel;
/**
* OperatorQue モデル
*
* @var OperatorQue
*/
protected $operatorQueModel;
/**
* ShjMailSendService
*
* @var ShjMailSendService
*/
protected $mailSendService;
/**
* 固定メールアドレスDB反映NG時用
*
* @var string
*/
const FIXED_EMAIL_ADDRESS = 'system-alert@so-manager.com';
/**
* プリンタログ監視期間(分)
*
* @var int
*/
const PRINTER_LOG_MONITOR_MINUTES = 15;
/**
* コンストラクタ
*
* @param Device $deviceModel
* @param HardwareCheckLog $hardwareCheckLogModel
* @param PrintJobLog $printJobLogModel
* @param BatchLog $batchLogModel
* @param OperatorQue $operatorQueModel
* @param ShjMailSendService $mailSendService
*/
public function __construct(
Device $deviceModel,
HardwareCheckLog $hardwareCheckLogModel,
PrintJobLog $printJobLogModel,
BatchLog $batchLogModel,
OperatorQue $operatorQueModel,
ShjMailSendService $mailSendService
) {
$this->deviceModel = $deviceModel;
$this->hardwareCheckLogModel = $hardwareCheckLogModel;
$this->printJobLogModel = $printJobLogModel;
$this->batchLogModel = $batchLogModel;
$this->operatorQueModel = $operatorQueModel;
$this->mailSendService = $mailSendService;
}
/**
* SHJ-6 サーバ死活監視処理メイン実行
*
* 処理フロー:
* 【処理1】サーバ死活監視DBアクセス
* 【処理2】デバイス管理マスタを取得する
* 【処理3】デバイス毎のハードウェア状態を取得する
* 【処理4】プリンタ制御プログラムログを取得する
* 【判断3】エラーログ有無
* 【処理5】バッチ処理ログを作成する
* ※ 異常時は共通A処理を実行
*
* @return array 処理結果
*/
public function executeServerMonitoring(): array
{
$batchLogId = null;
$warnings = [];
$errorDetails = [];
try {
// バッチ処理開始ログ作成
$batchLog = BatchLog::createBatchLog(
'shj6',
BatchLog::STATUS_START,
[],
'SHJ-6 サーバ死活監視処理開始'
);
$batchLogId = $batchLog->id;
Log::info('SHJ-6 サーバ死活監視処理開始', [
'batch_log_id' => $batchLogId
]);
// 【処理1】サーバ死活監視DBアクセス
$dbAccessResult = $this->checkDatabaseAccess();
if (!$dbAccessResult['success']) {
// DB接続NGの場合は共通A処理実行
$this->executeCommonProcessA($batchLogId, 'DB接続エラー: ' . $dbAccessResult['message']);
return [
'success' => false,
'message' => 'データベース接続エラーが発生しました',
'error_details' => [$dbAccessResult['message']],
'batch_log_id' => $batchLogId
];
}
// 【処理2】デバイス管理マスタを取得する
$devices = $this->getDeviceManagementData();
Log::info('デバイス管理マスタ取得完了', [
'device_count' => count($devices)
]);
// 【処理3】デバイス毎のハードウェア状態を取得する
$hardwareStatusResult = $this->getHardwareStatus($devices);
if (!$hardwareStatusResult['success']) {
// ハードウェア状態取得できなかった場合は共通A処理実行
$this->executeCommonProcessA($batchLogId, 'ハードウェア状態取得エラー: ' . $hardwareStatusResult['message']);
$warnings[] = 'ハードウェア状態の一部で異常を検出しました';
$errorDetails[] = $hardwareStatusResult['message'];
}
// 【処理4】プリンタ制御プログラムログを取得する
$printerLogResult = $this->getPrinterControlLogs();
// 【判断3】エラーログ有無
if ($printerLogResult['has_errors']) {
// エラーログ有の場合は共通A処理実行
$this->executeCommonProcessA($batchLogId, 'プリンタエラーログ検出: ' . $printerLogResult['error_summary']);
$warnings[] = 'プリンタ制御でエラーが検出されました';
$errorDetails[] = $printerLogResult['error_summary'];
}
// 【処理5】バッチ処理ログを作成する
$monitoringSummary = $this->createMonitoringSummary($devices, $hardwareStatusResult, $printerLogResult);
$status = empty($warnings) ? BatchLog::STATUS_SUCCESS : BatchLog::STATUS_WARNING;
$message = empty($warnings) ?
'SHJ-6 サーバ死活監視処理正常完了' :
'SHJ-6 サーバ死活監視処理完了(警告あり)';
$batchLog->update([
'status' => $status,
'end_time' => now(),
'message' => $message,
'success_count' => 1
]);
Log::info('SHJ-6 サーバ死活監視処理完了', [
'batch_log_id' => $batchLogId,
'monitoring_summary' => $monitoringSummary,
'warnings' => $warnings
]);
return [
'success' => true,
'message' => 'SHJ-6 サーバ死活監視処理が完了しました',
'monitoring_summary' => $monitoringSummary,
'warnings' => $warnings,
'error_details' => $errorDetails,
'batch_log_id' => $batchLogId
];
} catch (\Exception $e) {
$errorMessage = 'SHJ-6 サーバ死活監視処理でエラーが発生: ' . $e->getMessage();
if (isset($batchLog) && $batchLog) {
$batchLog->update([
'status' => BatchLog::STATUS_ERROR,
'end_time' => now(),
'message' => $errorMessage,
'error_details' => $e->getMessage(),
'error_count' => 1
]);
}
// 例外発生時も共通A処理実行
$this->executeCommonProcessA($batchLogId, $errorMessage);
Log::error('SHJ-6 サーバ死活監視処理エラー', [
'batch_log_id' => $batchLogId,
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);
return [
'success' => false,
'message' => $errorMessage,
'error_details' => [$e->getMessage()],
'batch_log_id' => $batchLogId
];
}
}
/**
* 【処理1】サーバ死活監視DBアクセス
*
* @return array アクセス結果
*/
private function checkDatabaseAccess(): array
{
try {
// 設定マスタテーブルへの簡単なクエリでDB接続確認
$result = DB::select('SELECT 1 as test');
if (empty($result)) {
return [
'success' => false,
'message' => 'データベースクエリの結果が空です'
];
}
Log::info('データベース接続確認成功');
return [
'success' => true,
'message' => 'データベース接続正常'
];
} catch (\Exception $e) {
Log::error('データベース接続エラー', [
'error' => $e->getMessage()
]);
return [
'success' => false,
'message' => $e->getMessage()
];
}
}
/**
* 【処理2】デバイス管理マスタを取得する
*
* @return array デバイス情報
*/
private function getDeviceManagementData(): array
{
try {
$devices = DB::table('device')
->select([
'device_id',
'park_id',
'device_type',
'device_subject',
'device_identifier',
'device_work',
'device_workstart',
'device_replace',
'device_remarks',
'operator_id'
])
->where('device_workstart', '<=', now())
->whereNull('device_replace')
->get()
->toArray();
Log::info('デバイス管理マスタ取得完了', [
'device_count' => count($devices)
]);
return $devices;
} catch (\Exception $e) {
Log::error('デバイス管理マスタ取得エラー', [
'error' => $e->getMessage()
]);
throw $e;
}
}
/**
* 【処理3】デバイス毎のハードウェア状態を取得する
*
* @param array $devices デバイス一覧
* @return array ハードウェア状態結果
*/
private function getHardwareStatus(array $devices): array
{
try {
$normalDevices = 0;
$abnormalDevices = 0;
$abnormalDetails = [];
foreach ($devices as $device) {
$latestStatus = HardwareCheckLog::getLatestStatusByDevice($device->device_id);
if (!$latestStatus) {
$abnormalDevices++;
$abnormalDetails[] = "デバイスID {$device->device_id}: ハードウェア状態ログが存在しません";
continue;
}
if ($latestStatus->isNormal()) {
$normalDevices++;
} else {
$abnormalDevices++;
$abnormalDetails[] = "デバイスID {$device->device_id}: {$latestStatus->getStatusNameAttribute()} - {$latestStatus->status_comment}";
}
}
Log::info('ハードウェア状態取得完了', [
'total_devices' => count($devices),
'normal_devices' => $normalDevices,
'abnormal_devices' => $abnormalDevices
]);
return [
'success' => $abnormalDevices === 0,
'total_devices' => count($devices),
'normal_devices' => $normalDevices,
'abnormal_devices' => $abnormalDevices,
'abnormal_details' => $abnormalDetails,
'message' => $abnormalDevices > 0 ?
"{$abnormalDevices}台のデバイスで異常を検出" :
'全デバイス正常'
];
} catch (\Exception $e) {
Log::error('ハードウェア状態取得エラー', [
'error' => $e->getMessage()
]);
return [
'success' => false,
'message' => $e->getMessage(),
'abnormal_details' => ["ハードウェア状態取得中にエラーが発生: " . $e->getMessage()]
];
}
}
/**
* 【処理4】プリンタ制御プログラムログを取得する
*
* @return array プリンタログ結果
*/
private function getPrinterControlLogs(): array
{
try {
// 過去15分間のエラーログを取得
$errorLogs = PrintJobLog::getRecentErrorLogs();
$hasErrors = $errorLogs->count() > 0;
$errorSummary = '';
$errorDetails = [];
if ($hasErrors) {
$errorsByCode = $errorLogs->groupBy('error_code');
$errorSummaryParts = [];
foreach ($errorsByCode as $errorCode => $logs) {
$count = $logs->count();
$errorSummaryParts[] = "エラーコード{$errorCode}: {$count}";
foreach ($logs as $log) {
$errorDetails[] = sprintf(
"[%s] %s - エラーコード: %d, %s",
$log->created_at->format('Y-m-d H:i:s'),
$log->process_name,
$log->error_code,
$log->status_comment
);
}
}
$errorSummary = implode(', ', $errorSummaryParts);
}
Log::info('プリンタ制御プログラムログ取得完了', [
'monitoring_period_minutes' => self::PRINTER_LOG_MONITOR_MINUTES,
'error_logs_count' => $errorLogs->count(),
'has_errors' => $hasErrors
]);
return [
'success' => true,
'has_errors' => $hasErrors,
'error_count' => $errorLogs->count(),
'error_summary' => $errorSummary,
'error_details' => $errorDetails,
'monitoring_period' => self::PRINTER_LOG_MONITOR_MINUTES . '分間'
];
} catch (\Exception $e) {
Log::error('プリンタ制御プログラムログ取得エラー', [
'error' => $e->getMessage()
]);
return [
'success' => false,
'has_errors' => true,
'error_summary' => 'ログ取得エラー: ' . $e->getMessage(),
'error_details' => ["プリンタログ取得中にエラーが発生: " . $e->getMessage()]
];
}
}
/**
* 監視結果サマリーを作成
*
* @param array $devices デバイス一覧
* @param array $hardwareResult ハードウェア結果
* @param array $printerResult プリンタ結果
* @return string 監視サマリー
*/
private function createMonitoringSummary(array $devices, array $hardwareResult, array $printerResult): string
{
$summary = [
'デバイス数: ' . count($devices),
'ハードウェア正常: ' . ($hardwareResult['normal_devices'] ?? 0) . '台',
'ハードウェア異常: ' . ($hardwareResult['abnormal_devices'] ?? 0) . '台',
'プリンタエラー: ' . ($printerResult['error_count'] ?? 0) . '件'
];
return implode(', ', $summary);
}
/**
* 共通A処理監視結果を反映
*
* @param int|null $batchLogId バッチログID
* @param string $alertMessage アラートメッセージ
* @return void
*/
private function executeCommonProcessA(?int $batchLogId, string $alertMessage): void
{
try {
Log::info('共通A処理開始', [
'batch_log_id' => $batchLogId,
'alert_message' => $alertMessage
]);
// 【共通判断1】DB反映可否判定
$canReflectToDb = $this->canReflectToDatabase();
if ($canReflectToDb) {
// 【共通処理1】オペレータキューを登録する
$this->registerOperatorQueue($alertMessage, $batchLogId);
// 【共通処理2】メール送信対象オペレータを取得する
$operators = $this->getMailTargetOperators();
// 【共通判断2】送信対象有無
if (!empty($operators)) {
foreach ($operators as $operator) {
$this->sendAlertMail($operator['email'], $alertMessage, 'オペレータ');
}
}
// 【共通処理3】駐輪場管理者を取得する
$parkManagers = $this->getParkManagers();
// 【共通判断3】送信対象有無
if (!empty($parkManagers)) {
foreach ($parkManagers as $manager) {
$this->sendAlertMail($manager['email'], $alertMessage, '駐輪場管理者');
}
}
} else {
// DB反映NGの場合は固定メールアドレスに送信
$this->sendAlertMail(self::FIXED_EMAIL_ADDRESS, $alertMessage, 'システム管理者');
}
Log::info('共通A処理完了', [
'batch_log_id' => $batchLogId
]);
} catch (\Exception $e) {
Log::error('共通A処理エラー', [
'batch_log_id' => $batchLogId,
'error' => $e->getMessage()
]);
}
}
/**
* DB反映可否判定
*
* @return bool 反映可能かどうか
*/
private function canReflectToDatabase(): bool
{
try {
// 簡単なINSERTテストでDB反映可否を確認
DB::beginTransaction();
$testId = DB::table('operator_que')->insertGetId([
'que_class' => 6,
'que_comment' => 'DB反映テスト',
'que_status' => 0,
'created_at' => now(),
'updated_at' => now()
]);
// テストレコードを削除
DB::table('operator_que')->where('que_id', $testId)->delete();
DB::commit();
return true;
} catch (\Exception $e) {
DB::rollBack();
Log::warning('DB反映不可', ['error' => $e->getMessage()]);
return false;
}
}
/**
* オペレータキューを登録
*
* @param string $alertMessage アラートメッセージ
* @param int|null $batchLogId バッチログID
* @return void
*/
private function registerOperatorQueue(string $alertMessage, ?int $batchLogId): void
{
try {
OperatorQue::create([
'que_class' => 6, // SHJ-6用のクラス
'user_id' => null,
'contract_id' => null,
'park_id' => null,
'que_comment' => $alertMessage,
'que_status' => 0, // 待機中
'que_status_comment' => 'システム監視アラート',
'work_instructions' => "SHJ-6監視処理 BatchLogID: {$batchLogId}",
'created_at' => now(),
'updated_at' => now()
]);
Log::info('オペレータキュー登録完了', [
'batch_log_id' => $batchLogId,
'alert_message' => $alertMessage
]);
} catch (\Exception $e) {
Log::error('オペレータキュー登録エラー', [
'batch_log_id' => $batchLogId,
'error' => $e->getMessage()
]);
}
}
/**
* メール送信対象オペレータを取得
*
* @return array オペレータ一覧
*/
private function getMailTargetOperators(): array
{
try {
// user_typeがオペレータのユーザーを取得仮の条件
$operators = DB::table('users')
->select(['user_id', 'email', 'name'])
->where('user_type', 'operator') // 実際のテーブル構造に合わせて調整
->whereNotNull('email')
->where('email', '!=', '')
->get()
->map(function ($user) {
return [
'user_id' => $user->user_id,
'email' => $user->email,
'name' => $user->name ?? 'オペレータ'
];
})
->toArray();
Log::info('メール送信対象オペレータ取得完了', [
'operator_count' => count($operators)
]);
return $operators;
} catch (\Exception $e) {
Log::error('メール送信対象オペレータ取得エラー', [
'error' => $e->getMessage()
]);
return [];
}
}
/**
* 駐輪場管理者を取得
*
* @return array 駐輪場管理者一覧
*/
private function getParkManagers(): array
{
try {
// 駐輪場管理者を取得(仮の条件)
$managers = DB::table('users')
->select(['user_id', 'email', 'name'])
->where('user_type', 'park_manager') // 実際のテーブル構造に合わせて調整
->whereNotNull('email')
->where('email', '!=', '')
->get()
->map(function ($user) {
return [
'user_id' => $user->user_id,
'email' => $user->email,
'name' => $user->name ?? '駐輪場管理者'
];
})
->toArray();
Log::info('駐輪場管理者取得完了', [
'manager_count' => count($managers)
]);
return $managers;
} catch (\Exception $e) {
Log::error('駐輪場管理者取得エラー', [
'error' => $e->getMessage()
]);
return [];
}
}
/**
* アラートメールを送信
*
* @param string $email メールアドレス
* @param string $alertMessage アラートメッセージ
* @param string $recipientType 受信者タイプ
* @return void
*/
private function sendAlertMail(string $email, string $alertMessage, string $recipientType): void
{
try {
// SHJメール送信機能を使用メールテンプレートID=1を使用、実際の値に調整
$result = $this->mailSendService->executeMailSend(
$email,
'', // 予備メールアドレスは空
1 // システムアラート用メールテンプレートID
);
if ($result['success']) {
Log::info('アラートメール送信成功', [
'email' => $email,
'recipient_type' => $recipientType,
'alert_message' => $alertMessage
]);
} else {
Log::error('アラートメール送信失敗', [
'email' => $email,
'recipient_type' => $recipientType,
'error' => $result['message']
]);
}
} catch (\Exception $e) {
Log::error('アラートメール送信エラー', [
'email' => $email,
'recipient_type' => $recipientType,
'error' => $e->getMessage()
]);
}
}
}