so-manager-dev.com/app/Services/ShjSixService.php
Your Name 10a917b556
All checks were successful
Deploy so-manager (auto) / deploy (push) Successful in 24s
【更新】SHJ関連の修正
2025-10-10 19:55:46 +09:00

1276 lines
52 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 = sprintf(
'デバイス数: %d, アラート件数: %d, メール正常: %d件, メール異常: %d件, キュー登録正常: %d件, キュー登録異常: %d件',
count($devices),
$alertCount,
$totalMailSuccessCount,
$totalMailErrorCount,
$totalQueueSuccessCount,
$totalQueueErrorCount
);
// バッチコメントが累積されている場合は追加
if (!empty($accumulatedBatchComment)) {
$statusComment .= ' | ' . $accumulatedBatchComment;
}
$status = 'success'; // 仕様書:常に"success"で記録
$message = empty($warnings) ?
'SHJ-6 サーバ死活監視処理正常完了' :
'SHJ-6 サーバ死活監視処理完了(警告あり)';
// 仕様書準拠処理2で取得したデバイスIDを連結
$deviceIds = $devices->pluck('device_id')->toArray();
$concatenatedDeviceIds = !empty($deviceIds) ? implode(',', $deviceIds) : '';
// SHJ-8 バッチ処理ログ作成(仕様書準拠)
// device_id = 処理2で取得したデバイスID複数なら連結
// process_name = 処理4のプロセス名プリンタログから取得、なければ'SHJ-6'
// job_name = "SHJ-6サーバ死活監視" 固定
// status = 常に "success"
$shj8Result = $this->createShjBatchLog([
'device_id' => $concatenatedDeviceIds, // 処理2のデバイスID連結
'process_name' => $printerResult['process_name'] ?? 'SHJ-6', // 処理4のプロセス名
'job_name' => 'SHJ-6サーバ死活監視',
'status' => 'success', // 仕様書常にsuccess
'status_comment' => $statusComment,
'mail_success_count' => $totalMailSuccessCount,
'mail_error_count' => $totalMailErrorCount,
'queue_success_count' => $totalQueueSuccessCount, // 仕様書準拠
'queue_error_count' => $totalQueueErrorCount, // 仕様書準拠
'device_count' => count($devices),
'alert_count' => $alertCount
]);
// 仕様書準拠SHJ-8の戻り値確認処理結果 = 0以外なら異常
if (!$shj8Result['success']) {
$shj8Error = $shj8Result['error'] ?? 'Unknown error';
Log::warning('SHJ-8 バッチ処理ログ作成で異常が発生しました', [
'error' => $shj8Error
]);
// 仕様書準拠:異常時はバッチコメントへ反映
$accumulatedBatchComment .= ($accumulatedBatchComment ? ' | ' : '') .
sprintf('SHJ-8異常: %s', $shj8Error);
// statusCommentも更新
$statusComment .= sprintf(' | SHJ-8異常: %s', $shj8Error);
}
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 {
$devices = DB::table('device')
->select([
'device_id',
'park_id',
'device_type',
'device_subject',
'device_identifier',
'device_work',
'device_workstart',
'device_remarks'
])
->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();
// ログが取得できない、または異常状態の場合
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'] ?? ''
];
}
// status != 1正常の場合は異常
if ($latestLog->status != HardwareCheckLog::STATUS_NORMAL) {
$statusName = HardwareCheckLog::getStatusName($latestLog->status);
$alertMessage = sprintf(
'ハードウェア監視異常: デバイスID=%d, デバイス名=%s, 状態=%s, コメント=%s',
$device->device_id,
$device->device_subject ?? 'N/A',
$statusName,
$latestLog->status_comment ?? 'N/A'
);
$commonAResult = $this->executeCommonProcessA(
$batchLogId,
$alertMessage,
self::QUE_CLASS_SERVER_ERROR,
$device->park_id,
null,
null,
$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'] ?? ''
];
}
// 正常
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の値に応じてキュー種別を判定
// - status 200番台 → 102プリンタエラー
// - status 300番台 → 103スキャナエラー
// - status 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 = 'スキャナエラー';
} else {
$queClass = self::QUE_CLASS_PRINTER_ERROR; // 102デフォルト
$errorType = 'プリンタエラー';
}
$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;
}
}
// 重複を除去して連結
$processNames = array_unique($processNames);
$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;
$mailErrorDetails = [];
// キュー登録結果統計(仕様書準拠)
$queueSuccessCount = 0;
$queueErrorCount = 0;
$registeredQueId = null; // 登録したキューID後で更新するため
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登録可能な場合
// 仕様書準拠:バッチコメント = 処理1.定期契約ID + 元のバッチコメント
$updatedBatchComment = sprintf(
'定期契約ID: %s / %s',
$contractId ?? 'なし',
$batchComment ?? ''
);
// 【共通処理1】オペレータキューを登録する仕様書順序準拠
// 注SHJ-7異常情報はメール送信後に追記
$queueResult = $this->registerOperatorQueue(
$alertMessage,
$batchLogId,
$queClass,
$parkId,
$userId,
$contractId,
$updatedBatchComment
);
// 仕様書準拠:キュー登録正常終了件数/異常終了件数を更新
if ($queueResult['success']) {
$queueSuccessCount++;
$registeredQueId = $queueResult['que_id']; // キューIDを保存
} else {
$queueErrorCount++;
$updatedBatchComment .= sprintf(
' | キュー登録異常: %s',
$queueResult['error'] ?? 'Unknown 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++;
$mailErrorDetails[] = sprintf(
'オペレータ[%s]へのメール送信失敗: %s',
$operator['email'],
$result['error'] ?? 'Unknown 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++;
$mailErrorDetails[] = sprintf(
'駐輪場管理者[%s]へのメール送信失敗: %s',
$manager['email'],
$result['error'] ?? 'Unknown error'
);
}
}
}
// 仕様書準拠メール異常時にSHJ-7異常情報を追記
if ($mailErrorCount > 0) {
$updatedBatchComment .= sprintf(
' | SHJ-7メール異常: %d件 (%s)',
$mailErrorCount,
implode('; ', $mailErrorDetails)
);
}
// 仕様書準拠キュー登録後、SHJ-7異常情報を含めてキューコメントを更新
// キューコメント = 内部変数.バッチコメント定期契約ID + SHJ-7異常情報
if ($registeredQueId && $mailErrorCount > 0) {
try {
OperatorQue::where('que_id', $registeredQueId)->update([ // 主キーはque_id
'que_comment' => $updatedBatchComment, // 仕様書:キューコメントに追記
'updated_at' => now()
]);
Log::info('オペレータキューコメント更新完了SHJ-7異常情報追記', [
'que_id' => $registeredQueId,
'updated_comment' => $updatedBatchComment
]);
} catch (\Exception $e) {
Log::error('オペレータキューコメント更新エラー', [
'que_id' => $registeredQueId,
'error' => $e->getMessage()
]);
}
}
} else {
// DB反映NGの場合は固定メールアドレスに緊急メール送信
// 仕様書準拠:テンプレート不使用、件名固定、本文なし
$result = $this->sendEmergencyMail(
self::FIXED_EMAIL_ADDRESS,
$alertMessage
);
if ($result['success']) {
$mailSuccessCount++;
} else {
$mailErrorCount++;
$mailErrorDetails[] = sprintf(
'固定アドレス[%s]への緊急メール送信失敗: %s',
self::FIXED_EMAIL_ADDRESS,
$result['error'] ?? 'Unknown error'
);
}
$updatedBatchComment = $batchComment ?? '';
if ($mailErrorCount > 0) {
$updatedBatchComment .= sprintf(
' | 緊急メール異常: %d件 (%s)',
$mailErrorCount,
implode('; ', $mailErrorDetails)
);
}
}
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,
'mail_error_details' => $mailErrorDetails,
'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,
'mail_error_details' => $mailErrorDetails,
'queue_success_count' => $queueSuccessCount,
'queue_error_count' => $queueErrorCount,
'updated_batch_comment' => $updatedBatchComment,
'error' => $e->getMessage()
];
}
}
/**
* オペレータキューを登録
*
* 仕様書準拠:
* - 定期契約IDをバッチコメントに含める
* - 登録の成否とque_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 バッチコメントSHJ-7異常情報は後で追記
* @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 駐輪場IDnullの場合は全オペレータ
* @return array オペレータ一覧
*/
private function getMailTargetOperators(int $queClass, ?int $parkId): array
{
try {
// キュー種別IDに対応する送信フラグカラム名を決定
$alertFlagColumn = $this->getOperatorAlertFlagColumn($queClass);
if (empty($alertFlagColumn)) {
Log::warning('不正なキュー種別ID', ['que_class' => $queClass]);
return [];
}
// 管轄駐輪場マスタをJOINしてオペレータを取得
$query = DB::table('ope as T1')
->select(['T1.ope_id', 'T1.ope_name', 'T1.ope_mail'])
->where('T1.' . $alertFlagColumn, 1) // 該当アラート送信フラグが有効
->where('T1.ope_quit_flag', 0) // 退職していない
->whereNotNull('T1.ope_mail')
->where('T1.ope_mail', '!=', '');
// 駐輪場IDが指定されている場合は管轄駐輪場マスタでフィルタ
if ($parkId !== null) {
$query->join('jurisdiction_parking as T2', 'T1.ope_id', '=', 'T2.ope_id')
->where('T2.park_id', $parkId);
}
$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 駐輪場IDnullの場合は全管理者
* @return array 駐輪場管理者一覧
*/
private function getParkManagers(?int $parkId): array
{
try {
$query = Manager::active()->hasEmail();
// 駐輪場IDが指定されている場合はフィルタ
if ($parkId !== null) {
$query->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 array $statistics 処理統計情報
* @return array 処理結果 ['success' => bool, 'error' => string|null]
*/
private function createShjBatchLog(array $statistics): array
{
try {
// 仕様書準拠のSHJ-8パラメータ設定
// device_id: 処理2で取得したデバイスID複数ある場合は最初のIDを使用
// process_name: 処理4のプロセス名
$deviceIdString = $statistics['device_id']; // 連結されたデバイスID文字列 "1,2,3"
// 複数デバイスIDがある場合は最初のIDを使用SHJ-8はint型を期待
$deviceIdArray = !empty($deviceIdString) ? explode(',', $deviceIdString) : [];
$deviceId = !empty($deviceIdArray) ? (int)$deviceIdArray[0] : 1;
$processName = $statistics['process_name'] ?? 'SHJ-6';
$jobName = $statistics['job_name']; // "SHJ-6サーバ死活監視" 固定
$status = $statistics['status']; // 常に "success"
$statusComment = $statistics['status_comment'] ?? '';
$createdDate = now()->format('Y/m/d');
$updatedDate = now()->format('Y/m/d');
Log::info('SHJ-8 バッチ処理ログ作成', [
'device_id' => $deviceId,
'device_id_original' => $deviceIdString,
'process_name' => $processName,
'job_name' => $jobName,
'status' => $status,
'status_comment' => $statusComment
]);
// SHJ-8サービスを呼び出し
$this->shjEightService->execute(
$deviceId,
$processName,
$jobName,
$status,
$statusComment,
$createdDate,
$updatedDate
);
Log::info('SHJ-8 バッチ処理ログ作成完了', [
'device_id' => $deviceId,
'process_name' => $processName
]);
return [
'success' => true,
'error' => null
];
} catch (\Exception $e) {
Log::error('SHJ-8 バッチ処理ログ作成エラー', [
'error' => $e->getMessage(),
'statistics' => $statistics
]);
// 仕様書準拠SHJ-8でエラーが発生してもメイン処理は継続
// エラー情報を返却
return [
'success' => false,
'error' => $e->getMessage()
];
}
}
}